From 88a44b383afd80d4090bb15c3ad823638b5f9c20 Mon Sep 17 00:00:00 2001 From: PProfizi Date: Wed, 24 Sep 2025 10:21:30 +0200 Subject: [PATCH 01/19] Add "operator_doc" dependency group --- pyproject.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 7ba8653cb7f..3c250842131 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,6 +60,11 @@ plotting = [ "imageio-ffmpeg", ] +operator_doc = [ + # Operator documentation generation + "jinja2" +] + [project.urls] Homepage = "https://dpf.docs.pyansys.com/" Documentation = "https://dpf.docs.pyansys.com/" From ca19bb759cb545f131b98cc7711f1efdf89f3fc0 Mon Sep 17 00:00:00 2001 From: PProfizi Date: Wed, 24 Sep 2025 15:22:19 +0200 Subject: [PATCH 02/19] Add "operator_doc" dependency group --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3c250842131..bd98f9eb8ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,7 +62,7 @@ plotting = [ operator_doc = [ # Operator documentation generation - "jinja2" + "jinja2", ] [project.urls] From 8986d9ea76ed31630a8b3b56462cfdc22afe41be Mon Sep 17 00:00:00 2001 From: PProfizi Date: Wed, 24 Sep 2025 15:22:52 +0200 Subject: [PATCH 03/19] Add "output_path" argument --- .ci/generate_operators_doc.py | 38 ++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/.ci/generate_operators_doc.py b/.ci/generate_operators_doc.py index 7ad34ec33ca..d7b3fc6f0bc 100644 --- a/.ci/generate_operators_doc.py +++ b/.ci/generate_operators_doc.py @@ -138,7 +138,7 @@ def get_plugin_operators(server, plugin_name): return plugin_operators -def generate_operator_doc(server, operator_name, include_private): +def generate_operator_doc(server, operator_name, include_private, output_path): operator_info = fetch_doc_info(server, operator_name) scripting_name = operator_info["scripting_info"]["scripting_name"] category = operator_info["scripting_info"]["category"] @@ -153,12 +153,14 @@ def generate_operator_doc(server, operator_name, include_private): script_path = Path(__file__) root_dir = script_path.parent.parent template_dir = Path(root_dir) / "doc" / "source" / "operators_doc" / "operator-specifications" - category_dir = Path(template_dir) / category + spec_folder = Path(output_path) / "operator-specifications" + category_dir = spec_folder / category + spec_folder.mkdir(parents=True, exist_ok=True) if category is not None: category_dir.mkdir(parents=True, exist_ok=True) # Ensure all parent directories are created file_dir = category_dir else: - file_dir = template_dir + file_dir = Path(output_path) / "operator-specifications" with Path.open(Path(template_dir) / "operator_doc_template.md", "r") as file: template = Template(file.read()) @@ -168,10 +170,9 @@ def generate_operator_doc(server, operator_name, include_private): def generate_toc_tree(docs_path): - # Target the operator-specifications folder for iteration - # operator_specs_path = docs_path / "operator-specifications" data = [] - for folder in docs_path.iterdir(): + specs_path = docs_path / "operator-specifications" + for folder in specs_path.iterdir(): if folder.is_dir(): # Ensure 'folder' is a directory category = folder.name operators = [] # Reset operators for each category @@ -186,7 +187,10 @@ def generate_toc_tree(docs_path): data.append({"category": category, "operators": operators}) # Render the Jinja2 template - template_path = docs_path / "toc_template.j2" + script_path = Path(__file__) + root_dir = script_path.parent.parent + template_dir = Path(root_dir) / "doc" / "source" / "operators_doc" / "operator-specifications" + template_path = template_dir / "toc_template.j2" with Path.open(template_path, "r") as template_file: template = Template(template_file.read()) output = template.render(data=data) # Pass 'data' as a named argument @@ -203,6 +207,7 @@ def main(): parser.add_argument( "--ansys_path", default=None, help="Path to Ansys DPF Server installation directory" ) + parser.add_argument("--output_path", default=None, help="Path to output directory") parser.add_argument("--include_private", action="store_true", help="Include private operators") parser.add_argument( "--include_composites", action="store_true", help="Include composites operators" @@ -210,6 +215,11 @@ def main(): parser.add_argument("--include_sound", action="store_true", help="Include sound operators") args = parser.parse_args() desired_plugin = args.plugin + output_path = ( + args.output_path + if args.output_path + else (Path(__file__).parent.parent / "doc" / "source" / "operators_doc") + ) server = initialize_server(args.ansys_path, args.include_composites, args.include_sound) if desired_plugin is None: @@ -217,17 +227,9 @@ def main(): else: operators = get_plugin_operators(server, desired_plugin) for operator_name in operators: - generate_operator_doc(server, operator_name, args.include_private) - - docs_path = ( - Path(__file__).parent.parent - / "doc" - / "source" - / "operators_doc" - / "operator-specifications" - ) - print(docs_path) - generate_toc_tree(docs_path) + generate_operator_doc(server, operator_name, args.include_private, output_path) + print(output_path) + generate_toc_tree(Path(output_path)) if __name__ == "__main__": From f5cf73b545987948144a0e3176e69afc2848e681 Mon Sep 17 00:00:00 2001 From: PProfizi Date: Fri, 26 Sep 2025 14:16:59 +0200 Subject: [PATCH 04/19] Fix typehint for server type in server.py --- src/ansys/dpf/core/__init__.py | 1 + src/ansys/dpf/core/server.py | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ansys/dpf/core/__init__.py b/src/ansys/dpf/core/__init__.py index b33f850a3a2..be4184c1723 100644 --- a/src/ansys/dpf/core/__init__.py +++ b/src/ansys/dpf/core/__init__.py @@ -99,6 +99,7 @@ AvailableServerContexts, LicenseContextManager ) +from ansys.dpf.core.server_types import AnyServerType from ansys.dpf.core.unit_system import UnitSystem, unit_systems from ansys.dpf.core.incremental import IncrementalHelper, split_workflow_in_chunks from ansys.dpf.core.any import Any diff --git a/src/ansys/dpf/core/server.py b/src/ansys/dpf/core/server.py index 68a1d38528d..ad4d86013b6 100644 --- a/src/ansys/dpf/core/server.py +++ b/src/ansys/dpf/core/server.py @@ -48,7 +48,7 @@ ServerConfig, ServerFactory, ) -from ansys.dpf.core.server_types import DPF_DEFAULT_PORT, LOCALHOST, RUNNING_DOCKER, BaseServer +from ansys.dpf.core.server_types import DPF_DEFAULT_PORT, LOCALHOST, RUNNING_DOCKER, AnyServerType def shutdown_global_server(): @@ -73,7 +73,7 @@ def has_local_server(): return dpf.core.SERVER is not None -def _global_server() -> BaseServer: +def _global_server() -> AnyServerType: """Retrieve the global server if it exists. If the global server has not been specified, check the expected server type in @@ -162,7 +162,7 @@ def start_local_server( config=None, use_pypim_by_default=True, context=None, -) -> BaseServer: +) -> AnyServerType: """Start a new local DPF server at a given port and IP address. This method requires Windows and ANSYS 2021 R1 or later. If ``as_global=True``, which is @@ -399,12 +399,12 @@ def connect(): raise e -def get_or_create_server(server: BaseServer | None) -> Union[BaseServer, None]: +def get_or_create_server(server: AnyServerType | None) -> Union[AnyServerType, None]: """Return the given server or if None, creates a new one. Parameters ---------- - server: BaseServer, None + server: Returns ------- From 51c6bce1bbe75fd468dbcd3d9a938112f5dc507a Mon Sep 17 00:00:00 2001 From: PProfizi Date: Fri, 26 Sep 2025 14:46:51 +0200 Subject: [PATCH 05/19] Create ansys.dpf.core.documentation module --- src/ansys/dpf/core/documentation/__init__.py | 22 ++ .../documentation}/generate_operators_doc.py | 234 ++++++++++++++---- .../documentation}/operator_doc_template.md | 0 .../dpf/core/documentation}/toc_template.j2 | 0 4 files changed, 208 insertions(+), 48 deletions(-) create mode 100644 src/ansys/dpf/core/documentation/__init__.py rename {.ci => src/ansys/dpf/core/documentation}/generate_operators_doc.py (52%) rename {doc/source/operators_doc/operator-specifications => src/ansys/dpf/core/documentation}/operator_doc_template.md (100%) rename {doc/source/operators_doc/operator-specifications => src/ansys/dpf/core/documentation}/toc_template.j2 (100%) diff --git a/src/ansys/dpf/core/documentation/__init__.py b/src/ansys/dpf/core/documentation/__init__.py new file mode 100644 index 00000000000..6aa139810bb --- /dev/null +++ b/src/ansys/dpf/core/documentation/__init__.py @@ -0,0 +1,22 @@ +# Copyright (C) 2020 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Documentation generation tools.""" diff --git a/.ci/generate_operators_doc.py b/src/ansys/dpf/core/documentation/generate_operators_doc.py similarity index 52% rename from .ci/generate_operators_doc.py rename to src/ansys/dpf/core/documentation/generate_operators_doc.py index d7b3fc6f0bc..a8048566aa6 100644 --- a/.ci/generate_operators_doc.py +++ b/src/ansys/dpf/core/documentation/generate_operators_doc.py @@ -1,15 +1,75 @@ +# Copyright (C) 2020 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Generation of Markdown documentation source files for operators of a given DPF installation.""" + +from __future__ import annotations + import argparse +from os import PathLike from pathlib import Path -from jinja2 import Template - from ansys.dpf import core as dpf from ansys.dpf.core.changelog import Changelog from ansys.dpf.core.core import load_library from ansys.dpf.core.dpf_operator import available_operator_names -def initialize_server(ansys_path=None, include_composites=False, include_sound=False): +class Jinja2ImportError(ModuleNotFoundError): + """Error raised when Jinja2 could not be imported during operator documentation generation.""" + + def __init__( + self, + msg="To generate Markdown documentation of operators, please install jinja2 with:\n" + "pip install jinja2", + ): + ModuleNotFoundError.__init__(self, msg) + + +try: + import jinja2 +except ModuleNotFoundError: + raise Jinja2ImportError + + +def initialize_server( + ansys_path: str | PathLike = None, include_composites: bool = False, include_sound: bool = False +) -> dpf.AnyServerType: + """Initialize a DPF server for a given installation folder by loading required plugins. + + Parameters + ---------- + ansys_path: + Path to the DPF installation to use to start a server. + include_composites: + Whether to generate documentation for operators of the Composites plugin. + include_sound: + Whether to generate documentation for operators of the Sound DPF plugin. + + Returns + ------- + server: + A running DPF server to generate operator documentation for. + + """ server = dpf.start_local_server(ansys_path=ansys_path) print(server.plugins) print(f"Ansys Path: {server.ansys_path}") @@ -37,20 +97,35 @@ def initialize_server(ansys_path=None, include_composites=False, include_sound=F return server -def fetch_doc_info(server, operator_name): +def fetch_doc_info(server: dpf.AnyServerType, operator_name: str) -> dict: + """Fetch information about the specifications of a given operator. + + Parameters + ---------- + server: + A DPF server to query the specifications of the operator. + operator_name: + The name of the operator of interest. + + Returns + ------- + doc_info: + Information about the operator structured for use with the documentation template. + + """ spec = dpf.Operator.operator_specification(op_name=operator_name, server=server) input_info = [] output_info = [] configurations_info = [] for input_pin in spec.inputs: - input = spec.inputs[input_pin] + input_pin_info = spec.inputs[input_pin] input_info.append( { "pin_number": input_pin, - "name": input.name, - "types": [str(t) for t in input._type_names], - "document": input.document, - "optional": input.optional, + "name": input_pin_info.name, + "types": [str(t) for t in input_pin_info._type_names], + "document": input_pin_info.document, + "optional": input_pin_info.optional, } ) for output_pin in spec.outputs: @@ -103,7 +178,7 @@ def fetch_doc_info(server, operator_name): if category: op_friendly_name = category + ":" + op_friendly_name - license = properties.pop("license", "None") + license_type = properties.pop("license", "None") exposure = properties.pop("exposure", "private") scripting_info = { @@ -112,7 +187,7 @@ def fetch_doc_info(server, operator_name): "scripting_name": scripting_name, "full_name": full_name, "internal_name": operator_name, - "license": license, + "license": license_type, "version": str(last_version), # Include last version in scripting_info "changelog": changelog_entries, # Include all changelog entries } @@ -128,7 +203,22 @@ def fetch_doc_info(server, operator_name): } -def get_plugin_operators(server, plugin_name): +def get_plugin_operators(server: dpf.AnyServerType, plugin_name: str) -> list[str]: + """Get the list of operators for a given plugin. + + Parameters + ---------- + server: + DPF server to query for the list of operators. + plugin_name: + Name of the plugin of interest. + + Returns + ------- + operator_list: + List of names of operators available on the server for the given plugin. + + """ operators = available_operator_names(server) plugin_operators = [] for operator_name in operators: @@ -138,7 +228,23 @@ def get_plugin_operators(server, plugin_name): return plugin_operators -def generate_operator_doc(server, operator_name, include_private, output_path): +def generate_operator_doc( + server: dpf.AnyServerType, operator_name: str, include_private: bool, output_path: Path +): + """Write the Markdown documentation page for a given operator on a given DPF server. + + Parameters + ---------- + server: + DPF server of interest. + operator_name: + Name of the operator of interest. + include_private: + Whether to generate the documentation if the operator is private. + output_path: + Path to write the operator documentation at. + + """ operator_info = fetch_doc_info(server, operator_name) scripting_name = operator_info["scripting_info"]["scripting_name"] category = operator_info["scripting_info"]["category"] @@ -150,26 +256,32 @@ def generate_operator_doc(server, operator_name, include_private, output_path): file_name = file_name.replace("::", "_") if not include_private and operator_info["exposure"] == "private": return - script_path = Path(__file__) - root_dir = script_path.parent.parent - template_dir = Path(root_dir) / "doc" / "source" / "operators_doc" / "operator-specifications" - spec_folder = Path(output_path) / "operator-specifications" + template_path = Path(__file__).parent / "operator_doc_template.md" + spec_folder = output_path / "operator-specifications" category_dir = spec_folder / category spec_folder.mkdir(parents=True, exist_ok=True) if category is not None: category_dir.mkdir(parents=True, exist_ok=True) # Ensure all parent directories are created file_dir = category_dir else: - file_dir = Path(output_path) / "operator-specifications" - with Path.open(Path(template_dir) / "operator_doc_template.md", "r") as file: - template = Template(file.read()) + file_dir = output_path / "operator-specifications" + with Path.open(template_path, "r") as file: + template = jinja2.Template(file.read()) output = template.render(operator_info) with Path.open(Path(file_dir) / f"{file_name}.md", "w") as file: file.write(output) -def generate_toc_tree(docs_path): +def generate_toc_tree(docs_path: Path): + """Write the global toc.yml file for the DPF documentation based on the operators found. + + Parameters + ---------- + docs_path: + Path to the root of the DPF documentation sources. + + """ data = [] specs_path = docs_path / "operator-specifications" for folder in specs_path.iterdir(): @@ -187,50 +299,76 @@ def generate_toc_tree(docs_path): data.append({"category": category, "operators": operators}) # Render the Jinja2 template - script_path = Path(__file__) - root_dir = script_path.parent.parent - template_dir = Path(root_dir) / "doc" / "source" / "operators_doc" / "operator-specifications" - template_path = template_dir / "toc_template.j2" + template_path = Path(__file__).parent / "toc_template.j2" with Path.open(template_path, "r") as template_file: - template = Template(template_file.read()) + template = jinja2.Template(template_file.read()) output = template.render(data=data) # Pass 'data' as a named argument # Write the rendered output to toc.yml at the operators_doc level - # toc_path = docs_path / "toc.yml" with Path.open(docs_path / "toc.yml", "w") as file: file.write(output) -def main(): +def generate_operators_doc( + ansys_path: Path, + output_path: Path, + include_composites: bool = False, + include_sound: bool = False, + include_private: bool = False, + desired_plugin: str = None, +): + """Generate the Markdown source files for the DPF operator documentation. + + This function generates a Markdown file for each operator found in a given DPF installation, + categorized in folders per operator category, as well as a `toc.yml` file. + These are used to generate the DPF html documentation website as seen on the Developer Portal. + + Parameters + ---------- + ansys_path: + Path to an Ansys/DPF installation. + output_path: + Path to write the output files at. + include_composites: + Whether to include operators of the Composites plugin. + include_sound: + Whether to include operators of the Sound plugin. + include_private: + Whether to include private operators. + desired_plugin: + Restrict documentation generation to the operators of this specific plugin. + + """ + server = initialize_server(ansys_path, include_composites, include_sound) + if desired_plugin is None: + operators = available_operator_names(server) + else: + operators = get_plugin_operators(server, desired_plugin) + for operator_name in operators: + generate_operator_doc(server, operator_name, include_private, output_path) + generate_toc_tree(output_path) + + +if __name__ == "__main__": parser = argparse.ArgumentParser(description="Fetch available operators") parser.add_argument("--plugin", help="Filter operators by plugin") parser.add_argument( "--ansys_path", default=None, help="Path to Ansys DPF Server installation directory" ) - parser.add_argument("--output_path", default=None, help="Path to output directory") + parser.add_argument( + "--output_path", default=None, help="Path to output directory", required=True + ) parser.add_argument("--include_private", action="store_true", help="Include private operators") parser.add_argument( "--include_composites", action="store_true", help="Include composites operators" ) parser.add_argument("--include_sound", action="store_true", help="Include sound operators") args = parser.parse_args() - desired_plugin = args.plugin - output_path = ( - args.output_path - if args.output_path - else (Path(__file__).parent.parent / "doc" / "source" / "operators_doc") - ) - server = initialize_server(args.ansys_path, args.include_composites, args.include_sound) - if desired_plugin is None: - operators = available_operator_names(server) - else: - operators = get_plugin_operators(server, desired_plugin) - for operator_name in operators: - generate_operator_doc(server, operator_name, args.include_private, output_path) - print(output_path) - generate_toc_tree(Path(output_path)) - - -if __name__ == "__main__": - main() + generate_operators_doc( + ansys_path=args.ansys_path, + output_path=args.output_path, + include_composites=args.include_composites, + include_sound=args.include_sound, + include_private=args.include_private, + ) diff --git a/doc/source/operators_doc/operator-specifications/operator_doc_template.md b/src/ansys/dpf/core/documentation/operator_doc_template.md similarity index 100% rename from doc/source/operators_doc/operator-specifications/operator_doc_template.md rename to src/ansys/dpf/core/documentation/operator_doc_template.md diff --git a/doc/source/operators_doc/operator-specifications/toc_template.j2 b/src/ansys/dpf/core/documentation/toc_template.j2 similarity index 100% rename from doc/source/operators_doc/operator-specifications/toc_template.j2 rename to src/ansys/dpf/core/documentation/toc_template.j2 From 7a2687643d82ff4c4854dbdf6fc9e9a9707a656f Mon Sep 17 00:00:00 2001 From: PProfizi Date: Fri, 26 Sep 2025 14:47:56 +0200 Subject: [PATCH 06/19] Add dpf_generate_operators_doc as an entry-point to the library --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index bd98f9eb8ab..7a6fce32c51 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,6 +71,9 @@ Documentation = "https://dpf.docs.pyansys.com/" Source = "https://github.com/ansys/pydpf-core" Tracker = "https://github.com/ansys/pydpf-core/issues" +[project.scripts] +dpf_generate_operators_doc = "ansys.dpf.core.documentation.generate_operators_doc:generate_operators_doc" + [tool.ruff] line-length = 100 From de8b9beec0a39d26f8a5f5369297d0adc83b1d05 Mon Sep 17 00:00:00 2001 From: PProfizi Date: Fri, 26 Sep 2025 14:59:10 +0200 Subject: [PATCH 07/19] Fix dpf_generate_operators_doc entry_point --- pyproject.toml | 2 +- .../documentation/generate_operators_doc.py | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7a6fce32c51..946b7886131 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,7 +72,7 @@ Source = "https://github.com/ansys/pydpf-core" Tracker = "https://github.com/ansys/pydpf-core/issues" [project.scripts] -dpf_generate_operators_doc = "ansys.dpf.core.documentation.generate_operators_doc:generate_operators_doc" +dpf_generate_operators_doc = "ansys.dpf.core.documentation.generate_operators_doc:run_with_args" [tool.ruff] line-length = 100 diff --git a/src/ansys/dpf/core/documentation/generate_operators_doc.py b/src/ansys/dpf/core/documentation/generate_operators_doc.py index a8048566aa6..7c96efc7082 100644 --- a/src/ansys/dpf/core/documentation/generate_operators_doc.py +++ b/src/ansys/dpf/core/documentation/generate_operators_doc.py @@ -349,9 +349,11 @@ def generate_operators_doc( generate_toc_tree(output_path) -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Fetch available operators") - parser.add_argument("--plugin", help="Filter operators by plugin") +def run_with_args(): + """Run generate_operators_doc from the command line with argument parsing.""" + parser = argparse.ArgumentParser( + description="Generate the operator documentation sources for operators of a given DPF installation." + ) parser.add_argument( "--ansys_path", default=None, help="Path to Ansys DPF Server installation directory" ) @@ -360,9 +362,10 @@ def generate_operators_doc( ) parser.add_argument("--include_private", action="store_true", help="Include private operators") parser.add_argument( - "--include_composites", action="store_true", help="Include composites operators" + "--include_composites", action="store_true", help="Include Composites operators" ) - parser.add_argument("--include_sound", action="store_true", help="Include sound operators") + parser.add_argument("--include_sound", action="store_true", help="Include Sound operators") + parser.add_argument("--plugin", help="Restrict to the given plugin.") args = parser.parse_args() generate_operators_doc( @@ -371,4 +374,9 @@ def generate_operators_doc( include_composites=args.include_composites, include_sound=args.include_sound, include_private=args.include_private, + desired_plugin=args.plugin, ) + + +if __name__ == "__main__": + run_with_args() From 759b9de0f2350f16ecf3ec229920af396bc4b846 Mon Sep 17 00:00:00 2001 From: PProfizi Date: Fri, 26 Sep 2025 15:31:44 +0200 Subject: [PATCH 08/19] Package operator doc templates --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 946b7886131..a33cf9b1e2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -123,4 +123,5 @@ where = ["src"] [tool.setuptools.package-data] "ansys.dpf.gatebin" = ["*.so", "*.dll"] +"ansys.dpf.core.documentation" = ["toc_template.j2", "operator_doc_template.md"] From 38eb6f9f57b17d1869427905e777aef0123c2613 Mon Sep 17 00:00:00 2001 From: PProfizi Date: Fri, 26 Sep 2025 15:42:24 +0200 Subject: [PATCH 09/19] Support servers running on Linux --- .../core/documentation/generate_operators_doc.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/ansys/dpf/core/documentation/generate_operators_doc.py b/src/ansys/dpf/core/documentation/generate_operators_doc.py index 7c96efc7082..6a9c2e83810 100644 --- a/src/ansys/dpf/core/documentation/generate_operators_doc.py +++ b/src/ansys/dpf/core/documentation/generate_operators_doc.py @@ -79,15 +79,15 @@ def initialize_server( print(f"Server version: {dpf.global_server().version}") if include_composites: print("Loading Composites Plugin") + if server.os == "nt": + binary_name = "composite_operators.dll" + else: + binary_name = "libcomposite_operators.so" load_library( - filename=Path(server.ansys_path) - / "dpf" - / "plugins" - / "dpf_composites" - / "composite_operators.dll", + filename=Path(server.ansys_path) / "dpf" / "plugins" / "dpf_composites" / binary_name, name="composites", ) - if include_sound: + if include_sound and server.os == "nt": print("Loading Acoustics Plugin") load_library( filename=Path(server.ansys_path) / "Acoustics" / "SAS" / "ads" / "dpf_sound.dll", @@ -364,7 +364,9 @@ def run_with_args(): parser.add_argument( "--include_composites", action="store_true", help="Include Composites operators" ) - parser.add_argument("--include_sound", action="store_true", help="Include Sound operators") + parser.add_argument( + "--include_sound", action="store_true", help="Include Sound operators (Windows only)" + ) parser.add_argument("--plugin", help="Restrict to the given plugin.") args = parser.parse_args() From 8a38248a49f36922285bb950583b4614c9c7eb37 Mon Sep 17 00:00:00 2001 From: PProfizi Date: Fri, 26 Sep 2025 16:06:37 +0200 Subject: [PATCH 10/19] Add verbosity option --- .../documentation/generate_operators_doc.py | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/ansys/dpf/core/documentation/generate_operators_doc.py b/src/ansys/dpf/core/documentation/generate_operators_doc.py index 6a9c2e83810..3df00e49659 100644 --- a/src/ansys/dpf/core/documentation/generate_operators_doc.py +++ b/src/ansys/dpf/core/documentation/generate_operators_doc.py @@ -51,7 +51,10 @@ def __init__( def initialize_server( - ansys_path: str | PathLike = None, include_composites: bool = False, include_sound: bool = False + ansys_path: str | PathLike = None, + include_composites: bool = False, + include_sound: bool = False, + verbose: bool = False, ) -> dpf.AnyServerType: """Initialize a DPF server for a given installation folder by loading required plugins. @@ -63,6 +66,8 @@ def initialize_server( Whether to generate documentation for operators of the Composites plugin. include_sound: Whether to generate documentation for operators of the Sound DPF plugin. + verbose: + Whether to print progress information. Returns ------- @@ -71,14 +76,15 @@ def initialize_server( """ server = dpf.start_local_server(ansys_path=ansys_path) - print(server.plugins) - print(f"Ansys Path: {server.ansys_path}") - print(f"Server Info: {server.info}") - print(f"Server Context: {server.context}") - print(f"Server Config: {server.config}") - print(f"Server version: {dpf.global_server().version}") + if verbose: + print(f"Ansys Path: {server.ansys_path}") + print(f"Server Info: {server.info}") + print(f"Server Context: {server.context}") + print(f"Server Config: {server.config}") + print(f"Server version: {dpf.global_server().version}") if include_composites: - print("Loading Composites Plugin") + if verbose: + print("Loading Composites Plugin") if server.os == "nt": binary_name = "composite_operators.dll" else: @@ -88,12 +94,14 @@ def initialize_server( name="composites", ) if include_sound and server.os == "nt": - print("Loading Acoustics Plugin") + if verbose: + print("Loading Acoustics Plugin") load_library( filename=Path(server.ansys_path) / "Acoustics" / "SAS" / "ads" / "dpf_sound.dll", name="sound", ) - print(f"Loaded plugins: {list(server.plugins.keys())}") + if verbose: + print(f"Loaded plugins: {list(server.plugins.keys())}") return server @@ -316,6 +324,7 @@ def generate_operators_doc( include_sound: bool = False, include_private: bool = False, desired_plugin: str = None, + verbose: bool = True, ): """Generate the Markdown source files for the DPF operator documentation. @@ -337,9 +346,11 @@ def generate_operators_doc( Whether to include private operators. desired_plugin: Restrict documentation generation to the operators of this specific plugin. + verbose: + Whether to print progress information. """ - server = initialize_server(ansys_path, include_composites, include_sound) + server = initialize_server(ansys_path, include_composites, include_sound, verbose) if desired_plugin is None: operators = available_operator_names(server) else: @@ -368,6 +379,13 @@ def run_with_args(): "--include_sound", action="store_true", help="Include Sound operators (Windows only)" ) parser.add_argument("--plugin", help="Restrict to the given plugin.") + parser.add_argument( + "-v", + "--verbose", + action="store_true", + default=True, + help="Print script progress information.", + ) args = parser.parse_args() generate_operators_doc( From 819f9c33795c0bae2a1dce070ab80f45f6b5c8d2 Mon Sep 17 00:00:00 2001 From: PProfizi Date: Fri, 26 Sep 2025 16:07:51 +0200 Subject: [PATCH 11/19] Add testing --- tests/test_documentation.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/test_documentation.py diff --git a/tests/test_documentation.py b/tests/test_documentation.py new file mode 100644 index 00000000000..a20e3b70421 --- /dev/null +++ b/tests/test_documentation.py @@ -0,0 +1,32 @@ +# Copyright (C) 2020 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from pathlib import Path + +import ansys.dpf.core as dpf +from ansys.dpf.core.documentation.generate_operators_doc import generate_operators_doc + + +def test_generate_operators_doc(tmp_path: Path): + generate_operators_doc(ansys_path=dpf.SERVER.ansys_path, output_path=tmp_path, verbose=False) + file_to_test = tmp_path / "toc.yml" + assert file_to_test.exists() From f8a30372e14fb6b772212fdcb7dcf2b341fae908 Mon Sep 17 00:00:00 2001 From: PProfizi Date: Fri, 26 Sep 2025 18:55:03 +0200 Subject: [PATCH 12/19] Add jinja2 to requirements_test.txt --- requirements/requirements_test.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements/requirements_test.txt b/requirements/requirements_test.txt index a8193452a45..cdf07ca69b4 100644 --- a/requirements/requirements_test.txt +++ b/requirements/requirements_test.txt @@ -3,6 +3,7 @@ coverage==7.10.6 graphviz==0.21 imageio==2.37.0 imageio-ffmpeg==0.6.0 +jinja2==3.1.6 pypandoc_binary==1.15 pytest==8.4.2 pytest-cov==6.2.1 From cd19ee70100625a742f940e24f2e52cbbf25fcaa Mon Sep 17 00:00:00 2001 From: PProfizi Date: Fri, 26 Sep 2025 19:12:37 +0200 Subject: [PATCH 13/19] Ignore F401 --- src/ansys/dpf/core/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansys/dpf/core/__init__.py b/src/ansys/dpf/core/__init__.py index be4184c1723..08a256525f6 100644 --- a/src/ansys/dpf/core/__init__.py +++ b/src/ansys/dpf/core/__init__.py @@ -99,7 +99,7 @@ AvailableServerContexts, LicenseContextManager ) -from ansys.dpf.core.server_types import AnyServerType +from ansys.dpf.core.server_types import AnyServerType # noqa: F401 from ansys.dpf.core.unit_system import UnitSystem, unit_systems from ansys.dpf.core.incremental import IncrementalHelper, split_workflow_in_chunks from ansys.dpf.core.any import Any From 406f2eae8fde4db21e4da4befb680c81678227f0 Mon Sep 17 00:00:00 2001 From: PProfizi Date: Fri, 26 Sep 2025 20:43:52 +0200 Subject: [PATCH 14/19] Improve coverage --- .../core/documentation/generate_operators_doc.py | 16 ++++++++-------- tests/test_documentation.py | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/ansys/dpf/core/documentation/generate_operators_doc.py b/src/ansys/dpf/core/documentation/generate_operators_doc.py index 3df00e49659..f35d3f28345 100644 --- a/src/ansys/dpf/core/documentation/generate_operators_doc.py +++ b/src/ansys/dpf/core/documentation/generate_operators_doc.py @@ -33,7 +33,7 @@ from ansys.dpf.core.dpf_operator import available_operator_names -class Jinja2ImportError(ModuleNotFoundError): +class Jinja2ImportError(ModuleNotFoundError): # pragma: nocover """Error raised when Jinja2 could not be imported during operator documentation generation.""" def __init__( @@ -46,7 +46,7 @@ def __init__( try: import jinja2 -except ModuleNotFoundError: +except ModuleNotFoundError: # pragma: nocover raise Jinja2ImportError @@ -76,13 +76,13 @@ def initialize_server( """ server = dpf.start_local_server(ansys_path=ansys_path) - if verbose: + if verbose: # pragma: nocover print(f"Ansys Path: {server.ansys_path}") print(f"Server Info: {server.info}") print(f"Server Context: {server.context}") print(f"Server Config: {server.config}") print(f"Server version: {dpf.global_server().version}") - if include_composites: + if include_composites: # pragma: nocover if verbose: print("Loading Composites Plugin") if server.os == "nt": @@ -93,14 +93,14 @@ def initialize_server( filename=Path(server.ansys_path) / "dpf" / "plugins" / "dpf_composites" / binary_name, name="composites", ) - if include_sound and server.os == "nt": + if include_sound and server.os == "nt": # pragma: nocover if verbose: print("Loading Acoustics Plugin") load_library( filename=Path(server.ansys_path) / "Acoustics" / "SAS" / "ads" / "dpf_sound.dll", name="sound", ) - if verbose: + if verbose: # pragma: nocover print(f"Loaded plugins: {list(server.plugins.keys())}") return server @@ -360,7 +360,7 @@ def generate_operators_doc( generate_toc_tree(output_path) -def run_with_args(): +def run_with_args(): # pragma: nocover """Run generate_operators_doc from the command line with argument parsing.""" parser = argparse.ArgumentParser( description="Generate the operator documentation sources for operators of a given DPF installation." @@ -398,5 +398,5 @@ def run_with_args(): ) -if __name__ == "__main__": +if __name__ == "__main__": # pragma: nocover run_with_args() diff --git a/tests/test_documentation.py b/tests/test_documentation.py index a20e3b70421..286b766223b 100644 --- a/tests/test_documentation.py +++ b/tests/test_documentation.py @@ -30,3 +30,18 @@ def test_generate_operators_doc(tmp_path: Path): generate_operators_doc(ansys_path=dpf.SERVER.ansys_path, output_path=tmp_path, verbose=False) file_to_test = tmp_path / "toc.yml" assert file_to_test.exists() + file_to_test = tmp_path / "operator-specifications" / "utility" / "forward.md" + assert file_to_test.exists() + + +def test_generate_operators_doc_plugin(tmp_path: Path): + generate_operators_doc( + ansys_path=dpf.SERVER.ansys_path, + output_path=tmp_path, + verbose=False, + desired_plugin="vtk", + ) + file_to_test = tmp_path / "toc.yml" + assert file_to_test.exists() + file_to_test = tmp_path / "operator-specifications" / "serialization" / "vtu_export.md" + assert file_to_test.exists() From fee7b344bbde46f072c4e60a683a3b43e84313b0 Mon Sep 17 00:00:00 2001 From: Paul Profizi <100710998+PProfizi@users.noreply.github.com> Date: Mon, 29 Sep 2025 09:23:06 +0200 Subject: [PATCH 15/19] Switch tested plugin in test_generate_operators_doc_plugin for retro --- tests/test_documentation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_documentation.py b/tests/test_documentation.py index 286b766223b..7b42017b3ee 100644 --- a/tests/test_documentation.py +++ b/tests/test_documentation.py @@ -39,9 +39,9 @@ def test_generate_operators_doc_plugin(tmp_path: Path): ansys_path=dpf.SERVER.ansys_path, output_path=tmp_path, verbose=False, - desired_plugin="vtk", + desired_plugin="core", ) file_to_test = tmp_path / "toc.yml" assert file_to_test.exists() - file_to_test = tmp_path / "operator-specifications" / "serialization" / "vtu_export.md" + file_to_test = tmp_path / "operator-specifications" / "utility" / "forward.md" assert file_to_test.exists() From b6ba84b3732eb5ddbf652ac7946b4b134127fdaf Mon Sep 17 00:00:00 2001 From: PProfizi Date: Tue, 30 Sep 2025 11:19:00 +0200 Subject: [PATCH 16/19] Separate documentation tests in tox.ini --- tox.ini | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tox.ini b/tox.ini index 96aa9c5663d..8740869eb52 100644 --- a/tox.ini +++ b/tox.ini @@ -95,14 +95,14 @@ commands_pre = commands = python -c "\ import os, shutil; \ - test_data=['test_launcher','test_server','test_local_server','test_multi_server','test_workflow','test_remote_workflow','test_remote_operator','test_service','test_custom_type_field']; \ + test_data=['test_documentation', 'test_launcher','test_server','test_local_server','test_multi_server','test_workflow','test_remote_workflow','test_remote_operator','test_service','test_custom_type_field']; \ [(os.makedirs(d, exist_ok=True), shutil.copy('tests/conftest.py', d), shutil.copy(f'tests/\{d}.py', d) if os.path.exists(f'tests/\{d}.py') else None) for d in test_data]; \ [os.remove(f'tests/\{d}.py') for d in test_data if os.path.exists(f'tests/\{d}.py')]" [testenv:posttest] description = Environment to revert test files to original state after testing -depends = pretest, test-{api,launcher,server,local_server,multi_server,remote_workflow,remote_operator,workflow,service,api_entry,custom_type_field,operators} +depends = pretest, test-{api,documentation,launcher,server,local_server,multi_server,remote_workflow,remote_operator,workflow,service,api_entry,custom_type_field,operators} skip_install = True @@ -113,14 +113,15 @@ commands_pre = commands = python -c "\ import os, shutil; \ - test_data=['test_launcher','test_server','test_local_server','test_multi_server','test_workflow','test_remote_workflow','test_remote_operator','test_service', 'test_custom_type_field']; \ + test_data=['test_documentation','test_launcher','test_server','test_local_server','test_multi_server','test_workflow','test_remote_workflow','test_remote_operator','test_service', 'test_custom_type_field']; \ [shutil.move(f'\{d}/\{d}.py', f'tests/\{d}.py') for d in test_data if os.path.exists(f'\{d}/\{d}.py')]; \ [shutil.rmtree(d) for d in test_data if os.path.exists(d)]" -[testenv:test-{api,launcher,server,local_server,multi_server,remote_workflow,remote_operator,workflow,service,api_entry,custom_type_field,operators}] +[testenv:test-{api,documentation,launcher,server,local_server,multi_server,remote_workflow,remote_operator,workflow,service,api_entry,custom_type_field,operators}] description = Environment for running api: api tests + documentation: documentation tests launcher: launcher tests server: server tests local_server: local server tests @@ -154,6 +155,7 @@ setenv = api_entry: JUNITXML = --junitxml=tests/junit/test-results10.xml -o junit_family=legacy custom_type_field: JUNITXML = --junitxml=tests/junit/test-results11.xml -o junit_family=legacy operators: JUNITXML = --junitxml=tests/junit/test-results12.xml -o junit_family=legacy + documentation: JUNITXML = --junitxml=tests/junit/test-results13.xml -o junit_family=legacy # Tests sets api: PYTEST_PYTHON_FILES = tests @@ -168,6 +170,7 @@ setenv = api_entry: PYTEST_PYTHON_FILES = tests/entry custom_type_field: PYTEST_PYTHON_FILES = test_custom_type_field operators: PYTEST_PYTHON_FILES = tests/operators + documentation: PYTEST_PYTHON_FILES = test_documentation TEMP = {env_tmp_dir} TMP = {env_tmp_dir} From 51d10be6005af7481ab547ba93987a8a886ce221 Mon Sep 17 00:00:00 2001 From: PProfizi Date: Tue, 30 Sep 2025 11:19:50 +0200 Subject: [PATCH 17/19] Separate documentation tests in tox.ini --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 8740869eb52..0340671cbeb 100644 --- a/tox.ini +++ b/tox.ini @@ -29,10 +29,10 @@ [tox] description = Default tox environment list and core configurations -envlist = pretest,test-{api,launcher,server,local_server,multi_server,api_entry,custom_type_field,operators,workflow,remote_workflow,remote_operator,service},posttest,kill-servers +envlist = pretest,test-{api,documentation,launcher,server,local_server,multi_server,api_entry,custom_type_field,operators,workflow,remote_workflow,remote_operator,service},posttest,kill-servers labels = - localparalleltests = pretest,test-{api,launcher,server,local_server,multi_server,custom_type_field,operators},posttest,kill-servers + localparalleltests = pretest,test-{api,documentation,launcher,server,local_server,multi_server,custom_type_field,operators},posttest,kill-servers othertests = pretest,test-{workflow,remote_workflow,remote_operator,service},posttest,kill-servers ciparalleltests = test-{api,launcher,local_server,multi_server,custom_type_field,operators},kill-servers From 8236a045859ac2c4ab9e538d0b1d3ee80dc07090 Mon Sep 17 00:00:00 2001 From: PProfizi Date: Tue, 30 Sep 2025 11:20:49 +0200 Subject: [PATCH 18/19] Separate documentation tests in tox.ini --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 0340671cbeb..192885679a1 100644 --- a/tox.ini +++ b/tox.ini @@ -34,7 +34,7 @@ envlist = pretest,test-{api,documentation,launcher,server,local_server,multi_ser labels = localparalleltests = pretest,test-{api,documentation,launcher,server,local_server,multi_server,custom_type_field,operators},posttest,kill-servers othertests = pretest,test-{workflow,remote_workflow,remote_operator,service},posttest,kill-servers - ciparalleltests = test-{api,launcher,local_server,multi_server,custom_type_field,operators},kill-servers + ciparalleltests = test-{api,documentation,launcher,local_server,multi_server,custom_type_field,operators},kill-servers isolated_build_env = build @@ -95,7 +95,7 @@ commands_pre = commands = python -c "\ import os, shutil; \ - test_data=['test_documentation', 'test_launcher','test_server','test_local_server','test_multi_server','test_workflow','test_remote_workflow','test_remote_operator','test_service','test_custom_type_field']; \ + test_data=['test_documentation','test_launcher','test_server','test_local_server','test_multi_server','test_workflow','test_remote_workflow','test_remote_operator','test_service','test_custom_type_field']; \ [(os.makedirs(d, exist_ok=True), shutil.copy('tests/conftest.py', d), shutil.copy(f'tests/\{d}.py', d) if os.path.exists(f'tests/\{d}.py') else None) for d in test_data]; \ [os.remove(f'tests/\{d}.py') for d in test_data if os.path.exists(f'tests/\{d}.py')]" From 7202d26fe9b9b28f08f354f0c9939d4deaee2a16 Mon Sep 17 00:00:00 2001 From: PProfizi Date: Tue, 30 Sep 2025 14:48:54 +0200 Subject: [PATCH 19/19] Separate test_documentation tests on Docker --- .github/workflows/scripts/separate_long_core_tests.ps1 | 6 +++++- .github/workflows/test_docker.yml | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/scripts/separate_long_core_tests.ps1 b/.github/workflows/scripts/separate_long_core_tests.ps1 index d97dcd4199b..46ae1108179 100644 --- a/.github/workflows/scripts/separate_long_core_tests.ps1 +++ b/.github/workflows/scripts/separate_long_core_tests.ps1 @@ -7,6 +7,7 @@ New-Item -Path ".\" -Name "test_remote_workflow" -ItemType "directory" New-Item -Path ".\" -Name "test_remote_operator" -ItemType "directory" New-Item -Path ".\" -Name "test_service" -ItemType "directory" New-Item -Path ".\" -Name "test_custom_type_field" -ItemType "directory" +New-Item -Path ".\" -Name "test_documentation" -ItemType "directory" Copy-Item -Path "tests\conftest.py" -Destination ".\test_launcher\" Copy-Item -Path "tests\conftest.py" -Destination ".\test_server\" Copy-Item -Path "tests\conftest.py" -Destination ".\test_local_server\" @@ -16,6 +17,7 @@ Copy-Item -Path "tests\conftest.py" -Destination ".\test_remote_workflow\" Copy-Item -Path "tests\conftest.py" -Destination ".\test_remote_operator\" Copy-Item -Path "tests\conftest.py" -Destination ".\test_service\" Copy-Item -Path "tests\conftest.py" -Destination ".\test_custom_type_field\" +Copy-Item -Path "tests\conftest.py" -Destination ".\test_documentation\" Copy-Item -Path "tests\test_launcher.py" -Destination ".\test_launcher\" Copy-Item -Path "tests\test_server.py" -Destination ".\test_server\" Copy-Item -Path "tests\test_local_server.py" -Destination ".\test_local_server\" @@ -25,6 +27,7 @@ Copy-Item -Path "tests\test_remote_workflow.py" -Destination ".\test_remote_work Copy-Item -Path "tests\test_remote_operator.py" -Destination ".\test_remote_operator\" Copy-Item -Path "tests\test_service.py" -Destination ".\test_service\" Copy-Item -Path "tests\test_custom_type_field.py" -Destination ".\test_custom_type_field\" +Copy-Item -Path "tests\test_documentation.py" -Destination ".\test_documentation\" Remove-Item -Path "tests\test_server.py" Remove-Item -Path "tests\test_launcher.py" Remove-Item -Path "tests\test_local_server.py" @@ -33,4 +36,5 @@ Remove-Item -Path "tests\test_workflow.py" Remove-Item -Path "tests\test_remote_workflow.py" Remove-Item -Path "tests\test_remote_operator.py" Remove-Item -Path "tests\test_service.py" -Remove-Item -Path "tests\test_custom_type_field.py" \ No newline at end of file +Remove-Item -Path "tests\test_custom_type_field.py" +Remove-Item -Path "tests\test_documentation.py" \ No newline at end of file diff --git a/.github/workflows/test_docker.yml b/.github/workflows/test_docker.yml index 425d2dfaf74..4099956cfc8 100644 --- a/.github/workflows/test_docker.yml +++ b/.github/workflows/test_docker.yml @@ -205,6 +205,15 @@ jobs: command: | pytest $DEBUG $COVERAGE $RERUNS --junitxml=../tests/junit/test-results12.xml tests/operators/. + - name: "Test Documentation" + uses: nick-fields/retry@v3 + with: + timeout_minutes: 4 + max_attempts: 2 + shell: bash + command: | + pytest $DEBUG $COVERAGE $RERUNS --junitxml=../tests/junit/test-results13.xml test_documentation/. + # - name: "Test API Entry" # shell: bash # working-directory: tests