Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changelog.d/4574.maintenance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove 'get_specs' from datamodel help.
180 changes: 162 additions & 18 deletions src/ansys/fluent/core/services/datamodel_se.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
# SOFTWARE.

"""Wrappers over StateEngine based datamodel gRPC service of Fluent."""

from enum import Enum
import functools
import inspect
import itertools
import logging
import os
Expand Down Expand Up @@ -1115,15 +1115,75 @@ def is_read_only(self) -> bool:
"""Checks whether the object is read only."""
return false_if_none(self.get_attr(Attribute.IS_READ_ONLY.value))

def _get_help_string(self) -> str:
help_lines = []

cls_name = self.__class__.__name__
is_task_object = cls_name == "_TaskObject"

if is_task_object:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to the PyParameter comment below, this should also be handled polymorphically, or at least externally (a type-based strategy).

help_lines = [
f"NamedObject: TaskObject ({self._name_()})",
f" Currently active: {self.is_active()}",
" Members:",
]
param_list = [
item
for item in inspect.getmembers_static(self)
if isinstance(item[1], PyParameter)
]
for _, param in param_list:
help_lines.extend(
[" " + line for line in param._get_help_string().splitlines()]
)
elif isinstance(self, PyParameter):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just override it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, will do.

name = str(self).split()[0].split(".")[-1]
default_val = getattr(self, "default_value", None)
default = default_val() if callable(default_val) else None
Comment on lines +1141 to +1142
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get what's going on here. Can we please have some comments?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, sure. If the default value returns something then we add it. Will add some inline comments.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, "default_val" can either be the attr or None. In the next line, it checks if it is not None then return the value of "default_val". Will simplify the logic as well.

if isinstance(self, PyTextual):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each of these conditions can be handled via an override per type.

label = "ListString" if isinstance(default, list) else "String"
elif isinstance(self, PyNumerical):
label = "ListReal" if isinstance(default, list) else "Real"
elif isinstance(self, PyDictionary):
label = "Dict"
else:
label = "Bool" if hasattr(self, "default_value") else "Struct"
help_lines.append(f"{label}: {name}")
if isinstance(self, PyDictionary):
from ansys.fluent.core.session_shared import _make_datamodel_module_base

command = self.service.get_state(
self.rules, convert_path_to_se_path([self.path[0]])
)["CommandName"]
temp_meshing_instance = _make_datamodel_module_base(
self.service, self.__class__.__module__.split("_")[-1], "meshing"
)
help_lines.extend(
[
" " + line
for line in getattr(temp_meshing_instance, command)
._get_help_string()
.splitlines()
]
)
help_lines.append(f" Currently active: {self.is_active()}")

for attr, label in [
("allowed_values", "Allowed values"),
("default_value", "Default value"),
("min", "Min"),
("max", "Max"),
]:
if hasattr(self, attr):
value = getattr(self, attr)()
if value is not None:
help_lines.append(f" {label}: {value}")

return "\n".join(help_lines)

def help(self) -> None:
"""Print help string."""
response = self.service.get_specs(
self.rules, convert_path_to_se_path(self.path)
)
help_string = _get_value_from_message_dict(
response, [member_specs_oneof_fields, "common", "helpstring"]
)
print(help_string)
print(self._get_help_string())

def __call__(self, *args, **kwargs) -> Any:
if kwargs:
Expand Down Expand Up @@ -1947,15 +2007,55 @@ def __call__(self, *args, **kwds) -> Any:
self.after_execute(value)
return command

def help(self) -> None:
def _get_help_string(self) -> str:
command_instance = self.create_instance()
command_name = command_instance.command
help_lines = [
f"Command: {command_name}",
" Supported keyword arguments:",
]
ex_parts = ["{"]

def _append_example_arg(obj_name: str, value, is_textual: bool = False):
val = f"'{value}'" if is_textual else value
ex_parts.append(f"'{obj_name}'={val}, ")

def _process_sub_args(com_obj):
if (
isinstance(com_obj, PyCommandArgumentsSubItem)
and hasattr(com_obj, "default_value")
and com_obj.default_value() is not None
):
is_textual = isinstance(com_obj, PyTextualCommandArgumentsSubItem)
_append_example_arg(com_obj.name, com_obj.default_value(), is_textual)

for name, obj in inspect.getmembers_static(command_instance):
_populated_help_lines = _populate_py_command_args_help_string(obj)
if _populated_help_lines:
help_lines.extend([f" {line}" for line in _populated_help_lines])
_process_sub_args(obj)

if isinstance(obj, PySingletonCommandArgumentsSubItem):
ex_parts.append(f"'{obj.name}'=" + "{")
for _, sub_obj in inspect.getmembers_static(obj):
_process_sub_args(sub_obj)
if not ex_parts[-1].endswith("{"):
ex_parts[-1] = ex_parts[-1].rstrip(", ") + "}, "
else:
ex_parts[-1] = ex_parts[-1] + "}, "

ret_type = command_instance.getAttribValue("returnType")
if ret_type:
help_lines.append(f" Return type: {ret_type}")
example = "".join(ex_parts).rstrip(", ") + "}"
ex_header = f"\nA valid state template, derived from the corresponding {command_instance.command} command:\n"
help_lines.append(ex_header)
help_lines.append(f" {example}\n\n")
return "\n".join(help_lines)

def help(self):
"""Prints help string."""
response = self.service.get_specs(
self.rules, convert_path_to_se_path(self.path)
)
help_string = _get_value_from_message_dict(
response, [member_specs_oneof_fields, "common", "helpstring"]
)
print(help_string)
print(self._get_help_string())

def _create_command_arguments(self) -> str:
commandid = self.service.create_command_arguments(
Expand Down Expand Up @@ -1990,6 +2090,46 @@ def create_instance(self) -> "PyCommandArguments":
return PyCommandArguments(*args)


def _populate_py_command_args_help_string(com_obj):
type_map = {
PyTextualCommandArgumentsSubItem: "String",
PyNumericalCommandArgumentsSubItem: "Real",
PyDictionaryCommandArgumentsSubItem: "Dict",
PySingletonCommandArgumentsSubItem: "Struct",
PyParameterCommandArgumentsSubItem: "Bool",
}
help_lines = None

if isinstance(com_obj, PyCommandArgumentsSubItem):
for cls, label in type_map.items():
if isinstance(com_obj, cls):
break
help_lines = [
f"{label}: {com_obj.name}",
f" Currently active: {com_obj.is_active()}",
]
if isinstance(com_obj, PySingletonCommandArgumentsSubItem):
for name, obj in inspect.getmembers_static(com_obj):
if isinstance(obj, PyCommandArgumentsSubItem):
help_lines.extend(
[" " + line for line in obj._get_help_string().splitlines()]
)

default_val = getattr(com_obj, "default_value", lambda: None)()
if default_val is not None:
help_lines.append(f" Default value: {default_val}")

allowed_vals = getattr(com_obj, "allowed_values", lambda: [])()
if allowed_vals:
help_lines.append(f" Allowed values: {', '.join(map(str, allowed_vals))}")

for bound_name in ("min", "max"):
bound_val = getattr(com_obj, bound_name, lambda: None)()
if bound_val is not None:
help_lines.append(f" {bound_name.capitalize()}: {bound_val}")
return help_lines


class _InputFile:
def _do_before_execute(self, value):
try:
Expand Down Expand Up @@ -2017,7 +2157,7 @@ class _InOutFile(_InputFile, _OutputFile):
pass


class PyCommandArgumentsSubItem(PyCallableStateObject):
class PyCommandArgumentsSubItem(PyStateContainer):
"""Class representing command argument in datamodel."""

def __init__(
Expand Down Expand Up @@ -2070,9 +2210,13 @@ def get_attr(self, attrib: str) -> Any:

getAttribValue = get_attr

def _get_help_string(self):
help_lines = _populate_py_command_args_help_string(self)
return "\n".join(help_lines)

def help(self) -> None:
"""Get help."""
print(self.__doc__.strip())
print(self._get_help_string())

def __setattr__(self, key, value):
if isinstance(value, PyCommandArgumentsSubItem):
Expand Down
23 changes: 16 additions & 7 deletions src/ansys/fluent/core/session_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,18 +63,27 @@ def _make_tui_module(session, module_name):


def _make_datamodel_module(session, module_name):
try:
return _make_datamodel_module_base(
session._se_service, session._version, module_name
)
except RuntimeError:
datamodel_logger.warning("Generated API not found for %s.", module_name)
datamodel_logger.warning(_CODEGEN_MSG_DATAMODEL)
return PyMenuGeneric(session._se_service, module_name)


def _make_datamodel_module_base(service, version, module_name):
try:
from ansys.fluent.core import config
from ansys.fluent.core.codegen.datamodelgen import datamodel_file_name_map

file_name = datamodel_file_name_map[module_name]
module = pyfluent.utils.load_module(
f"{module_name}_{session._version}",
config.codegen_outdir / f"datamodel_{session._version}" / f"{file_name}.py",
f"{module_name}_{version}",
config.codegen_outdir / f"datamodel_{version}" / f"{file_name}.py",
)
warning_for_fluent_dev_version(session._version)
return module.Root(session._se_service, module_name, [])
warning_for_fluent_dev_version(version)
return module.Root(service, module_name, [])
except (ImportError, FileNotFoundError):
datamodel_logger.warning("Generated API not found for %s.", module_name)
datamodel_logger.warning(_CODEGEN_MSG_DATAMODEL)
return PyMenuGeneric(session._se_service, module_name)
raise RuntimeError("Not found in generated classes.")