diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9808cdd1bd..2929a09f435 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -142,8 +142,12 @@ jobs: src/ansys/fluent/core/meshing/tui.py src/ansys/fluent/core/solver/settings src/ansys/fluent/core/solver/tui.py - key: API-Code-v${{ env.API_CODE_CACHE }}-${{ steps.version.outputs.PYFLUENT_VERSION }}-${{ matrix.image-tag }}-${{ hashFiles('codegen/**') }}-${{ github.sha }} - restore-keys: API-Code-v${{ env.API_CODE_CACHE }}-${{ steps.version.outputs.PYFLUENT_VERSION }}-${{ matrix.image-tag }}-${{ hashFiles('codegen/**') }} + doc/source/api/core/meshing/tui + doc/source/api/core/meshing/datamodel + doc/source/api/core/solver/tui + doc/source/api/core/solver/datamodel + key: API-Code-v${{ env.API_CODE_CACHE }}-${{ steps.version.outputs.PYFLUENT_VERSION }}-${{ matrix.image-tag }}-${{ hashFiles('codegen/**') }} + restore-keys: API-Code-v${{ env.API_CODE_CACHE }}-${{ steps.version.outputs.PYFLUENT_VERSION }}-${{ matrix.image-tag }} - name: Login to GitHub Container Registry if: steps.cache-api-code.outputs.cache-hit != 'true' @@ -177,9 +181,9 @@ jobs: uses: actions/cache@v3 with: path: doc/source/examples - key: Examples-v${{ env.RESET_EXAMPLES_CACHE }}-${{ steps.version.outputs.PYFLUENT_VERSION }}-${{ matrix.image-tag }}-${{ hashFiles('examples/**') }}-${{ github.sha }} + key: Examples-v${{ env.RESET_EXAMPLES_CACHE }}-${{ steps.version.outputs.PYFLUENT_VERSION }}-${{ matrix.image-tag }}-${{ hashFiles('examples/**') }} restore-keys: | - Examples-v${{ env.RESET_EXAMPLES_CACHE }}-${{ steps.version.outputs.PYFLUENT_VERSION }}-${{ matrix.image-tag }}-${{ hashFiles('examples/**') }} + Examples-v${{ env.RESET_EXAMPLES_CACHE }}-${{ steps.version.outputs.PYFLUENT_VERSION }}-${{ matrix.image-tag }} - name: Build Documentation run: make build-doc DOCS_CNAME=fluentdocs.pyansys.com @@ -256,8 +260,8 @@ jobs: doc/source/api/core/meshing/datamodel doc/source/api/core/solver/tui doc/source/api/core/solver/datamodel - key: API-Code-v${{ env.API_CODE_CACHE }}-${{ steps.version.outputs.PYFLUENT_VERSION }}-${{ matrix.image-tag }}-${{ hashFiles('codegen/**') }}-${{ github.sha }} - restore-keys: API-Code-v${{ env.API_CODE_CACHE }}-${{ steps.version.outputs.PYFLUENT_VERSION }}-${{ matrix.image-tag }}-${{ hashFiles('codegen/**') }} + key: API-Code-v${{ env.API_CODE_CACHE }}-${{ steps.version.outputs.PYFLUENT_VERSION }}-${{ matrix.image-tag }}-${{ hashFiles('codegen/**') }} + restore-keys: API-Code-v${{ env.API_CODE_CACHE }}-${{ steps.version.outputs.PYFLUENT_VERSION }}-${{ matrix.image-tag }} - name: Login to GitHub Container Registry if: steps.cache-api-code.outputs.cache-hit != 'true' diff --git a/codegen/tuigen.py b/codegen/tuigen.py index 9c89d034e18..efef4f42246 100644 --- a/codegen/tuigen.py +++ b/codegen/tuigen.py @@ -18,7 +18,7 @@ import shutil import string import subprocess -from typing import Iterable +from typing import Any, Dict import xml.etree.ElementTree as ET from data.fluent_gui_help_patch import XML_HELP_PATCH @@ -28,7 +28,6 @@ from ansys.fluent.core import LOG from ansys.fluent.core.launcher.launcher import FLUENT_VERSION, get_fluent_path from ansys.fluent.core.services.datamodel_tui import ( - DatamodelService, PyMenu, convert_path_to_grpc_path, convert_tui_menu_to_func_name, @@ -148,32 +147,30 @@ def _populate_xml_helpstrings(): _XML_HELPSTRINGS[k] = v -class _TUIMenuGenerator: - """Wrapper over PyMenu to extract TUI menu metadata from Fluent.""" - - def __init__(self, path: str, service: DatamodelService): - self._menu = PyMenu(service, path) - - def get_child_names(self) -> Iterable[str]: - return self._menu.get_child_names(True) - - def get_doc_string(self) -> str: - return self._menu.get_doc_string(True) +def _is_valid_tui_menu_name(name): + return name and not all(x in string.punctuation for x in name) class _TUIMenu: """Class representing Fluent's TUI menu.""" - def __init__(self, path: str): + def __init__(self, path: str, doc: str, is_command: bool = False): self.path = path self.tui_name = path[-1] if path else "" self.name = convert_tui_menu_to_func_name(self.tui_name) + self.is_command = is_command tui_path = convert_path_to_grpc_path(path) self.doc = _XML_HELPSTRINGS.get(tui_path, None) if self.doc: del _XML_HELPSTRINGS[tui_path] + else: + self.doc = doc + self.doc = self.doc.replace("\\*", "*") + self.doc = self.doc.replace("*", "\*") + self.doc = self.doc.strip() + if not self.doc.endswith("."): + self.doc = self.doc + "." self.children = {} - self.is_command = False def get_command_path(self, command: str) -> str: return convert_path_to_grpc_path(self.path + [command]) @@ -200,26 +197,22 @@ def __init__( shutil.rmtree(Path(self._tui_doc_dir)) self.session = pyfluent.launch_fluent(meshing_mode=meshing) self._service = self.session._datamodel_service_tui - self._main_menu = _TUIMenu([]) - - def _populate_menu(self, menu: _TUIMenu): - menugen = _TUIMenuGenerator(menu.path, self._service) - if not menu.doc: - menu.doc = menugen.get_doc_string() - menu.doc = menu.doc.replace("\\*", "*") - menu.doc = menu.doc.replace("*", "\*") - menu.doc = menu.doc.strip() - if not menu.doc.endswith("."): - menu.doc = menu.doc + "." - child_names = menugen.get_child_names() - if child_names: - for child_name in child_names: - if child_name and not all(x in string.punctuation for x in child_name): - child_menu = _TUIMenu(menu.path + [child_name]) - menu.children[child_menu.name] = child_menu - self._populate_menu(child_menu) - else: - menu.is_command = True + self._main_menu = _TUIMenu([], "") + + def _populate_menu(self, menu: _TUIMenu, info: Dict[str, Any]): + for child_menu_name, child_menu_info in info["menus"].items(): + if _is_valid_tui_menu_name(child_menu_name): + child_menu = _TUIMenu( + menu.path + [child_menu_name], child_menu_info["help"] + ) + menu.children[child_menu.name] = child_menu + self._populate_menu(child_menu, child_menu_info) + for child_command_name, child_command_info in info["commands"].items(): + if _is_valid_tui_menu_name(child_command_name): + child_menu = _TUIMenu( + menu.path + [child_command_name], child_command_info["help"], True + ) + menu.children[child_menu.name] = child_menu def _write_code_to_tui_file(self, code: str, indent: int = 0): self.__writer.write(" " * _INDENT_STEP * indent + code) @@ -317,7 +310,8 @@ def _write_doc_for_menu(self, menu, doc_dir: Path, heading, class_name) -> None: def generate(self) -> None: Path(self._tui_file).parent.mkdir(exist_ok=True) with open(self._tui_file, "w", encoding="utf8") as self.__writer: - self._populate_menu(self._main_menu) + info = PyMenu(self._service, self._main_menu.path).get_static_info() + self._populate_menu(self._main_menu, info) self.session.exit() if self._tui_file == _SOLVER_TUI_FILE: self._write_code_to_tui_file('"""Fluent Solver TUI Commands"""\n') diff --git a/src/ansys/fluent/core/launcher/fluent_container.py b/src/ansys/fluent/core/launcher/fluent_container.py index 5e6521e287e..8bd12784e89 100644 --- a/src/ansys/fluent/core/launcher/fluent_container.py +++ b/src/ansys/fluent/core/launcher/fluent_container.py @@ -57,7 +57,7 @@ def start_fluent_container(mounted_from: str, mounted_to: str, args: List[str]) "-e", "FLUENT_LAUNCHED_FROM_PYFLUENT=1", "ghcr.io/pyansys/pyfluent", - "-g", + "-gu", f"-sifile={container_sifile}", ] + args diff --git a/src/ansys/fluent/core/services/datamodel_tui.py b/src/ansys/fluent/core/services/datamodel_tui.py index 839f5881613..5416f4cb559 100644 --- a/src/ansys/fluent/core/services/datamodel_tui.py +++ b/src/ansys/fluent/core/services/datamodel_tui.py @@ -2,8 +2,9 @@ import keyword import types -from typing import Any, Iterable, List, Tuple, Union +from typing import Any, Dict, Iterable, List, Tuple, Union +from google.protobuf.json_format import MessageToDict import grpc from ansys.api.fluent.v0 import datamodel_tui_pb2 as DataModelProtoModule @@ -57,6 +58,10 @@ def execute_query( ) -> DataModelProtoModule.ExecuteQueryResponse: return self.__stub.ExecuteQuery(request, metadata=self.__metadata) + @catch_grpc_error + def get_static_info(self, request): + return self.__stub.GetStaticInfo(request, metadata=self.__metadata) + def _convert_value_to_gvalue(val: Any, gval: Variant): """Convert Python datatype to Value type of @@ -197,6 +202,45 @@ def get_doc_string(self, include_unavailable: bool = False) -> str: response = self._service.get_attribute_value(request) return _convert_gvalue_to_value(response.value) + def get_static_info(self) -> Dict[str, Any]: + """Get static info at menu level. + + Returns + ------- + DataModelProtoModule.StaticInfo + static info + """ + if hasattr(DataModelProtoModule, "GetStaticInfoRequest"): + request = DataModelProtoModule.GetStaticInfoRequest() + request.path = self._path + response = self._service.get_static_info(request) + return MessageToDict(response.info, including_default_value_fields=True) + else: + return _get_static_info_at_level(self) + + +def _get_static_info_at_level(menu: PyMenu) -> Dict[str, Any]: + info = {} + info["help"] = menu.get_doc_string(include_unavailable=True) + info["menus"] = {} + info["commands"] = {} + child_names = menu.get_child_names(include_unavailable=True) + if child_names: + for child_name in child_names: + if child_name: + child_menu = PyMenu( + menu._service, + menu._path + ("" if menu._path.endswith("/") else "/") + child_name, + ) + child_info = _get_static_info_at_level(child_menu) + if child_info.pop("is_command", False): + info["commands"][child_name] = child_info + else: + info["menus"][child_name] = child_info + else: + info["is_command"] = True + return info + class TUIMenu: """Base class for the generated menu classes.""" diff --git a/src/ansys/fluent/core/services/error_handler.py b/src/ansys/fluent/core/services/error_handler.py index f38a637bc4f..4b01d6f5f27 100644 --- a/src/ansys/fluent/core/services/error_handler.py +++ b/src/ansys/fluent/core/services/error_handler.py @@ -3,8 +3,6 @@ import grpc -from ansys.fluent.core.utils.logging import LOG - def catch_grpc_error(f: Callable) -> Callable: """Decorator to catch gRPC errors.""" @@ -14,7 +12,6 @@ def func(*args, **kwargs) -> Callable: try: return f(*args, **kwargs) except grpc.RpcError as ex: - LOG.error("GRPC_ERROR: %s", ex.details()) raise RuntimeError(ex.details()) from None return func