diff --git a/doc/changelog.d/4574.maintenance.md b/doc/changelog.d/4574.maintenance.md new file mode 100644 index 00000000000..ede3a178154 --- /dev/null +++ b/doc/changelog.d/4574.maintenance.md @@ -0,0 +1 @@ +Remove 'get_specs' from datamodel help. diff --git a/src/ansys/fluent/core/services/datamodel_se.py b/src/ansys/fluent/core/services/datamodel_se.py index 1cde9c1ee2f..606a8a3fe20 100644 --- a/src/ansys/fluent/core/services/datamodel_se.py +++ b/src/ansys/fluent/core/services/datamodel_se.py @@ -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 @@ -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: + 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): + name = str(self).split()[0].split(".")[-1] + default_val = getattr(self, "default_value", None) + default = default_val() if callable(default_val) else None + if isinstance(self, PyTextual): + 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: @@ -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( @@ -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: @@ -2017,7 +2157,7 @@ class _InOutFile(_InputFile, _OutputFile): pass -class PyCommandArgumentsSubItem(PyCallableStateObject): +class PyCommandArgumentsSubItem(PyStateContainer): """Class representing command argument in datamodel.""" def __init__( @@ -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): diff --git a/src/ansys/fluent/core/session_shared.py b/src/ansys/fluent/core/session_shared.py index 25a1858ef57..4f9fe32ae60 100644 --- a/src/ansys/fluent/core/session_shared.py +++ b/src/ansys/fluent/core/session_shared.py @@ -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.")