From b556f1e5b419729dfdf7a9cbbddacdabf523c878 Mon Sep 17 00:00:00 2001 From: Mainak Kundu <94432368+mkundu1@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:15:40 -0500 Subject: [PATCH 1/3] docs: event streaming callback (#3546) * docs: event streaming callback * docs: event streaming callback * feat: optional last * docs: move cheatsheet build to nightly --- .github/workflows/ci.yml | 17 +------- .github/workflows/doc-build-dev-nightly.yml | 16 ++++++++ .github/workflows/doc-build-release.yml | 16 ++++++++ doc/source/conf.py | 13 +++---- .../contributing/environment_variables.rst | 6 ++- doc/source/user_guide/events.rst | 21 ++++++---- .../streaming_services/events_streaming.py | 39 +++++++++++++------ tests/test_events_manager.py | 13 ++++++- 8 files changed, 97 insertions(+), 44 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 527477151ad..e45b1445b70 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -115,22 +115,6 @@ jobs: restore-keys: | Python-${{ runner.os }}-${{ matrix.python-version }} - - name: Install Quarto - uses: quarto-dev/quarto-actions/setup@v2 - with: - tinytex: true - - - name: Check Quarto Version - shell: bash - run: | - quarto --version - - - name: "Install Poppler for PDF to PNG conversion" - shell: bash - run: | - sudo apt-get update - sudo apt-get install -y poppler-utils - - name: Install pyfluent run: make install @@ -197,6 +181,7 @@ jobs: make build-doc-source env: FLUENT_IMAGE_TAG: ${{ env.DOC_DEPLOYMENT_IMAGE_TAG }} + PYFLUENT_DOC_SKIP_CHEATSHEET: 1 - name: Zip HTML Documentation before upload run: | diff --git a/.github/workflows/doc-build-dev-nightly.yml b/.github/workflows/doc-build-dev-nightly.yml index d4ad51ec8c6..a5ae573e594 100644 --- a/.github/workflows/doc-build-dev-nightly.yml +++ b/.github/workflows/doc-build-dev-nightly.yml @@ -34,6 +34,22 @@ jobs: sudo apt update sudo apt-get install pandoc libegl1 make xvfb libfontconfig1 libxrender1 libxkbcommon-x11-0 -y + - name: Install Quarto + uses: quarto-dev/quarto-actions/setup@v2 + with: + tinytex: true + + - name: Check Quarto Version + shell: bash + run: | + quarto --version + + - name: "Install Poppler for PDF to PNG conversion" + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y poppler-utils + - name: Install pyfluent run: make install diff --git a/.github/workflows/doc-build-release.yml b/.github/workflows/doc-build-release.yml index b304b462253..62312889b8b 100644 --- a/.github/workflows/doc-build-release.yml +++ b/.github/workflows/doc-build-release.yml @@ -36,6 +36,22 @@ jobs: sudo apt update sudo apt-get install pandoc libegl1 make xvfb libfontconfig1 libxrender1 libxkbcommon-x11-0 -y + - name: Install Quarto + uses: quarto-dev/quarto-actions/setup@v2 + with: + tinytex: true + + - name: Check Quarto Version + shell: bash + run: | + quarto --version + + - name: "Install Poppler for PDF to PNG conversion" + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y poppler-utils + - name: Install pyfluent run: make install diff --git a/doc/source/conf.py b/doc/source/conf.py index 1ed05a26c3a..0b7fbbdd122 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -42,10 +42,7 @@ toggleprompt_offset_right = 35 -skip_examples = int(os.getenv("PYFLUENT_SKIP_EXAMPLES_DOC", 0)) -if skip_examples: - pass -else: +if os.getenv("PYFLUENT_DOC_SKIP_EXAMPLES") != "1": extensions.append("sphinx_gallery.gen_gallery") typehints_document_rtype = False @@ -197,13 +194,15 @@ def _stop_fluent_container(gallery_conf, fname): "navbar_end": ["version-switcher", "theme-switcher", "navbar-icon-links"], "navigation_depth": -1, "collapse_navigation": True, - "cheatsheet": { +} + +if os.getenv("PYFLUENT_DOC_SKIP_CHEATSHEET") != "1": + html_theme_options["cheatsheet"] = { "file": "cheatsheet/cheat_sheet.qmd", "pages": ["index", "getting_started/index", "user_guide/index"], "title": "PyFluent cheat sheet", "version": __version__, - }, -} + } # -- Options for HTMLHelp output --------------------------------------------- diff --git a/doc/source/contributing/environment_variables.rst b/doc/source/contributing/environment_variables.rst index 75b47d6edd2..280b2b1505d 100644 --- a/doc/source/contributing/environment_variables.rst +++ b/doc/source/contributing/environment_variables.rst @@ -29,6 +29,10 @@ Following is a list of environment variables that can be set to control various - Specifies the path inside the container where the host path is mounted while starting a Fluent container in :func:`launch_fluent() `. * - PYFLUENT_FLUENT_DEBUG - Starts Fluent in debug mode while launching Fluent in :func:`launch_fluent() `. + * - PYFLUENT_DOC_SKIP_CHEATSHEET: + - Skips the generation of cheatsheet. + * - PYFLUENT_DOC_SKIP_EXAMPLES + - Skips the generation of examples documentation. * - PYFLUENT_FLUENT_IP - Specifies the IP address of the Fluent server in :func:`connect_to_fluent() `. * - PYFLUENT_FLUENT_PORT @@ -47,8 +51,6 @@ Following is a list of environment variables that can be set to control various - Shows the Fluent GUI while launching Fluent in :func:`launch_fluent() `. * - PYFLUENT_SKIP_API_UPGRADE_ADVICE - Disables printing of TUI to settings API upgrade advice. - * - PYFLUENT_SKIP_EXAMPLES_DOC - - Skips the generation of examples documentation. * - PYFLUENT_TIMEOUT_FORCE_EXIT - Enables force exit while exiting a Fluent session and specifies the timeout in seconds. * - PYFLUENT_WATCHDOG_DEBUG diff --git a/doc/source/user_guide/events.rst b/doc/source/user_guide/events.rst index d38879b01c1..1bd18e53a0b 100644 --- a/doc/source/user_guide/events.rst +++ b/doc/source/user_guide/events.rst @@ -18,13 +18,19 @@ The following code triggers a callback at the end of every iteration. .. code-block:: python - >>> from ansys.fluent.core import SolverEvent + >>> from ansys.fluent.core import SolverEvent, IterationEndedEventInfo >>> - >>> def on_iteration_ended(session, event_info): + >>> def on_iteration_ended(session, event_info: IterationEndedEventInfo): >>> print("Iteration ended. Index = ", event_info.index) >>> >>> callback_id = solver.events.register_callback(SolverEvent.ITERATION_ENDED, on_iteration_ended) - >>> + +The general signature of the callback function is ``cb(session, event_info, )``, where ``session`` is the session instance +and ``event_info`` instance holds information about the event. The event information classes for each event are documented in the +API reference of the :obj:`~ansys.fluent.core.streaming_services.events_streaming` module. See the callback function +``on_case_loaded_with_args()`` in the below examples for an example of how to pass additional arguments to the callback +function. + Examples -------- @@ -32,6 +38,7 @@ Examples .. code-block:: python >>> from ansys.fluent.core import MeshingEvent, SolverEvent + >>> from ansys.fluent.core import CaseLoadedEventInfo, DataLoadedEventInfo, SolutionInitializedEventInfo, IterationEndedEventInfo >>> from ansys.fluent.core.utils.event_loop import execute_in_event_loop_threadsafe >>> from ansys.fluent.visualization.matplotlib import matplot_windows_manager >>> from ansys.fluent.visualization.pyvista import pyvista_windows_manager @@ -48,7 +55,7 @@ Examples >>> contour2.surfaces_list = ["symmetry"] >>> >>> @execute_in_event_loop_threadsafe - >>> def auto_refersh_call_back_iteration(session, event_info): + >>> def auto_refersh_call_back_iteration(session, event_info: IterationEndedEventInfo): >>> if event_info.index % 5 == 0: >>> pyvista_windows_manager.refresh_windows(session.id, ["contour-1", "contour-2"]) >>> matplot_windows_manager.refresh_windows("", ["residual"]) @@ -56,7 +63,7 @@ Examples >>> callback_itr_id = solver.events.register_callback(SolverEvent.ITERATION_ENDED, auto_refersh_call_back_iteration) >>> >>> @execute_in_event_loop_threadsafe - >>> def initialize_call_back(session, event_info): + >>> def initialize_call_back(session, event_info: SolutionInitializedEventInfo | DataLoadedEventInfo): >>> pyvista_windows_manager.refresh_windows(session.id, ["contour-1", "contour-2"]) >>> matplot_windows_manager.refresh_windows("", ["residual"]) >>> @@ -64,10 +71,10 @@ Examples >>> >>> callback_data_read_id = solver.events.register_callback(SolverEvent.DATA_LOADED, initialize_call_back) >>> - >>> def on_case_loaded(session, event_info): + >>> def on_case_loaded(session, event_info: CaseLoadedEventInfo): >>> print("Case loaded. Index = ", event_info.index) >>> - >>> def on_case_loaded_with_args(x, y, session, event_info): + >>> def on_case_loaded_with_args(session, event_info: CaseLoadedEventInfo, x, y): >>> print(f"Case loaded with {x}, {y}. Index = ", event_info.index) >>> >>> callback = meshing.events.register_callback(MeshingEvent.CASE_LOADED, on_case_loaded) diff --git a/src/ansys/fluent/core/streaming_services/events_streaming.py b/src/ansys/fluent/core/streaming_services/events_streaming.py index 1d73b02c6aa..0c48c15511f 100644 --- a/src/ansys/fluent/core/streaming_services/events_streaming.py +++ b/src/ansys/fluent/core/streaming_services/events_streaming.py @@ -16,6 +16,7 @@ from ansys.fluent.core.warnings import PyFluentDeprecationWarning __all__ = [ + "EventsManager", "Event", "SolverEvent", "MeshingEvent", @@ -128,6 +129,7 @@ def __getattr__(self, name): @dataclass class TimestepStartedEventInfo(EventInfoBase, event=SolverEvent.TIMESTEP_STARTED): """Information about the event triggered when a timestep is started. + Attributes ---------- index : int @@ -143,6 +145,7 @@ class TimestepStartedEventInfo(EventInfoBase, event=SolverEvent.TIMESTEP_STARTED @dataclass class TimestepEndedEventInfo(EventInfoBase, event=SolverEvent.TIMESTEP_ENDED): """Information about the event triggered when a timestep is ended. + Attributes ---------- index : int @@ -158,6 +161,7 @@ class TimestepEndedEventInfo(EventInfoBase, event=SolverEvent.TIMESTEP_ENDED): @dataclass class IterationEndedEventInfo(EventInfoBase, event=SolverEvent.ITERATION_ENDED): """Information about the event triggered when an iteration is ended. + Attributes ---------- index : int @@ -190,6 +194,7 @@ class CalculationsResumedEventInfo( @dataclass class AboutToLoadCaseEventInfo(EventInfoBase, event=SolverEvent.ABOUT_TO_LOAD_CASE): """Information about the event triggered just before a case file is loaded. + Attributes ---------- case_file_name : str @@ -202,6 +207,7 @@ class AboutToLoadCaseEventInfo(EventInfoBase, event=SolverEvent.ABOUT_TO_LOAD_CA @dataclass class CaseLoadedEventInfo(EventInfoBase, event=SolverEvent.CASE_LOADED): """Information about the event triggered after a case file is loaded. + Attributes ---------- case_file_name : str @@ -214,6 +220,7 @@ class CaseLoadedEventInfo(EventInfoBase, event=SolverEvent.CASE_LOADED): @dataclass class AboutToLoadDataEventInfo(EventInfoBase, event=SolverEvent.ABOUT_TO_LOAD_DATA): """Information about the event triggered just before a data file is loaded. + Attributes ---------- data_file_name : str @@ -226,6 +233,7 @@ class AboutToLoadDataEventInfo(EventInfoBase, event=SolverEvent.ABOUT_TO_LOAD_DA @dataclass class DataLoadedEventInfo(EventInfoBase, event=SolverEvent.DATA_LOADED): """Information about the event triggered after a data file is loaded. + Attributes ---------- data_file_name : str @@ -252,6 +260,7 @@ class ReportDefinitionUpdatedEventInfo( EventInfoBase, event=SolverEvent.REPORT_DEFINITION_UPDATED ): """Information about the event triggered when a report definition is updated. + Attributes ---------- report_name : str @@ -266,6 +275,7 @@ class ReportPlotSetUpdatedEventInfo( EventInfoBase, event=SolverEvent.REPORT_PLOT_SET_UPDATED ): """Information about the event triggered when a report plot set is updated. + Attributes ---------- plot_set_name : str @@ -288,6 +298,7 @@ class SettingsClearedEventInfo(EventInfoBase, event=SolverEvent.SETTINGS_CLEARED @dataclass class SolutionPausedEventInfo(EventInfoBase, event=SolverEvent.SOLUTION_PAUSED): """Information about the event triggered when solution is paused. + Attributes ---------- level : str @@ -303,6 +314,7 @@ class SolutionPausedEventInfo(EventInfoBase, event=SolverEvent.SOLUTION_PAUSED): @dataclass class ProgressUpdatedEventInfo(EventInfoBase, event=SolverEvent.PROGRESS_UPDATED): """Information about the event triggered when progress is updated. + Attributes ---------- message : str @@ -320,6 +332,7 @@ class SolverTimeEstimateUpdatedEventInfo( EventInfoBase, event=SolverEvent.SOLVER_TIME_ESTIMATE_UPDATED ): """Information about the event triggered when solver time estimate is updated. + Attributes ---------- hours : float @@ -338,6 +351,7 @@ class SolverTimeEstimateUpdatedEventInfo( @dataclass class FatalErrorEventInfo(EventInfoBase, event=SolverEvent.FATAL_ERROR): """Information about the event triggered when a fatal error occurs. + Attributes ---------- message : str @@ -428,23 +442,26 @@ def _process_streaming( @staticmethod def _make_callback_to_call(callback: Callable, args, kwargs): - old_style = "session_id" in inspect.signature(callback).parameters - if old_style: + params = inspect.signature(callback).parameters + if "session_id" in params: warnings.warn( "Update event callback function signatures" " substituting 'session' for 'session_id'.", PyFluentDeprecationWarning, ) - fn = partial(callback, *args, **kwargs) - return ( - ( - lambda session, event_info: fn( - session_id=session.id, event_info=event_info - ) + return lambda session, event_info: callback( + *args, session_id=session.id, event_info=event_info, **kwargs + ) + else: + positional_args = [ + p + for p in params + if p not in kwargs and p not in ("session", "event_info") + ] + kwargs.update(dict(zip(positional_args, args))) + return lambda session, event_info: callback( + session=session, event_info=event_info, **kwargs ) - if old_style - else fn - ) def register_callback( self, diff --git a/tests/test_events_manager.py b/tests/test_events_manager.py index 925ed39de3e..fea1b35b067 100644 --- a/tests/test_events_manager.py +++ b/tests/test_events_manager.py @@ -28,7 +28,12 @@ def on_case_loaded(session, event_info): on_case_loaded.loaded = False - def on_case_loaded_with_args(x, y, session, event_info): + def on_case_loaded_with_args_optional_first(x, y, session, event_info): + on_case_loaded_with_args_optional_first.state = dict(x=x, y=y) + + on_case_loaded_with_args_optional_first.state = None + + def on_case_loaded_with_args(session, event_info, x, y): on_case_loaded_with_args.state = dict(x=x, y=y) on_case_loaded_with_args.state = None @@ -43,6 +48,10 @@ def on_case_loaded_with_args(x, y, session, event_info): solver.events.register_callback(SolverEvent.CASE_LOADED, on_case_loaded) + solver.events.register_callback( + SolverEvent.CASE_LOADED, on_case_loaded_with_args_optional_first, 12, y=42 + ) + solver.events.register_callback( SolverEvent.CASE_LOADED, on_case_loaded_with_args, 12, y=42 ) @@ -54,6 +63,7 @@ def on_case_loaded_with_args(x, y, session, event_info): assert not on_case_loaded_old.loaded assert not on_case_loaded.loaded assert not on_case_loaded_old_with_args.state + assert not on_case_loaded_with_args_optional_first.state assert not on_case_loaded_with_args.state try: @@ -64,6 +74,7 @@ def on_case_loaded_with_args(x, y, session, event_info): assert on_case_loaded_old.loaded assert on_case_loaded.loaded assert on_case_loaded_old_with_args.state == dict(x=12, y=42) + assert on_case_loaded_with_args_optional_first.state == dict(x=12, y=42) assert on_case_loaded_with_args.state == dict(x=12, y=42) From 89f91ac1dd9cce2fa5fe85316e8040fc69eadab9 Mon Sep 17 00:00:00 2001 From: Roberto Pastor Muela <37798125+RobPasMue@users.noreply.github.com> Date: Tue, 3 Dec 2024 20:09:34 +0100 Subject: [PATCH 2/3] fix: missing EOF line on CONTRIBUTORS.md (#3543) --- CONTRIBUTORS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 803bd936d15..4fc1b8a5037 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -13,4 +13,4 @@ * [Harshal Pohekar](https://github.com/hpohekar) * [Mainak Kundu](https://github.com/mkundu1) * [Prithwish Mukherjee](https://github.com/prmukherj) -* [Raphael Luciano](https://github.com/raph-luc) \ No newline at end of file +* [Raphael Luciano](https://github.com/raph-luc) From 2e41f58c833c04c9cf49cc5b07b9beded6404447 Mon Sep 17 00:00:00 2001 From: Mainak Kundu <94432368+mkundu1@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:12:31 -0500 Subject: [PATCH 3/3] feat: cleanup old codegen related code (#3545) --- .../contributing/environment_variables.rst | 2 + src/ansys/fluent/core/__init__.py | 9 +- src/ansys/fluent/core/codegen/allapigen.py | 3 - .../core/codegen/builtin_settingsgen.py | 25 +- .../fluent/core/codegen/settingsgen_old.py | 535 ------------------ src/ansys/fluent/core/solver/flobject.py | 32 +- tests/test_codegen.py | 410 -------------- 7 files changed, 20 insertions(+), 996 deletions(-) delete mode 100644 src/ansys/fluent/core/codegen/settingsgen_old.py diff --git a/doc/source/contributing/environment_variables.rst b/doc/source/contributing/environment_variables.rst index 280b2b1505d..9446428ff46 100644 --- a/doc/source/contributing/environment_variables.rst +++ b/doc/source/contributing/environment_variables.rst @@ -21,6 +21,8 @@ Following is a list of environment variables that can be set to control various - Specifies the Docker image name while starting a Fluent container in :func:`launch_fluent() `. * - FLUENT_IMAGE_TAG - Specifies the Docker image tag while starting a Fluent container in :func:`launch_fluent() `. + * - PYFLUENT_CODEGEN_OUTDIR + - Specifies the directory where API files are written out during codegen. * - PYFLUENT_CODEGEN_SKIP_BUILTIN_SETTINGS - Skips the generation of built-in settings during codegen. * - PYFLUENT_CONTAINER_MOUNT_SOURCE diff --git a/src/ansys/fluent/core/__init__.py b/src/ansys/fluent/core/__init__.py index 6fcdacbf8d1..43b55531c15 100644 --- a/src/ansys/fluent/core/__init__.py +++ b/src/ansys/fluent/core/__init__.py @@ -103,11 +103,10 @@ def version_info() -> str: # Whether to use remote gRPC file transfer service USE_FILE_TRANSFER_SERVICE = False -# Directory where API files are writes out during codegen -CODEGEN_OUTDIR = (Path(__file__) / ".." / "generated").resolve() - -# Whether to zip settings API files during codegen -CODEGEN_ZIP_SETTINGS = os.getenv("PYFLUENT_CODEGEN_ZIP_SETTINGS", False) +# Directory where API files are written out during codegen +CODEGEN_OUTDIR = os.getenv( + "PYFLUENT_CODEGEN_OUTDIR", (Path(__file__) / ".." / "generated").resolve() +) # Whether to show mesh in Fluent after case read FLUENT_SHOW_MESH_AFTER_CASE_READ = False diff --git a/src/ansys/fluent/core/codegen/allapigen.py b/src/ansys/fluent/core/codegen/allapigen.py index cb26b444bf8..1c9f620845b 100644 --- a/src/ansys/fluent/core/codegen/allapigen.py +++ b/src/ansys/fluent/core/codegen/allapigen.py @@ -23,9 +23,6 @@ def generate(version: str, static_infos: dict): api_tree = {"": {}, "": {}} _update_first_level(api_tree, tuigen.generate(version, static_infos)) _update_first_level(api_tree, datamodelgen.generate(version, static_infos)) - if os.getenv("PYFLUENT_USE_OLD_SETTINGSGEN") == "1": - global settingsgen - from ansys.fluent.core.codegen import settingsgen_old as settingsgen _update_first_level(api_tree, settingsgen.generate(version, static_infos)) api_tree_file = get_api_tree_file_name(version) Path(api_tree_file).parent.mkdir(parents=True, exist_ok=True) diff --git a/src/ansys/fluent/core/codegen/builtin_settingsgen.py b/src/ansys/fluent/core/codegen/builtin_settingsgen.py index d7ca37a8841..acc32494f1e 100644 --- a/src/ansys/fluent/core/codegen/builtin_settingsgen.py +++ b/src/ansys/fluent/core/codegen/builtin_settingsgen.py @@ -1,8 +1,5 @@ """Generate builtin setting classes.""" -import os -from zipimport import zipimporter - from ansys.fluent.core import CODEGEN_OUTDIR, FluentVersion from ansys.fluent.core.solver.flobject import CreatableNamedObjectMixin, NamedObject from ansys.fluent.core.solver.settings_builtin_data import DATA @@ -12,24 +9,12 @@ def _get_settings_root(version: str): - from ansys.fluent.core import CODEGEN_OUTDIR, CODEGEN_ZIP_SETTINGS, utils + from ansys.fluent.core import CODEGEN_OUTDIR, utils - if os.getenv("PYFLUENT_USE_OLD_SETTINGSGEN") != "1": - settings = utils.load_module( - f"settings_{version}", - CODEGEN_OUTDIR / "solver" / f"settings_{version}.py", - ) - else: - if CODEGEN_ZIP_SETTINGS: - importer = zipimporter( - str(CODEGEN_OUTDIR / "solver" / f"settings_{version}.zip") - ) - settings = importer.load_module("settings") - else: - settings = utils.load_module( - f"settings_{version}", - CODEGEN_OUTDIR / "solver" / f"settings_{version}" / "__init__.py", - ) + settings = utils.load_module( + f"settings_{version}", + CODEGEN_OUTDIR / "solver" / f"settings_{version}.py", + ) return settings.root diff --git a/src/ansys/fluent/core/codegen/settingsgen_old.py b/src/ansys/fluent/core/codegen/settingsgen_old.py deleted file mode 100644 index 990d32bbb3b..00000000000 --- a/src/ansys/fluent/core/codegen/settingsgen_old.py +++ /dev/null @@ -1,535 +0,0 @@ -"""Provide a module to generate the Fluent settings tree. - -Running this module generates a python module with the definition of the Fluent -settings classes. The out is placed at: - -- src/ansys/fluent/core/solver/settings.py - -Running this module requires Fluent to be installed. - -Process -------- - - Launch fluent and get static info. Parse the class with flobject.get_cls() - - Generate a dictionary of unique classes with their hash as a key and a tuple of cls, children hash, commands hash, arguments hash, child object type hash as value. - - - This eliminates reduandancy and only unique classes are written. - - Generate .py files for the classes in hash dictionary. Resolve named conflicts with integer suffix. - - - Populate files dictionary with hash as key and file name as value. - - - child_object_type handled specially to avoid a lot of files with same name and to provide more insight of the child. - - Populate the classes. - - - For writing the import statements, get the hash of the child/command/argument/named object stored in the hash dict tuple value. - - - Use that hash to locate the corresponding children file name in the hash dict. - -Usage ------ -python -""" - -import hashlib -import io -import os -from pathlib import Path -import pickle -import pprint -import shutil - -import ansys.fluent.core as pyfluent -from ansys.fluent.core import launch_fluent -from ansys.fluent.core.codegen import StaticInfoType -from ansys.fluent.core.solver import flobject -from ansys.fluent.core.utils.fix_doc import fix_settings_doc -from ansys.fluent.core.utils.fluent_version import get_version_for_file_name - -hash_dict = {} -files_dict = {} -root_class_path = "" - - -def _gethash(obj_info): - dhash = hashlib.sha256() - dhash.update(pickle.dumps(obj_info)) - return dhash.hexdigest() - - -def _get_indent_str(indent): - return f"{' '*indent*4}" - - -def _populate_hash_dict(name, info, cls, api_tree): - children = info.get("children") - if children: - children_hash = [] - for cname, cinfo in children.items(): - for child in getattr(cls, "child_names", None): - child_cls = cls._child_classes[child] - if cname == child_cls.fluent_name: - api_tree[child] = {} - children_hash.append( - _populate_hash_dict(cname, cinfo, child_cls, api_tree[child]) - ) - okey = f"{child}:" - if okey in api_tree[child]: - api_tree[child].update(api_tree[child][okey]) - del api_tree[child][okey] - api_tree[okey] = api_tree.pop(child) - else: - api_tree[child] = api_tree[child] or "Parameter" - break - else: - children_hash = None - - commands = info.get("commands") - if commands: - commands_hash = [] - for cname, cinfo in commands.items(): - for command in getattr(cls, "command_names", None): - command_cls = cls._child_classes[command] - if cname == command_cls.fluent_name: - api_tree[command] = "Command" - commands_hash.append( - _populate_hash_dict(cname, cinfo, command_cls, {}) - ) - break - else: - commands_hash = None - - queries = info.get("queries") - if queries: - queries_hash = [] - for qname, qinfo in queries.items(): - for query in getattr(cls, "query_names", None): - query_cls = cls._child_classes[query] - if qname == query_cls.fluent_name: - api_tree[query] = "Query" - queries_hash.append( - _populate_hash_dict(qname, qinfo, query_cls, {}) - ) - break - else: - queries_hash = None - - arguments = info.get("arguments") - if arguments: - arguments_hash = [] - for aname, ainfo in arguments.items(): - for argument in getattr(cls, "argument_names", None): - argument_cls = cls._child_classes[argument] - if aname == argument_cls.fluent_name: - arguments_hash.append( - _populate_hash_dict(aname, ainfo, argument_cls, {}) - ) - break - else: - arguments_hash = None - - object_type = info.get("object-type") - if object_type: - key = f"{cls.__name__}:" - api_tree[key] = {} - object_hash = _populate_hash_dict( - "child-object-type", - object_type, - getattr(cls, "child_object_type", None), - api_tree[key], - ) - else: - object_hash = None - - cls_tuple = ( - name, - cls.__name__, - cls.__bases__, - info["type"], - info.get("help"), - children_hash, - commands_hash, - queries_hash, - arguments_hash, - object_hash, - ) - hash = _gethash(cls_tuple) - if not hash_dict.get(hash): - hash_dict[hash] = ( - cls, - children_hash, - commands_hash, - queries_hash, - arguments_hash, - object_hash, - ) - return hash - - -class _CommandInfo: - def __init__(self, doc, args_info): - self.doc = doc - self.args_info = args_info - - -_arg_type_strings = { - flobject.Boolean: "bool", - flobject.Integer: "int", - flobject.Real: "float | str", - flobject.String: "str", - flobject.Filename: "str", - flobject.BooleanList: "List[bool]", - flobject.IntegerList: "List[int]", - flobject.RealVector: "Tuple[float | str, float | str, float | str", - flobject.RealList: "List[float | str]", - flobject.StringList: "List[str]", - flobject.FilenameList: "List[str]", -} - - -def _get_commands_info(commands_hash): - commands_info = {} - for command_hash in commands_hash: - command_hash_info = hash_dict.get(command_hash) - command_cls = command_hash_info[0] - command_name = command_cls.__name__ - command_info = _CommandInfo(command_cls.__doc__, []) - if command_hash_info[4]: - for arg_hash in command_hash_info[4]: - arg_hash_info = hash_dict.get(arg_hash) - arg_cls = arg_hash_info[0] - arg_name = arg_cls.__name__ - arg_type = _arg_type_strings[arg_cls.__bases__[0]] - command_info.args_info.append(f"{arg_name}: {arg_type}") - commands_info[command_name] = command_info - return commands_info - - -def _write_doc_string(doc, indent, writer): - doc = ("\n" + indent).join(doc.split("\n")) - writer.write(f'{indent}"""\n') - writer.write(f"{indent}{doc}") - writer.write(f'\n{indent}"""\n\n') - - -def _populate_classes(parent_dir): - istr = _get_indent_str(0) - istr1 = _get_indent_str(1) - istr2 = _get_indent_str(2) - files = [] - # generate files - for key, ( - cls, - children_hash, - commands_hash, - queries_hash, - arguments_hash, - object_hash, - ) in hash_dict.items(): - cls_name = file_name = cls.__name__ - if cls_name == "child_object_type": - # Get the first parent for this class. - for ( - cls1, - children_hash1, - commands_hash1, - queries_hash1, - arguments_hash1, - object_hash1, - ) in hash_dict.values(): - if key == object_hash1: - cls.__name__ = file_name = cls1.__name__ + "_child" - break - i = 0 - while file_name in files: - if i > 0: - file_name = file_name[: file_name.rfind("_")] - i += 1 - file_name += "_" + str(i) - files.append(file_name) - files_dict[key] = file_name - - # Store root class path for __init__.py - if cls_name == "root": - global root_class_path - root_class_path = file_name - - file_name += ".py" - file_name = os.path.normpath(os.path.join(parent_dir, file_name)) - with open(file_name, "w") as f: - f.write(f"name: {cls_name}") - - # populate files - for key, ( - cls, - children_hash, - commands_hash, - queries_hash, - arguments_hash, - object_hash, - ) in hash_dict.items(): - file_name = files_dict.get(key) - cls_name = cls.__name__ - file_name = os.path.normpath(os.path.join(parent_dir, file_name + ".py")) - stub_f = None - if not pyfluent.CODEGEN_ZIP_SETTINGS: - stub_file_name = file_name + "i" - stub_f = open(stub_file_name, "w") - with open(file_name, "w") as f: - # disclaimer to py file - f.write("#\n") - f.write("# This is an auto-generated file. DO NOT EDIT!\n") - f.write("#\n") - f.write("\n") - if stub_f: - stub_f.write("#\n") - stub_f.write("# This is an auto-generated file. DO NOT EDIT!\n") - stub_f.write("#\n") - stub_f.write("\n\n") - - # write imports to py file - import_str = ( - "from ansys.fluent.core.solver.flobject import *\n\n" - "from ansys.fluent.core.solver.flobject import (\n" - f"{istr1}_ChildNamedObjectAccessorMixin,\n" - f"{istr1}CreatableNamedObjectMixin,\n" - f"{istr1}_NonCreatableNamedObjectMixin,\n" - f"{istr1}AllowedValuesMixin,\n" - f"{istr1}_InputFile,\n" - f"{istr1}_OutputFile,\n" - f"{istr1}_InOutFile,\n" - ")\n\n" - ) - f.write(import_str) - if stub_f: - stub_f.write(import_str) - stub_f.write("from typing import Union, List, Tuple\n\n") - - if children_hash: - for child in children_hash: - pchild_name = hash_dict.get(child)[0].__name__ - import_str = f"from .{files_dict.get(child)} import {pchild_name} as {pchild_name}_cls\n" - f.write(import_str) - if stub_f: - stub_f.write(import_str) - - if commands_hash: - for child in commands_hash: - pchild_name = hash_dict.get(child)[0].__name__ - import_str = f"from .{files_dict.get(child)} import {pchild_name} as {pchild_name}_cls\n" - f.write(import_str) - if stub_f: - stub_f.write(import_str) - - if queries_hash: - for child in queries_hash: - pchild_name = hash_dict.get(child)[0].__name__ - import_str = f"from .{files_dict.get(child)} import {pchild_name} as {pchild_name}_cls\n" - f.write(import_str) - if stub_f: - stub_f.write(import_str) - - if arguments_hash: - for child in arguments_hash: - pchild_name = hash_dict.get(child)[0].__name__ - import_str = f"from .{files_dict.get(child)} import {pchild_name} as {pchild_name}_cls\n" - f.write(import_str) - if stub_f: - stub_f.write(import_str) - - if object_hash: - pchild_name = hash_dict.get(object_hash)[0].__name__ - import_str = ( - f"from .{files_dict.get(object_hash)} import {pchild_name}\n\n" - ) - f.write(import_str) - if stub_f: - stub_f.write(import_str) - - # class name - class_def_str = ( - f"\n{istr}class {cls_name}" - f'({", ".join(f"{c.__name__}[{hash_dict.get(object_hash)[0].__name__}]" if object_hash else c.__name__ for c in cls.__bases__)}):\n' - ) - f.write(class_def_str) - if stub_f: - stub_f.write(class_def_str) - - doc = fix_settings_doc(cls.__doc__) - # Custom doc for child object type - if cls.fluent_name == "child-object-type": - parent_name = Path(file_name).stem[ - 0 : Path(file_name).stem.find("_child") - ] - doc = f"'child_object_type' of {parent_name}." - - _write_doc_string(doc, istr1, f) - f.write(f'{istr1}fluent_name = "{cls.fluent_name}"\n') - f.write(f'{istr1}version = "{cls.version}"\n\n') - if stub_f: - stub_f.write(f"{istr1}fluent_name = ...\n") - stub_f.write(f"{istr1}version = ...\n\n") - - child_class_strings = [] - - # write children objects - child_names = getattr(cls, "child_names", None) - if child_names: - f.write(f"{istr1}child_names = \\\n") - strout = io.StringIO() - pprint.pprint(child_names, stream=strout, compact=True, width=70) - mn = ("\n" + istr2).join(strout.getvalue().strip().split("\n")) - f.write(f"{istr2}{mn}\n\n") - if stub_f: - stub_f.write(f"{istr1}child_names = ...\n") - - for child in child_names: - child_cls = cls._child_classes[child] - child_class_strings.append(f"{child}={child_cls.__name__}_cls") - if stub_f: - stub_f.write( - f"{istr1}{child}: {child_cls.__name__}_cls = ...\n" - ) - - # write command objects - command_names = getattr(cls, "command_names", None) - if command_names: - f.write(f"{istr1}command_names = \\\n") - strout = io.StringIO() - pprint.pprint(command_names, stream=strout, compact=True, width=70) - mn = ("\n" + istr2).join(strout.getvalue().strip().split("\n")) - f.write(f"{istr2}{mn}\n\n") - if stub_f: - stub_f.write(f"{istr1}command_names = ...\n\n") - - commands_info = _get_commands_info(commands_hash) - for command in command_names: - command_cls = cls._child_classes[command] - child_class_strings.append(f"{command}={command_cls.__name__}_cls") - # function annotation for commands - command_info = commands_info[command] - if stub_f: - stub_f.write(f"{istr1}def {command}(self, ") - stub_f.write(", ".join(command_info.args_info)) - stub_f.write("):\n") - _write_doc_string(command_info.doc, istr2, stub_f) - - # write query objects - query_names = getattr(cls, "query_names", None) - if query_names: - f.write(f"{istr1}query_names = \\\n") - strout = io.StringIO() - pprint.pprint(query_names, stream=strout, compact=True, width=70) - mn = ("\n" + istr2).join(strout.getvalue().strip().split("\n")) - f.write(f"{istr2}{mn}\n\n") - if stub_f: - stub_f.write(f"{istr1}query_names = ...\n\n") - - queries_info = _get_commands_info(queries_hash) - for query in query_names: - query_cls = cls._child_classes[query] - child_class_strings.append(f"{query}={query_cls.__name__}_cls") - # function annotation for queries - query_info = queries_info[query] - if stub_f: - stub_f.write(f"{istr1}def {query}(self, ") - stub_f.write(", ".join(query_info.args_info)) - stub_f.write("):\n") - _write_doc_string(query_info.doc, istr2, stub_f) - - # write arguments - arguments = getattr(cls, "argument_names", None) - if arguments: - f.write(f"{istr1}argument_names = \\\n") - strout = io.StringIO() - pprint.pprint(arguments, stream=strout, compact=True, width=70) - mn = ("\n" + istr2).join(strout.getvalue().strip().split("\n")) - f.write(f"{istr2}{mn}\n\n") - if stub_f: - stub_f.write(f"{istr1}argument_names = ...\n") - - for argument in arguments: - argument_cls = cls._child_classes[argument] - child_class_strings.append( - f"{argument}={argument_cls.__name__}_cls" - ) - if stub_f: - stub_f.write( - f"{istr1}{argument}: {argument_cls.__name__}_cls = ...\n" - ) - - if child_class_strings: - f.write(f"{istr1}_child_classes = dict(\n") - f.writelines( - [f"{istr2}{cls_str},\n" for cls_str in child_class_strings] - ) - f.write(f"{istr1})\n\n") - - child_aliases = getattr(cls, "_child_aliases", None) - if child_aliases: - f.write(f"{istr1}_child_aliases = dict(\n") - f.writelines([f"{istr2}{k}={v!r},\n" for k, v in child_aliases.items()]) - f.write(f"{istr1})\n\n") - - # write object type - child_object_type = getattr(cls, "child_object_type", None) - if child_object_type: - f.write(f"{istr1}child_object_type: {pchild_name} = {pchild_name}\n") - f.write(f'{istr1}"""\n') - f.write(f"{istr1}child_object_type of {cls_name}.") - f.write(f'\n{istr1}"""\n') - if stub_f: - stub_f.write(f"{istr1}child_object_type: {pchild_name} = ...\n") - - return_type = getattr(cls, "return_type", None) - if return_type: - f.write(f'{istr1}return_type = "{return_type}"\n') - if stub_f: - stub_f.write(f"{istr1}return_type = ...\n") - if stub_f: - stub_f.close() - - -def _populate_init(parent_dir, hash): - file_name = os.path.normpath(os.path.join(parent_dir, "__init__.py")) - with open(file_name, "w") as f: - f.write("#\n") - f.write("# This is an auto-generated file. DO NOT EDIT!\n") - f.write("#\n") - f.write("\n") - f.write('"""A package providing Fluent\'s Settings Objects in Python."""') - f.write("\n") - f.write("from ansys.fluent.core.solver.flobject import *\n\n") - f.write(f'SHASH = "{hash}"\n') - f.write(f"from .{root_class_path} import root") - - -def generate(version, static_infos: dict): - """Generate settings API classes.""" - parent_dir = (pyfluent.CODEGEN_OUTDIR / "solver" / f"settings_{version}").resolve() - api_tree = {} - sinfo = static_infos.get(StaticInfoType.SETTINGS) - - # Clear previously generated data - if os.path.exists(parent_dir): - shutil.rmtree(parent_dir) - - if sinfo: - hash = _gethash(sinfo) - os.makedirs(parent_dir) - - if pyfluent.CODEGEN_ZIP_SETTINGS: - parent_dir = parent_dir / "settings" - os.makedirs(parent_dir) - - cls, _ = flobject.get_cls("", sinfo, version=version) - - _populate_hash_dict("", sinfo, cls, api_tree) - _populate_classes(parent_dir) - _populate_init(parent_dir, hash) - - if pyfluent.CODEGEN_ZIP_SETTINGS: - shutil.make_archive(parent_dir.parent, "zip", parent_dir.parent) - shutil.rmtree(parent_dir.parent) - - return {"": api_tree} - - -if __name__ == "__main__": - solver = launch_fluent() - version = get_version_for_file_name(session=solver) - static_infos = {StaticInfoType.SETTINGS: solver._settings_service.get_static_info()} - generate(version, static_infos) diff --git a/src/ansys/fluent/core/solver/flobject.py b/src/ansys/fluent/core/solver/flobject.py index b987300dcd8..3088c70b172 100644 --- a/src/ansys/fluent/core/solver/flobject.py +++ b/src/ansys/fluent/core/solver/flobject.py @@ -46,7 +46,6 @@ ) import warnings import weakref -from zipimport import zipimporter import ansys.fluent.core as pyfluent from ansys.fluent.core.utils.fluent_version import FluentVersion @@ -2161,30 +2160,17 @@ def get_root( RuntimeError If hash values are inconsistent. """ - from ansys.fluent.core import CODEGEN_OUTDIR, CODEGEN_ZIP_SETTINGS, utils + from ansys.fluent.core import CODEGEN_OUTDIR, utils - if os.getenv("PYFLUENT_USE_OLD_SETTINGSGEN") != "1": - try: - settings = utils.load_module( - f"settings_{version}", - CODEGEN_OUTDIR / "solver" / f"settings_{version}.py", - ) - root_cls = settings.root - except FileNotFoundError: - obj_info = flproxy.get_static_info() - root_cls, _ = get_cls("", obj_info, version=version) - else: - if CODEGEN_ZIP_SETTINGS: - importer = zipimporter( - str(CODEGEN_OUTDIR / "solver" / f"settings_{version}.zip") - ) - settings = importer.load_module("settings") - else: - settings = utils.load_module( - f"settings_{version}", - CODEGEN_OUTDIR / "solver" / f"settings_{version}" / "__init__.py", - ) + try: + settings = utils.load_module( + f"settings_{version}", + CODEGEN_OUTDIR / "solver" / f"settings_{version}.py", + ) root_cls = settings.root + except FileNotFoundError: + obj_info = flproxy.get_static_info() + root_cls, _ = get_cls("", obj_info, version=version) root = root_cls() root.set_flproxy(flproxy) root._set_on_interrupt(interrupt) diff --git a/tests/test_codegen.py b/tests/test_codegen.py index 65f312b3949..214bbd29588 100644 --- a/tests/test_codegen.py +++ b/tests/test_codegen.py @@ -430,416 +430,6 @@ def _get_query_settings_static_info(name, args): } -_expected_init_settings_api_output = '''# -# This is an auto-generated file. DO NOT EDIT! -# - -"""A package providing Fluent's Settings Objects in Python.""" -from ansys.fluent.core.solver.flobject import * - -SHASH = "3e6d76a4601701388ea8258912d145b7b7c436699a50b6c7fe9a29f41eeff194" -from .root import root''' - - -_expected_root_settings_api_output = '''# -# This is an auto-generated file. DO NOT EDIT! -# - -from ansys.fluent.core.solver.flobject import * - -from ansys.fluent.core.solver.flobject import ( - _ChildNamedObjectAccessorMixin, - CreatableNamedObjectMixin, - _NonCreatableNamedObjectMixin, - AllowedValuesMixin, - _InputFile, - _OutputFile, - _InOutFile, -) - -from .G1 import G1 as G1_cls -from .P1 import P1 as P1_cls -from .N1 import N1 as N1_cls -from .C1 import C1 as C1_cls -from .Q1 import Q1 as Q1_cls - -class root(Group): - """ - 'root' object. - """ - - fluent_name = "" - version = "251" - - child_names = \\ - ['G1', 'P1', 'N1'] - - command_names = \\ - ['C1'] - - query_names = \\ - ['Q1'] - - _child_classes = dict( - G1=G1_cls, - P1=P1_cls, - N1=N1_cls, - C1=C1_cls, - Q1=Q1_cls, - )''' - - -_expected_A1_settings_api_output = '''# -# This is an auto-generated file. DO NOT EDIT! -# - -from ansys.fluent.core.solver.flobject import * - -from ansys.fluent.core.solver.flobject import ( - _ChildNamedObjectAccessorMixin, - CreatableNamedObjectMixin, - _NonCreatableNamedObjectMixin, - AllowedValuesMixin, - _InputFile, - _OutputFile, - _InOutFile, -) - - -class A1(String): - """ - A1 help. - """ - - fluent_name = "A1" - version = "251"''' - - -_expected_C1_settings_api_output = '''# -# This is an auto-generated file. DO NOT EDIT! -# - -from ansys.fluent.core.solver.flobject import * - -from ansys.fluent.core.solver.flobject import ( - _ChildNamedObjectAccessorMixin, - CreatableNamedObjectMixin, - _NonCreatableNamedObjectMixin, - AllowedValuesMixin, - _InputFile, - _OutputFile, - _InOutFile, -) - -from .A1 import A1 as A1_cls - -class C1(Command): - """ - C1 help. - - Parameters - ---------- - A1 : str - A1 help. - - """ - - fluent_name = "C1" - version = "251" - - argument_names = \\ - ['A1'] - - _child_classes = dict( - A1=A1_cls, - )''' # noqa: W293 - - -_expected_G1_settings_api_output = '''# -# This is an auto-generated file. DO NOT EDIT! -# - -from ansys.fluent.core.solver.flobject import * - -from ansys.fluent.core.solver.flobject import ( - _ChildNamedObjectAccessorMixin, - CreatableNamedObjectMixin, - _NonCreatableNamedObjectMixin, - AllowedValuesMixin, - _InputFile, - _OutputFile, - _InOutFile, -) - -from .G2 import G2 as G2_cls -from .P2 import P2 as P2_cls -from .C2 import C2 as C2_cls -from .Q2 import Q2 as Q2_cls - -class G1(Group): - """ - G1 help. - """ - - fluent_name = "G1" - version = "251" - - child_names = \\ - ['G2', 'P2'] - - command_names = \\ - ['C2'] - - query_names = \\ - ['Q2'] - - _child_classes = dict( - G2=G2_cls, - P2=P2_cls, - C2=C2_cls, - Q2=Q2_cls, - )''' - - -_expected_N1_settings_api_output = '''# -# This is an auto-generated file. DO NOT EDIT! -# - -from ansys.fluent.core.solver.flobject import * - -from ansys.fluent.core.solver.flobject import ( - _ChildNamedObjectAccessorMixin, - CreatableNamedObjectMixin, - _NonCreatableNamedObjectMixin, - AllowedValuesMixin, - _InputFile, - _OutputFile, - _InOutFile, -) - -from .P4 import P4 as P4_cls -from .N1_child import N1_child - - -class N1(NamedObject[N1_child], _NonCreatableNamedObjectMixin[N1_child]): - """ - N1 help. - """ - - fluent_name = "N1" - version = "251" - - child_names = \\ - ['P4'] - - _child_classes = dict( - P4=P4_cls, - ) - - child_object_type: N1_child = N1_child - """ - child_object_type of N1. - """''' - - -_expected_N1_child_settings_api_output = '''# -# This is an auto-generated file. DO NOT EDIT! -# - -from ansys.fluent.core.solver.flobject import * - -from ansys.fluent.core.solver.flobject import ( - _ChildNamedObjectAccessorMixin, - CreatableNamedObjectMixin, - _NonCreatableNamedObjectMixin, - AllowedValuesMixin, - _InputFile, - _OutputFile, - _InOutFile, -) - - -class N1_child(Group): - """ - 'child_object_type' of N1. - """ - - fluent_name = "child-object-type" - version = "251"''' - - -_expected_P1_settings_api_output = '''# -# This is an auto-generated file. DO NOT EDIT! -# - -from ansys.fluent.core.solver.flobject import * - -from ansys.fluent.core.solver.flobject import ( - _ChildNamedObjectAccessorMixin, - CreatableNamedObjectMixin, - _NonCreatableNamedObjectMixin, - AllowedValuesMixin, - _InputFile, - _OutputFile, - _InOutFile, -) - - -class P1(String): - """ - P1 help. - """ - - fluent_name = "P1" - version = "251"''' - - -_expected_Q1_settings_api_output = '''# -# This is an auto-generated file. DO NOT EDIT! -# - -from ansys.fluent.core.solver.flobject import * - -from ansys.fluent.core.solver.flobject import ( - _ChildNamedObjectAccessorMixin, - CreatableNamedObjectMixin, - _NonCreatableNamedObjectMixin, - AllowedValuesMixin, - _InputFile, - _OutputFile, - _InOutFile, -) - -from .A1 import A1 as A1_cls - -class Q1(Query): - """ - Q1 help. - - Parameters - ---------- - A1 : str - A1 help. - - """ - - fluent_name = "Q1" - version = "251" - - argument_names = \\ - ['A1'] - - _child_classes = dict( - A1=A1_cls, - )''' # noqa: W293 - - -def test_codegen_old_with_settings_static_info(monkeypatch): - monkeypatch.setenv("PYFLUENT_USE_OLD_SETTINGSGEN", "1") - codegen_outdir = Path(tempfile.mkdtemp()) - monkeypatch.setattr(pyfluent, "CODEGEN_OUTDIR", codegen_outdir) - version = "251" - static_infos = {} - static_infos[StaticInfoType.SETTINGS] = _settings_static_info - allapigen.generate(version, static_infos) - generated_paths = list(codegen_outdir.iterdir()) - assert len(generated_paths) == 2 - assert set(p.name for p in generated_paths) == { - f"api_tree_{version}.pickle", - "solver", - } - solver_paths = list((codegen_outdir / "solver").iterdir()) - assert len(solver_paths) == 1 - assert set(p.name for p in solver_paths) == {f"settings_{version}"} - settings_paths = list((codegen_outdir / "solver" / f"settings_{version}").iterdir()) - filenames = [ - "root", - "A1", - "A2", - "C1", - "C2", - "G1", - "G2", - "N1", - "N1_child", - "P1", - "P2", - "P3", - "P4", - "Q1", - "Q2", - ] - filenames = ( - ["__init__.py"] - + [f"{f}.py" for f in filenames] - + [f"{f}.pyi" for f in filenames] - ) - assert set(p.name for p in settings_paths) == set(filenames) - with open( - codegen_outdir / "solver" / f"settings_{version}" / "__init__.py", "r" - ) as f: - assert f.read().strip() == _expected_init_settings_api_output - with open(codegen_outdir / "solver" / f"settings_{version}" / "root.py", "r") as f: - assert f.read().strip() == _expected_root_settings_api_output - with open(codegen_outdir / "solver" / f"settings_{version}" / "A1.py", "r") as f: - assert f.read().strip() == _expected_A1_settings_api_output - with open(codegen_outdir / "solver" / f"settings_{version}" / "C1.py", "r") as f: - assert f.read().strip() == _expected_C1_settings_api_output - with open(codegen_outdir / "solver" / f"settings_{version}" / "G1.py", "r") as f: - assert f.read().strip() == _expected_G1_settings_api_output - with open(codegen_outdir / "solver" / f"settings_{version}" / "N1.py", "r") as f: - assert f.read().strip() == _expected_N1_settings_api_output - with open( - codegen_outdir / "solver" / f"settings_{version}" / "N1_child.py", "r" - ) as f: - assert f.read().strip() == _expected_N1_child_settings_api_output - with open(codegen_outdir / "solver" / f"settings_{version}" / "P1.py", "r") as f: - assert f.read().strip() == _expected_P1_settings_api_output - with open(codegen_outdir / "solver" / f"settings_{version}" / "Q1.py", "r") as f: - assert f.read().strip() == _expected_Q1_settings_api_output - api_tree_file = get_api_tree_file_name(version) - with open(api_tree_file, "rb") as f: - api_tree = pickle.load(f) - settings_tree = { - "C1": "Command", - "G1": { - "C2": "Command", - "G2": {"P3": "Parameter"}, - "P2": "Parameter", - "Q2": "Query", - }, - "N1:": {"P4": "Parameter"}, - "P1": "Parameter", - "Q1": "Query", - } - api_tree_expected = {} - api_tree_expected[""] = {} - api_tree_expected[""] = settings_tree - assert api_tree == api_tree_expected - shutil.rmtree(str(codegen_outdir)) - - -def test_codegen_old_with_zipped_settings_static_info(monkeypatch): - monkeypatch.setenv("PYFLUENT_USE_OLD_SETTINGSGEN", "1") - codegen_outdir = Path(tempfile.mkdtemp()) - monkeypatch.setattr(pyfluent, "CODEGEN_OUTDIR", codegen_outdir) - monkeypatch.setattr(pyfluent, "CODEGEN_ZIP_SETTINGS", True) - version = "251" - static_infos = {} - static_infos[StaticInfoType.SETTINGS] = _settings_static_info - allapigen.generate(version, static_infos) - generated_paths = list(codegen_outdir.iterdir()) - assert len(generated_paths) == 2 - assert set(p.name for p in generated_paths) == { - f"api_tree_{version}.pickle", - "solver", - } - solver_paths = list((codegen_outdir / "solver").iterdir()) - assert len(solver_paths) == 1 - assert set(p.name for p in solver_paths) == {f"settings_{version}.zip"} - shutil.rmtree(str(codegen_outdir)) - - _expected_settings_api_output = '''# # This is an auto-generated file. DO NOT EDIT! #