Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
0bfc3ba
Remove operators module files after successfully starting the server
PProfizi Feb 12, 2025
cfdff60
Try converting MD to RST on-the-fly with pypandoc (way too slow)
PProfizi Feb 12, 2025
e0de641
Directly include the MD descriptions
PProfizi Feb 12, 2025
702c7c6
Try without black formatting
PProfizi Feb 12, 2025
decef7a
Add MD but with indents
PProfizi Feb 12, 2025
675b291
Properly expose Markdown descriptions in code generation
PProfizi Feb 13, 2025
d279a16
Fix mustache spacings
PProfizi Feb 13, 2025
4fcd103
Fix mustache
PProfizi Feb 13, 2025
df55f74
Improve type hinting in operator.mustache
PProfizi Feb 13, 2025
d1a734c
Use a dedicated indented string for pin docstrings
PProfizi Feb 13, 2025
a82b86f
Remove superfluous newlines after the operator description in the doc…
PProfizi Feb 13, 2025
05e6649
Fix mustache for pin description in main operator docstring
PProfizi Feb 13, 2025
a10b555
Use black to reformat generated modules (does not scramble descriptions)
PProfizi Feb 13, 2025
02ca718
Code quality fix to mustache
PProfizi Feb 13, 2025
cd6501a
Manage operator description separately
PProfizi Feb 24, 2025
3ac82fe
Print timings of generation
PProfizi Feb 24, 2025
b3620f1
try pypandoc
PProfizi Feb 24, 2025
8775abb
Add Markdown2RstTranslator in ansys.dpf.core.operators.build and a test
PProfizi Feb 25, 2025
7b80c6e
Move Markdown2RstTranslator to ansys.dpf.core.operators.translator
PProfizi Feb 25, 2025
7584491
Add pypandoc to requirements_test.txt
PProfizi Feb 25, 2025
1459f5e
Add pypandoc_binary to requirements_test.txt
PProfizi Feb 25, 2025
75edc2c
Merge branch 'master' into feat/support_markdown_and_latex_in_operato…
PProfizi Feb 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .ci/code_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
from ansys.dpf import core
from ansys.dpf.core.operators import build

core.set_default_server_context(core.AvailableServerContexts.premium)
core.start_local_server(config=core.AvailableServerConfigs.LegacyGrpcServer)

local_dir = Path(__file__).parent
TARGET_PATH = local_dir.parent / "src" / "ansys" / "dpf" / "core" / "operators"
files = TARGET_PATH.glob("*")
Expand All @@ -26,7 +29,4 @@
except:
pass

core.set_default_server_context(core.AvailableServerContexts.premium)
core.start_local_server(config=core.AvailableServerConfigs.LegacyGrpcServer)

build.build_operators()
66 changes: 45 additions & 21 deletions src/ansys/dpf/core/operators/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
from datetime import datetime
from textwrap import wrap
import time

import black
import chevron
Expand All @@ -13,16 +14,12 @@
from ansys.dpf.core.mapping_types import map_types_to_python


def build_docstring(specification):
def build_docstring(specification_description):
"""Used to generate class docstrings."""
docstring = ""
if specification.description:
docstring += "\n".join(
wrap(specification.description, subsequent_indent=" ")
)
docstring += "\n\n"
docstring = docstring.rstrip()
return docstring.replace('"', "'")
if specification_description:
docstring += specification_description.replace("\n", "\n ")
return docstring


def map_types(cpp_types):
Expand Down Expand Up @@ -83,6 +80,9 @@ def build_pin_data(pins, output=False):
if multiple_types and output:
printable_type_names = [_make_printable_type(name) for name in type_names]

document = specification.document
document_pin_docstring = document.replace("\n", "\n ")

pin_data = {
"id": id,
"name": pin_name,
Expand All @@ -97,13 +97,8 @@ def build_pin_data(pins, output=False):
"main_type": main_type,
"built_in_main_type": main_type in built_in_types,
"optional": specification.optional,
"document": "\n".join(
wrap(
specification.document.capitalize().lstrip(' '),
subsequent_indent=" ",
width=45,
)
),
"document": document,
"document_pin_docstring": document_pin_docstring,
"ellipsis": 0 if specification.ellipsis else -1,
}

Expand All @@ -125,7 +120,7 @@ def build_pin_data(pins, output=False):


def build_operator(
specification, operator_name, class_name, capital_class_name, category
specification, operator_name, class_name, capital_class_name, category, specification_description
):

input_pins = []
Expand All @@ -137,11 +132,7 @@ def build_operator(
output_pins = build_pin_data(specification.outputs, output=True)
multiple_output_types = any(pin["multiple_types"] for pin in output_pins)

docstring = build_docstring(specification)

specification_description = "\n".join(
wrap(specification.description, subsequent_indent=" ")
)
docstring = build_docstring(specification_description)

date_and_time = datetime.now().strftime("%m/%d/%Y, %H:%M:%S")

Expand Down Expand Up @@ -173,6 +164,7 @@ def build_operator(

def build_operators():
print(f"Generating operators for server {dpf.SERVER.version}")
time_0 = time.time()

this_path = os.path.dirname(os.path.abspath(__file__))

Expand All @@ -192,6 +184,9 @@ def build_operators():
"invert", "invert_fc",
]
categories = set()

translator = Markdown2RstTranslator()

for operator_name in available_operators:
if succeeded == done + 100:
done += 100
Expand Down Expand Up @@ -226,6 +221,9 @@ def build_operators():
# Get python class name from scripting name
capital_class_name = common._snake_to_camel_case(scripting_name)

# Convert Markdown descriptions to RST
specification_description = translator.convert(specification.description)

# Write to operator file
operator_file = os.path.join(category_path, scripting_name + ".py")
with open(operator_file, "wb") as f:
Expand All @@ -236,6 +234,7 @@ def build_operators():
scripting_name,
capital_class_name,
category,
specification_description,
)
exec(operator_str, globals())
f.write(operator_str.encode())
Expand Down Expand Up @@ -269,12 +268,37 @@ def build_operators():

if succeeded == len(available_operators) - hidden:
print("Success")
print(f"Took {time.time() - time_0}")
exit(0)
else:
print("Terminated with errors")
exit(1)


class Markdown2RstTranslator:
"""A translator class for Markdown to RST text conversion."""
def __init__(self):
import pypandoc

self._convert = pypandoc.convert_text

# import mistune
# from mistune.renderers.rst import RSTRenderer
#
# md2rst_converter = mistune.create_markdown(renderer=RSTRenderer())

def convert(self, text:str) -> str:
"""Convert the Markdown text in input to RST."""
return self._convert(
source=text,
to="rst",
format="md",
verify_format=False, # Improves performance but does not check validity of format
extra_args=["--eol=lf"],
)
# return md2rst_converter(specification.description)


if __name__ == "__main__":
dpf.set_default_server_context(dpf.AvailableServerContexts.premium)
dpf.start_local_server(config=dpf.AvailableServerConfigs.LegacyGrpcServer)
Expand Down
105 changes: 61 additions & 44 deletions src/ansys/dpf/core/operators/operator.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
Autogenerated DPF operator classes.
"""

from __future__ import annotations

from warnings import warn
from ansys.dpf.core.dpf_operator import Operator
from ansys.dpf.core.inputs import Input, _Inputs
Expand All @@ -17,36 +19,39 @@ from ansys.dpf.core.outputs import _Outputs
from ansys.dpf.core.outputs import _modify_output_spec_with_one_type
{{/multiple_output_types}}
from ansys.dpf.core.operators.specification import PinSpecification, Specification
from ansys.dpf.core.config import Config
from ansys.dpf.core.server_types import AnyServerType


class {{class_name}}(Operator):
"""{{{docstring}}}
r"""{{{docstring}}}

Parameters
----------
{{#input_pins}}
{{#optional}}
{{name}} :{{#types_for_docstring}} {{types_for_docstring}},{{/types_for_docstring}} optional
{{name}}:{{#types_for_docstring}} {{types_for_docstring}},{{/types_for_docstring}} optional
{{/optional}}
{{^optional}}
{{name}} :{{#types_for_docstring}} {{types_for_docstring}}{{/types_for_docstring}}
{{name}}:{{#types_for_docstring}} {{types_for_docstring}}{{/types_for_docstring}}
{{/optional}}
{{#document}}
{{{document}}}
{{/document}}
{{#document_pin_docstring}}
{{{document_pin_docstring}}}
{{/document_pin_docstring}}
{{/input_pins}}

Returns
-------
{{#output_pins}}
{{#optional}}
{{name}} :{{#types_for_docstring}} {{types_for_docstring}},{{/types_for_docstring}} optional
{{name}}:{{#types_for_docstring}} {{types_for_docstring}},{{/types_for_docstring}} optional
{{/optional}}
{{^optional}}
{{name}} :{{#types_for_docstring}} {{types_for_docstring}}{{/types_for_docstring}}
{{name}}:{{#types_for_docstring}} {{types_for_docstring}}{{/types_for_docstring}}
{{/optional}}
{{#document}}
{{{document}}}
{{/document}}
{{#document_pin_docstring}}
{{{document_pin_docstring}}}
{{/document_pin_docstring}}
{{/output_pins}}

Examples
Expand Down Expand Up @@ -87,8 +92,8 @@ class {{class_name}}(Operator):
{{/input_pins}}

@staticmethod
def _spec():
description = """{{specification_description}}"""
def _spec() -> Specification:
description = r"""{{{specification_description}}}"""
spec = Specification(
description=description,
map_input_pin_spec={
Expand All @@ -102,7 +107,7 @@ class {{class_name}}(Operator):
type_names=["any"],
{{/has_types}}
optional={{optional}},
document="""{{{document}}}""",
document=r"""{{{document}}}""",
{{#has_derived_class}}
name_derived_class=["{{{derived_type_name}}}"],
{{/has_derived_class}}
Expand All @@ -117,7 +122,7 @@ class {{class_name}}(Operator):
type_names={{{types}}},
{{/has_types}}
optional={{optional}},
document="""{{{document}}}""",
document=r"""{{{document}}}""",
{{#has_derived_class}}
name_derived_class=["{{{derived_type_name}}}"],
{{/has_derived_class}}
Expand All @@ -128,7 +133,7 @@ class {{class_name}}(Operator):
return spec

@staticmethod
def default_config(server=None):
def default_config(server: AnyServerType = None) -> Config:
"""Returns the default config of the operator.

This config can then be changed to the user needs and be used to
Expand All @@ -137,32 +142,40 @@ class {{class_name}}(Operator):

Parameters
----------
server : server.DPFServer, optional
server:
Server with channel connected to the remote or local instance. When
``None``, attempts to use the global server.

Returns
-------
config:
A new Config instance equivalent to the default config for this operator.
"""
return Operator.default_config(name="{{operator_name}}", server=server)

@property
def inputs(self):
def inputs(self) -> Inputs{{capital_class_name}}:
"""Enables to connect inputs to the operator

Returns
--------
inputs : Inputs{{capital_class_name}}
inputs:
An instance of Inputs{{capital_class_name}}.
"""
return super().inputs

@property
def outputs(self):
def outputs(self) -> Outputs{{capital_class_name}}:
"""Enables to get outputs of the operator by evaluating it

Returns
--------
outputs : Outputs{{capital_class_name}}
outputs:
An instance of Outputs{{capital_class_name}}.
"""
return super().outputs


class Inputs{{capital_class_name}}(_Inputs):
"""Intermediate class used to connect user inputs to
{{class_name}} operator.
Expand All @@ -183,19 +196,20 @@ class Inputs{{capital_class_name}}(_Inputs):
self._{{name}} = Input({{class_name}}._spec().input_pin({{id}}), {{id}}, op, {{ellipsis}})
self._inputs.append(self._{{name}})
{{/input_pins}}

{{#input_pins}}

@property
def {{name}}(self):
"""Allows to connect {{name}} input to the operator.
{{#document}}
def {{name}}(self) -> Input:
r"""Allows to connect {{name}} input to the operator.
{{#document_pin_docstring}}

{{{document}}}
{{/document}}
{{{document_pin_docstring}}}
{{/document_pin_docstring}}

Parameters
----------
my_{{name}} :{{#types_for_docstring}} {{types_for_docstring}}{{/types_for_docstring}}
Returns
-------
input:
An Input instance for this pin.

Examples
--------
Expand All @@ -206,8 +220,9 @@ class Inputs{{capital_class_name}}(_Inputs):
>>> op.inputs.{{name}}(my_{{name}})
"""
return self._{{name}}

{{/input_pins}}


class Outputs{{capital_class_name}}(_Outputs):
"""Intermediate class used to get outputs from
{{class_name}} operator.
Expand Down Expand Up @@ -236,27 +251,29 @@ class Outputs{{capital_class_name}}(_Outputs):
self._outputs.append(self._{{name}})
{{/multiple_types}}
{{/output_pins}}
{{#output_pins}}{{^multiple_types}}

{{#output_pins}}
{{^multiple_types}}
@property
def {{name}}(self):
"""Allows to get {{name}} output of the operator
def {{name}}(self) -> Output:
r"""Allows to get {{name}} output of the operator
{{#document_pin_docstring}}

{{{document_pin_docstring}}}
{{/document_pin_docstring}}

Returns
----------
{{#derived_type_name}}
my_{{name}} : {{derived_type_name}}
{{/derived_type_name}}
{{^derived_type_name}}
my_{{name}} :{{#types_for_docstring}} {{types_for_docstring}}{{/types_for_docstring}}
{{/derived_type_name}}
-------
output:
An Output instance for this pin.

Examples
--------
>>> from ansys.dpf import core as dpf
>>> op = dpf.operators.{{category}}.{{class_name}}()
>>> # Connect inputs : op.inputs. ...
>>> # Get the output from op.outputs. ...
>>> result_{{name}} = op.outputs.{{name}}()
""" # noqa: E501
"""
return self._{{name}}
{{/multiple_types}}{{/output_pins}}
{{/multiple_types}}
{{/output_pins}}
Loading
Loading