ansys-fluent-core 0.30.dev1__py3-none-any.whl → 0.30.dev3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ansys-fluent-core might be problematic. Click here for more details.
- ansys/fluent/core/__init__.py +14 -3
- ansys/fluent/core/codegen/datamodelgen.py +17 -3
- ansys/fluent/core/codegen/settingsgen.py +26 -4
- ansys/fluent/core/codegen/walk_api.py +11 -1
- ansys/fluent/core/exceptions.py +7 -4
- ansys/fluent/core/generated/api_tree/api_objects.json +1 -1
- ansys/fluent/core/generated/datamodel_242/meshing_utilities.pyi +990 -0
- ansys/fluent/core/generated/datamodel_251/meshing_utilities.pyi +1002 -0
- ansys/fluent/core/generated/datamodel_252/meshing.py +7 -0
- ansys/fluent/core/generated/datamodel_252/meshing_utilities.pyi +1007 -0
- ansys/fluent/core/generated/datamodel_252/preferences.py +132 -3
- ansys/fluent/core/generated/fluent_version_252.py +3 -3
- ansys/fluent/core/generated/meshing/tui_252.py +111 -12
- ansys/fluent/core/generated/solver/settings_222.py +166 -170
- ansys/fluent/core/generated/solver/settings_222.pyi +10 -8
- ansys/fluent/core/generated/solver/settings_231.py +551 -598
- ansys/fluent/core/generated/solver/settings_231.pyi +40 -38
- ansys/fluent/core/generated/solver/settings_232.py +689 -736
- ansys/fluent/core/generated/solver/settings_232.pyi +39 -37
- ansys/fluent/core/generated/solver/settings_241.py +1365 -1439
- ansys/fluent/core/generated/solver/settings_241.pyi +1071 -1074
- ansys/fluent/core/generated/solver/settings_242.py +2852 -3098
- ansys/fluent/core/generated/solver/settings_242.pyi +2059 -2151
- ansys/fluent/core/generated/solver/settings_251.py +3656 -3914
- ansys/fluent/core/generated/solver/settings_251.pyi +2915 -3008
- ansys/fluent/core/generated/solver/settings_252.py +5894 -5707
- ansys/fluent/core/generated/solver/settings_252.pyi +4411 -4297
- ansys/fluent/core/generated/solver/tui_252.py +205 -25
- ansys/fluent/core/get_build_details.py +2 -2
- ansys/fluent/core/launcher/container_launcher.py +7 -1
- ansys/fluent/core/launcher/fluent_container.py +3 -2
- ansys/fluent/core/launcher/pim_launcher.py +7 -1
- ansys/fluent/core/launcher/slurm_launcher.py +7 -1
- ansys/fluent/core/launcher/standalone_launcher.py +7 -1
- ansys/fluent/core/post_objects/meta.py +6 -6
- ansys/fluent/core/services/datamodel_se.py +28 -23
- ansys/fluent/core/services/field_data.py +17 -5
- ansys/fluent/core/session_base_meshing.py +3 -3
- ansys/fluent/core/session_meshing.py +4 -4
- ansys/fluent/core/session_meshing.pyi +9 -9
- ansys/fluent/core/session_pure_meshing.pyi +9 -9
- ansys/fluent/core/session_solver.py +1 -1
- ansys/fluent/core/session_solver.pyi +5 -5
- ansys/fluent/core/solver/_docstrings.py +244 -0
- ansys/fluent/core/solver/error_message.py +7 -12
- ansys/fluent/core/solver/flobject.py +40 -15
- ansys/fluent/core/utils/fluent_version.py +2 -3
- ansys/fluent/core/workflow.py +1 -0
- {ansys_fluent_core-0.30.dev1.dist-info → ansys_fluent_core-0.30.dev3.dist-info}/METADATA +4 -4
- {ansys_fluent_core-0.30.dev1.dist-info → ansys_fluent_core-0.30.dev3.dist-info}/RECORD +76 -73
- ansys/fluent/core/_version.py +0 -40
- /ansys/fluent/core/generated/datamodel_222/{PartManagement.py → part_management.py} +0 -0
- /ansys/fluent/core/generated/datamodel_222/{PMFileManagement.py → pm_file_management.py} +0 -0
- /ansys/fluent/core/generated/datamodel_231/{PartManagement.py → part_management.py} +0 -0
- /ansys/fluent/core/generated/datamodel_231/{PMFileManagement.py → pm_file_management.py} +0 -0
- /ansys/fluent/core/generated/datamodel_231/{solverworkflow.py → solver_workflow.py} +0 -0
- /ansys/fluent/core/generated/datamodel_232/{PartManagement.py → part_management.py} +0 -0
- /ansys/fluent/core/generated/datamodel_232/{PMFileManagement.py → pm_file_management.py} +0 -0
- /ansys/fluent/core/generated/datamodel_232/{solverworkflow.py → solver_workflow.py} +0 -0
- /ansys/fluent/core/generated/datamodel_241/{PartManagement.py → part_management.py} +0 -0
- /ansys/fluent/core/generated/datamodel_241/{PMFileManagement.py → pm_file_management.py} +0 -0
- /ansys/fluent/core/generated/datamodel_241/{solverworkflow.py → solver_workflow.py} +0 -0
- /ansys/fluent/core/generated/datamodel_242/{MeshingUtilities.py → meshing_utilities.py} +0 -0
- /ansys/fluent/core/generated/datamodel_242/{PartManagement.py → part_management.py} +0 -0
- /ansys/fluent/core/generated/datamodel_242/{PMFileManagement.py → pm_file_management.py} +0 -0
- /ansys/fluent/core/generated/datamodel_242/{solverworkflow.py → solver_workflow.py} +0 -0
- /ansys/fluent/core/generated/datamodel_251/{MeshingUtilities.py → meshing_utilities.py} +0 -0
- /ansys/fluent/core/generated/datamodel_251/{PartManagement.py → part_management.py} +0 -0
- /ansys/fluent/core/generated/datamodel_251/{PMFileManagement.py → pm_file_management.py} +0 -0
- /ansys/fluent/core/generated/datamodel_251/{solverworkflow.py → solver_workflow.py} +0 -0
- /ansys/fluent/core/generated/datamodel_252/{MeshingUtilities.py → meshing_utilities.py} +0 -0
- /ansys/fluent/core/generated/datamodel_252/{PartManagement.py → part_management.py} +0 -0
- /ansys/fluent/core/generated/datamodel_252/{PMFileManagement.py → pm_file_management.py} +0 -0
- /ansys/fluent/core/generated/datamodel_252/{solverworkflow.py → solver_workflow.py} +0 -0
- /ansys/fluent/core/{systemcoupling.py → system_coupling.py} +0 -0
- {ansys_fluent_core-0.30.dev1.dist-info → ansys_fluent_core-0.30.dev3.dist-info}/LICENSE +0 -0
- {ansys_fluent_core-0.30.dev1.dist-info → ansys_fluent_core-0.30.dev3.dist-info}/WHEEL +0 -0
|
@@ -1016,7 +1016,7 @@ class PyStateContainer(PyCallableStateObject):
|
|
|
1016
1016
|
service=service,
|
|
1017
1017
|
rules=rules,
|
|
1018
1018
|
path=[] if path is None else path,
|
|
1019
|
-
|
|
1019
|
+
_cached_attrs={},
|
|
1020
1020
|
)
|
|
1021
1021
|
)
|
|
1022
1022
|
|
|
@@ -1071,15 +1071,15 @@ class PyStateContainer(PyCallableStateObject):
|
|
|
1071
1071
|
)
|
|
1072
1072
|
|
|
1073
1073
|
def _get_cached_attr(self, attrib: str) -> Any:
|
|
1074
|
-
cached_val = self.
|
|
1074
|
+
cached_val = self._cached_attrs.get(attrib)
|
|
1075
1075
|
if cached_val is None:
|
|
1076
1076
|
cached_val = self._get_remote_attr(attrib)
|
|
1077
1077
|
try: # will fail for Fluent 23.1 or before
|
|
1078
1078
|
self.add_on_attribute_changed(
|
|
1079
1079
|
attrib,
|
|
1080
|
-
functools.partial(dict.__setitem__, self.
|
|
1080
|
+
functools.partial(dict.__setitem__, self._cached_attrs, attrib),
|
|
1081
1081
|
)
|
|
1082
|
-
self.
|
|
1082
|
+
self._cached_attrs[attrib] = cached_val
|
|
1083
1083
|
except Exception:
|
|
1084
1084
|
pass
|
|
1085
1085
|
return cached_val
|
|
@@ -1885,42 +1885,47 @@ class PyCommand:
|
|
|
1885
1885
|
else:
|
|
1886
1886
|
self.path = path
|
|
1887
1887
|
self._static_info = None # command's static info
|
|
1888
|
+
self.file_behavior = None
|
|
1889
|
+
|
|
1890
|
+
def _update_file_behavior(self, file_purpose):
|
|
1891
|
+
purpose_to_class = {
|
|
1892
|
+
"input": _InputFile,
|
|
1893
|
+
"output": _OutputFile,
|
|
1894
|
+
"inout": _InOutFile,
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
if file_purpose:
|
|
1898
|
+
if file_purpose in purpose_to_class:
|
|
1899
|
+
file_class = purpose_to_class[file_purpose]
|
|
1900
|
+
self.file_behavior = file_class()
|
|
1901
|
+
setattr(self.file_behavior, "service", self.service)
|
|
1902
|
+
else:
|
|
1903
|
+
raise DisallowedFilePurpose(
|
|
1904
|
+
"File purpose", file_purpose, ["input", "output", "inout"]
|
|
1905
|
+
)
|
|
1888
1906
|
|
|
1889
1907
|
def _get_file_purpose(self, arg):
|
|
1890
1908
|
try:
|
|
1891
1909
|
cmd_instance = self.create_instance()
|
|
1892
1910
|
arg_instance = getattr(cmd_instance, arg)
|
|
1893
1911
|
file_purpose = arg_instance.get_attr("filePurpose")
|
|
1894
|
-
if file_purpose:
|
|
1895
|
-
if file_purpose == "input":
|
|
1896
|
-
if _InputFile not in self.__class__.__bases__:
|
|
1897
|
-
self.__class__.__bases__ += (_InputFile,)
|
|
1898
|
-
elif file_purpose == "output":
|
|
1899
|
-
if _OutputFile not in self.__class__.__bases__:
|
|
1900
|
-
self.__class__.__bases__ += (_OutputFile,)
|
|
1901
|
-
elif file_purpose == "inout":
|
|
1902
|
-
if _InOutFile not in self.__class__.__bases__:
|
|
1903
|
-
self.__class__.__bases__ += (_InOutFile,)
|
|
1904
|
-
else:
|
|
1905
|
-
raise DisallowedFilePurpose(
|
|
1906
|
-
"File purpose", file_purpose, ["input", "output", "inout"]
|
|
1907
|
-
)
|
|
1908
1912
|
del cmd_instance, arg_instance
|
|
1913
|
+
self._update_file_behavior(file_purpose)
|
|
1909
1914
|
return file_purpose if file_purpose else None
|
|
1910
1915
|
except AttributeError:
|
|
1911
1916
|
pass
|
|
1912
1917
|
|
|
1913
1918
|
def before_execute(self, value):
|
|
1914
1919
|
"""Executes before command execution."""
|
|
1915
|
-
if hasattr(self, "_do_before_execute"):
|
|
1916
|
-
return self._do_before_execute(value)
|
|
1920
|
+
if hasattr(self.file_behavior, "_do_before_execute"):
|
|
1921
|
+
return self.file_behavior._do_before_execute(value)
|
|
1917
1922
|
else:
|
|
1918
1923
|
return value
|
|
1919
1924
|
|
|
1920
1925
|
def after_execute(self, value):
|
|
1921
1926
|
"""Executes after command execution."""
|
|
1922
|
-
if hasattr(self, "_do_after_execute"):
|
|
1923
|
-
self._do_after_execute(value)
|
|
1927
|
+
if hasattr(self.file_behavior, "_do_after_execute"):
|
|
1928
|
+
self.file_behavior._do_after_execute(value)
|
|
1924
1929
|
|
|
1925
1930
|
def __call__(self, *args, **kwds) -> Any:
|
|
1926
1931
|
"""Execute the command.
|
|
@@ -2302,7 +2307,7 @@ class DataModelType(Enum):
|
|
|
2302
2307
|
class PyMenuGeneric(PyMenu):
|
|
2303
2308
|
"""Generic PyMenu class for when generated API code is not available."""
|
|
2304
2309
|
|
|
2305
|
-
attrs = ("service", "rules", "path", "
|
|
2310
|
+
attrs = ("service", "rules", "path", "_cached_attrs")
|
|
2306
2311
|
|
|
2307
2312
|
def _get_child_names(self) -> tuple[list, list, list, list]:
|
|
2308
2313
|
response = self.service.get_specs(
|
|
@@ -334,11 +334,19 @@ class _AllowedSurfaceNames(_AllowedNames):
|
|
|
334
334
|
|
|
335
335
|
Raises
|
|
336
336
|
------
|
|
337
|
+
RuntimeError
|
|
338
|
+
If issue in retrieving surface list.
|
|
337
339
|
DisallowedValuesError
|
|
338
340
|
If surface name is invalid.
|
|
339
341
|
"""
|
|
340
|
-
|
|
341
|
-
|
|
342
|
+
try:
|
|
343
|
+
valid_names = self() # Fetch once, upfront
|
|
344
|
+
except Exception as e:
|
|
345
|
+
raise RuntimeError("Failed to retrieve valid surface names.") from e
|
|
346
|
+
|
|
347
|
+
if validate_inputs and surface_name not in valid_names:
|
|
348
|
+
raise DisallowedValuesError("surface", surface_name, valid_names)
|
|
349
|
+
|
|
342
350
|
return surface_name
|
|
343
351
|
|
|
344
352
|
|
|
@@ -726,7 +734,7 @@ class FieldTransaction:
|
|
|
726
734
|
[
|
|
727
735
|
FieldDataProtoModule.PathlinesFieldRequest(
|
|
728
736
|
surfaceId=surface_id,
|
|
729
|
-
field=field_name,
|
|
737
|
+
field=self._allowed_scalar_field_names.valid_name(field_name),
|
|
730
738
|
additionalField=additional_field_name,
|
|
731
739
|
provideParticleTimeField=provide_particle_time_field,
|
|
732
740
|
dataLocation=(
|
|
@@ -818,7 +826,11 @@ def _get_surface_ids(
|
|
|
818
826
|
]
|
|
819
827
|
)
|
|
820
828
|
else:
|
|
821
|
-
|
|
829
|
+
allowed_surf_ids = _AllowedSurfaceIDs(field_info)()
|
|
830
|
+
if surf in allowed_surf_ids:
|
|
831
|
+
surface_ids.append(surf)
|
|
832
|
+
else:
|
|
833
|
+
raise DisallowedValuesError("surface", surf, allowed_surf_ids)
|
|
822
834
|
return surface_ids
|
|
823
835
|
|
|
824
836
|
|
|
@@ -1437,7 +1449,7 @@ class FieldData:
|
|
|
1437
1449
|
[
|
|
1438
1450
|
FieldDataProtoModule.PathlinesFieldRequest(
|
|
1439
1451
|
surfaceId=surface_id,
|
|
1440
|
-
field=field_name,
|
|
1452
|
+
field=self._allowed_scalar_field_names.valid_name(field_name),
|
|
1441
1453
|
additionalField=additional_field_name,
|
|
1442
1454
|
provideParticleTimeField=provide_particle_time_field,
|
|
1443
1455
|
dataLocation=(
|
|
@@ -118,10 +118,10 @@ class BaseMeshing:
|
|
|
118
118
|
from ansys.fluent.core import CODEGEN_OUTDIR
|
|
119
119
|
|
|
120
120
|
meshing_utilities_module = pyfluent.utils.load_module(
|
|
121
|
-
f"
|
|
121
|
+
f"meshing_utilities_{self._version}",
|
|
122
122
|
CODEGEN_OUTDIR
|
|
123
123
|
/ f"datamodel_{self._version}"
|
|
124
|
-
/ "
|
|
124
|
+
/ "meshing_utilities.py",
|
|
125
125
|
)
|
|
126
126
|
meshing_utilities_root = meshing_utilities_module.Root(
|
|
127
127
|
self._se_service, "MeshingUtilities", []
|
|
@@ -130,7 +130,7 @@ class BaseMeshing:
|
|
|
130
130
|
datamodel_logger.warning(_CODEGEN_MSG_DATAMODEL)
|
|
131
131
|
if self.get_fluent_version() >= FluentVersion.v242:
|
|
132
132
|
meshing_utilities_root = PyMenuGeneric(
|
|
133
|
-
self._se_service, "
|
|
133
|
+
self._se_service, "MeshingUtilities"
|
|
134
134
|
)
|
|
135
135
|
return meshing_utilities_root
|
|
136
136
|
|
|
@@ -64,7 +64,7 @@ class Meshing(PureMeshing):
|
|
|
64
64
|
transcript can be subsequently started and stopped
|
|
65
65
|
using method calls on the ``Session`` object.
|
|
66
66
|
"""
|
|
67
|
-
self.
|
|
67
|
+
self._switched = False
|
|
68
68
|
super(Meshing, self).__init__(
|
|
69
69
|
fluent_connection=fluent_connection,
|
|
70
70
|
scheme_eval=scheme_eval,
|
|
@@ -89,14 +89,14 @@ class Meshing(PureMeshing):
|
|
|
89
89
|
scheme_eval=self.scheme_eval,
|
|
90
90
|
file_transfer_service=self._file_transfer_service,
|
|
91
91
|
)
|
|
92
|
-
self.
|
|
92
|
+
self._switched = True
|
|
93
93
|
return solver_session
|
|
94
94
|
|
|
95
95
|
def __getattribute__(self, item: str):
|
|
96
|
-
if item == "
|
|
96
|
+
if item == "_switched":
|
|
97
97
|
return super(Meshing, self).__getattribute__(item)
|
|
98
98
|
|
|
99
|
-
if self.
|
|
99
|
+
if self._switched and item != "exit":
|
|
100
100
|
return None
|
|
101
101
|
|
|
102
102
|
return super(Meshing, self).__getattribute__(item)
|
|
@@ -20,21 +20,21 @@
|
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
# SOFTWARE.
|
|
22
22
|
|
|
23
|
-
from ansys.fluent.core.generated.
|
|
23
|
+
from ansys.fluent.core.generated.datamodel_251.meshing import Root as meshing_root
|
|
24
|
+
from ansys.fluent.core.generated.datamodel_251.meshing_utilities import (
|
|
24
25
|
Root as meshing_utilities_root,
|
|
25
26
|
)
|
|
26
|
-
from ansys.fluent.core.generated.
|
|
27
|
-
Root as pmfilemanagement_root,
|
|
28
|
-
)
|
|
29
|
-
from ansys.fluent.core.generated.datamodel_242.PartManagement import (
|
|
27
|
+
from ansys.fluent.core.generated.datamodel_251.part_management import (
|
|
30
28
|
Root as partmanagement_root,
|
|
31
29
|
)
|
|
32
|
-
from ansys.fluent.core.generated.
|
|
33
|
-
|
|
30
|
+
from ansys.fluent.core.generated.datamodel_251.pm_file_management import (
|
|
31
|
+
Root as pmfilemanagement_root,
|
|
32
|
+
)
|
|
33
|
+
from ansys.fluent.core.generated.datamodel_251.preferences import (
|
|
34
34
|
Root as preferences_root,
|
|
35
35
|
)
|
|
36
|
-
from ansys.fluent.core.generated.
|
|
37
|
-
from ansys.fluent.core.generated.meshing.
|
|
36
|
+
from ansys.fluent.core.generated.datamodel_251.workflow import Root as workflow_root
|
|
37
|
+
from ansys.fluent.core.generated.meshing.tui_251 import main_menu
|
|
38
38
|
|
|
39
39
|
class Meshing:
|
|
40
40
|
@property
|
|
@@ -20,21 +20,21 @@
|
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
# SOFTWARE.
|
|
22
22
|
|
|
23
|
-
from ansys.fluent.core.generated.
|
|
23
|
+
from ansys.fluent.core.generated.datamodel_251.meshing import Root as meshing_root
|
|
24
|
+
from ansys.fluent.core.generated.datamodel_251.meshing_utilities import (
|
|
24
25
|
Root as meshing_utilities_root,
|
|
25
26
|
)
|
|
26
|
-
from ansys.fluent.core.generated.
|
|
27
|
-
Root as pmfilemanagement_root,
|
|
28
|
-
)
|
|
29
|
-
from ansys.fluent.core.generated.datamodel_242.PartManagement import (
|
|
27
|
+
from ansys.fluent.core.generated.datamodel_251.part_management import (
|
|
30
28
|
Root as partmanagement_root,
|
|
31
29
|
)
|
|
32
|
-
from ansys.fluent.core.generated.
|
|
33
|
-
|
|
30
|
+
from ansys.fluent.core.generated.datamodel_251.pm_file_management import (
|
|
31
|
+
Root as pmfilemanagement_root,
|
|
32
|
+
)
|
|
33
|
+
from ansys.fluent.core.generated.datamodel_251.preferences import (
|
|
34
34
|
Root as preferences_root,
|
|
35
35
|
)
|
|
36
|
-
from ansys.fluent.core.generated.
|
|
37
|
-
from ansys.fluent.core.generated.meshing.
|
|
36
|
+
from ansys.fluent.core.generated.datamodel_251.workflow import Root as workflow_root
|
|
37
|
+
from ansys.fluent.core.generated.meshing.tui_251 import main_menu
|
|
38
38
|
|
|
39
39
|
class PureMeshing:
|
|
40
40
|
@property
|
|
@@ -54,7 +54,7 @@ from ansys.fluent.core.solver.flobject import (
|
|
|
54
54
|
import ansys.fluent.core.solver.function.reduction as reduction_old
|
|
55
55
|
from ansys.fluent.core.streaming_services.events_streaming import SolverEvent
|
|
56
56
|
from ansys.fluent.core.streaming_services.monitor_streaming import MonitorsManager
|
|
57
|
-
from ansys.fluent.core.
|
|
57
|
+
from ansys.fluent.core.system_coupling import SystemCoupling
|
|
58
58
|
from ansys.fluent.core.utils.execution import asynchronous
|
|
59
59
|
from ansys.fluent.core.utils.fluent_version import (
|
|
60
60
|
FluentVersion,
|
|
@@ -20,13 +20,13 @@
|
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
# SOFTWARE.
|
|
22
22
|
|
|
23
|
-
from ansys.fluent.core.generated.
|
|
23
|
+
from ansys.fluent.core.generated.datamodel_251.preferences import (
|
|
24
24
|
Root as preferences_root,
|
|
25
25
|
)
|
|
26
|
-
from ansys.fluent.core.generated.
|
|
27
|
-
import ansys.fluent.core.generated.solver.
|
|
28
|
-
from ansys.fluent.core.generated.solver.
|
|
29
|
-
from ansys.fluent.core.
|
|
26
|
+
from ansys.fluent.core.generated.datamodel_251.workflow import Root as workflow_root
|
|
27
|
+
import ansys.fluent.core.generated.solver.settings_251 as settings_root
|
|
28
|
+
from ansys.fluent.core.generated.solver.tui_251 import main_menu
|
|
29
|
+
from ansys.fluent.core.system_coupling import SystemCoupling
|
|
30
30
|
|
|
31
31
|
class Solver:
|
|
32
32
|
@property
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Copyright (C) 2021 - 2025 ANSYS, Inc. and/or its affiliates.
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
#
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
# copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
# SOFTWARE.
|
|
22
|
+
|
|
23
|
+
"""Module for analyzing API docstrings."""
|
|
24
|
+
|
|
25
|
+
# N.b. misspellings:
|
|
26
|
+
# - selected missing last e.
|
|
27
|
+
# - many known API misspellings also have corresponding
|
|
28
|
+
# misspellingling for docstring.
|
|
29
|
+
# - CGSN
|
|
30
|
+
|
|
31
|
+
import re
|
|
32
|
+
|
|
33
|
+
_choose_words = ("Choose", "Indicate", "Select", "Specify")
|
|
34
|
+
|
|
35
|
+
_remove_first_words = _choose_words + ("Enter", "Set", "To")
|
|
36
|
+
|
|
37
|
+
# Define problematic patterns with exclusions
|
|
38
|
+
_dubious_patterns = [
|
|
39
|
+
(r"Create an instance of this.", ()),
|
|
40
|
+
(r"Enter the adaption .* menu.", ()),
|
|
41
|
+
(r"Allows you to .*", ()),
|
|
42
|
+
(r"Select to .*", ("method",)),
|
|
43
|
+
(r"Choose .*", ("method",)),
|
|
44
|
+
(r"Enter .*", ("method",)),
|
|
45
|
+
(r"Indicate .*", ("method",)),
|
|
46
|
+
(r"Perform .*", ("method",)),
|
|
47
|
+
(r"Select .*", ("method",)),
|
|
48
|
+
(r"Set .*", ("method",)),
|
|
49
|
+
(r"Specify .*", ("method",)),
|
|
50
|
+
(r".* menu.", ()),
|
|
51
|
+
(r"To .*", ()),
|
|
52
|
+
(r"Name for an object.*", ()),
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
# Rewriting problematic patterns for suggestions
|
|
56
|
+
|
|
57
|
+
_bad_0 = {
|
|
58
|
+
r"Create an instance of this.": r"Create a new instance of the current object type.",
|
|
59
|
+
r"Allows you to (.*)": r"\1",
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
_bad_1 = {
|
|
63
|
+
rf"{w} (whether or not|whether|if) (.*)": r"Specifies whether \2"
|
|
64
|
+
for w in _choose_words
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
_bad_2 = {
|
|
68
|
+
r"Select to (.*)": r"Specifies whether to \1",
|
|
69
|
+
r"Perform (.*)": r"Specifies whether to perform \1",
|
|
70
|
+
r"Enter a (.*)": r"The \1",
|
|
71
|
+
r"Enter an (.*)": r"The \1",
|
|
72
|
+
r"(.*) menu.": r"\1 object.",
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
_bad_3 = {rf"{w} (.*)": r"\1" for w in _remove_first_words}
|
|
76
|
+
|
|
77
|
+
_bad_4 = {r"Name for an object(.*)": r"Object name\1"}
|
|
78
|
+
|
|
79
|
+
_bad_to_suggested = dict(**_bad_0, **_bad_1, **_bad_2, **_bad_3, **_bad_4)
|
|
80
|
+
|
|
81
|
+
# Pre-compile fixed-format empty patterns (case-sensitive)
|
|
82
|
+
_empty_patterns = [
|
|
83
|
+
re.compile(f"'.*' {item}.") for item in ("child", "command", "query")
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _suggest_fix(docstring: str):
|
|
88
|
+
"""Suggest an improved version of a problematic docstring."""
|
|
89
|
+
for bad, suggestion in _bad_to_suggested.items():
|
|
90
|
+
match = re.match(bad, docstring)
|
|
91
|
+
if match:
|
|
92
|
+
replacement = match.expand(suggestion)
|
|
93
|
+
|
|
94
|
+
# Capitalize if original docstring started with a capital letter
|
|
95
|
+
if docstring[0].isupper():
|
|
96
|
+
replacement = replacement[0].upper() + replacement[1:]
|
|
97
|
+
|
|
98
|
+
# Ensure we don't add duplicate periods
|
|
99
|
+
if replacement.endswith("."):
|
|
100
|
+
return replacement
|
|
101
|
+
return replacement + "."
|
|
102
|
+
|
|
103
|
+
return None # No fix available
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _fixed_doc_string(api_item_type, docstring):
|
|
107
|
+
for pattern_str, exclude_types in _dubious_patterns:
|
|
108
|
+
if api_item_type in exclude_types:
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
if re.match(pattern_str, docstring):
|
|
112
|
+
fix = _suggest_fix(docstring)
|
|
113
|
+
new_fix = None
|
|
114
|
+
if fix:
|
|
115
|
+
new_fix = _fixed_doc_string(api_item_type, fix)
|
|
116
|
+
return new_fix or fix
|
|
117
|
+
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class _DocStringAnalysis:
|
|
122
|
+
"""Analyzes docstrings for potential issues and categorizes them."""
|
|
123
|
+
|
|
124
|
+
def __init__(self):
|
|
125
|
+
"""Initialize storage for categorized docstring issues."""
|
|
126
|
+
self.dubious = {}
|
|
127
|
+
self.empty = {}
|
|
128
|
+
self.clean = {}
|
|
129
|
+
|
|
130
|
+
def analyse(self, api_path: str, api_item_type: str, api_cls: object):
|
|
131
|
+
"""Extract and analyze the docstring for a given API item."""
|
|
132
|
+
docstring = self.get_basic_docstring(api_cls)
|
|
133
|
+
self.analyse_docstring(api_path, api_item_type, docstring)
|
|
134
|
+
|
|
135
|
+
def analyse_docstring(self, api_path: str, api_item_type: str, docstring: str):
|
|
136
|
+
"""Analyze a docstring and categorize it as clean, dubious, or empty."""
|
|
137
|
+
|
|
138
|
+
# Check for empty docstrings first (case-sensitive)
|
|
139
|
+
for pattern in _empty_patterns:
|
|
140
|
+
if pattern.match(docstring):
|
|
141
|
+
self.empty[api_path] = (api_item_type, docstring)
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
# Check for dubious patterns
|
|
145
|
+
# TODO just use None?
|
|
146
|
+
fix = _fixed_doc_string(api_item_type, docstring)
|
|
147
|
+
if fix:
|
|
148
|
+
self.dubious[api_path] = (api_item_type, docstring, fix)
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
# If no issues, classify as clean
|
|
152
|
+
self.clean[api_path] = (api_item_type, docstring)
|
|
153
|
+
|
|
154
|
+
def get_basic_docstring(self, api_cls: object):
|
|
155
|
+
"""Extract and clean a class docstring, handling None values."""
|
|
156
|
+
raw_docstring = api_cls.__doc__ or ""
|
|
157
|
+
docstring = "".join(raw_docstring.splitlines()).strip()
|
|
158
|
+
|
|
159
|
+
# Remove parameters section if present
|
|
160
|
+
params_section = " Parameters ---------- "
|
|
161
|
+
idx = docstring.find(params_section)
|
|
162
|
+
return docstring[:idx] if idx != -1 else docstring
|
|
163
|
+
|
|
164
|
+
def show_results(self):
|
|
165
|
+
"""Print a categorized summary of analyzed docstrings."""
|
|
166
|
+
print("\nSummary:\n")
|
|
167
|
+
for label, result in [
|
|
168
|
+
("Empty", self.empty),
|
|
169
|
+
("Dubious", self.dubious),
|
|
170
|
+
("Clean", self.clean),
|
|
171
|
+
]:
|
|
172
|
+
count = len(result)
|
|
173
|
+
print(f"{label}: {count}")
|
|
174
|
+
if label == "Dubious":
|
|
175
|
+
fix_count = len([i for i in result.values() if i[2]])
|
|
176
|
+
print(
|
|
177
|
+
f"\tNumber with/without suggested fixes: {fix_count}/{count - fix_count}"
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
print("\nDetailed results:")
|
|
181
|
+
for label, result in [
|
|
182
|
+
("Empty", self.empty),
|
|
183
|
+
("Dubious", self.dubious),
|
|
184
|
+
("Clean", self.clean),
|
|
185
|
+
]:
|
|
186
|
+
print(f"\n{label} ({len(result)}):")
|
|
187
|
+
for k, (t, s, *extra) in result.items():
|
|
188
|
+
print(f"{k} ({t}):")
|
|
189
|
+
print(f"\t{s}")
|
|
190
|
+
|
|
191
|
+
# Suggest fixes for dubious patterns
|
|
192
|
+
if label == "Dubious" and extra:
|
|
193
|
+
suggested = extra[0]
|
|
194
|
+
if suggested:
|
|
195
|
+
print(f"\tSuggested Fix: {suggested}")
|
|
196
|
+
else:
|
|
197
|
+
print("\tNo suggested fix")
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def show_all_doc_strings(api_path: str, api_item_type: str, api_cls: object):
|
|
201
|
+
"""Prints all docstrings for debugging."""
|
|
202
|
+
indent = " " * (api_path.count("."))
|
|
203
|
+
bullet = f"{indent}-"
|
|
204
|
+
|
|
205
|
+
print(f"{bullet} item: {api_path.split('.')[-1]}")
|
|
206
|
+
print(f"{bullet} type: {api_item_type}")
|
|
207
|
+
print(f"{bullet} docstring: {api_cls.__doc__}")
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
if __name__ == "__main__":
|
|
211
|
+
import argparse
|
|
212
|
+
import importlib
|
|
213
|
+
|
|
214
|
+
from ansys.fluent.core.codegen import walk_api
|
|
215
|
+
|
|
216
|
+
# Sample Usage
|
|
217
|
+
#
|
|
218
|
+
# Whole Fluent settings API check with default settings
|
|
219
|
+
# >>> python src\ansys\fluent\core\solver\_docstrings.py --api-version=252
|
|
220
|
+
|
|
221
|
+
parser = argparse.ArgumentParser()
|
|
222
|
+
parser.add_argument("--cls", dest="api_cls", type=str, default="root")
|
|
223
|
+
parser.add_argument("--path", dest="api_path", type=str, default="")
|
|
224
|
+
parser.add_argument("--output-type", dest="output_type", type=str, default="stats")
|
|
225
|
+
parser.add_argument("--api-version", dest="api_version", type=str, default="")
|
|
226
|
+
|
|
227
|
+
args = parser.parse_args()
|
|
228
|
+
|
|
229
|
+
api_cls = args.api_cls
|
|
230
|
+
api_path = args.api_path
|
|
231
|
+
output_type = args.output_type
|
|
232
|
+
api_version = args.api_version
|
|
233
|
+
|
|
234
|
+
mod = importlib.import_module(
|
|
235
|
+
name=f"ansys.fluent.core.generated.solver.settings_{api_version}"
|
|
236
|
+
)
|
|
237
|
+
cls = getattr(mod, api_cls)
|
|
238
|
+
|
|
239
|
+
if output_type == "show":
|
|
240
|
+
walk_api.walk_api(cls, show_all_doc_strings, api_path)
|
|
241
|
+
elif output_type == "stats":
|
|
242
|
+
analysis = _DocStringAnalysis()
|
|
243
|
+
walk_api.walk_api(cls, analysis.analyse, api_path)
|
|
244
|
+
analysis.show_results()
|
|
@@ -24,45 +24,40 @@
|
|
|
24
24
|
|
|
25
25
|
import difflib
|
|
26
26
|
from functools import partial
|
|
27
|
-
from typing import Any,
|
|
27
|
+
from typing import Any, Iterable
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
def closest_allowed_names(trial_name: str, allowed_names: str) ->
|
|
30
|
+
def closest_allowed_names(trial_name: str, allowed_names: Iterable[str]) -> list[str]:
|
|
31
31
|
"""Checks if the 'trail_name' is closely matching the 'allowed_names'."""
|
|
32
32
|
f = partial(difflib.get_close_matches, trial_name, allowed_names)
|
|
33
33
|
return f(cutoff=0.6, n=5) or f(cutoff=0.3, n=1)
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
def allowed_name_error_message(
|
|
37
|
-
allowed_values: Any | None = None,
|
|
37
|
+
allowed_values: Iterable[Any] | None = None,
|
|
38
38
|
context: str | None = None,
|
|
39
|
-
trial_name:
|
|
39
|
+
trial_name: Any | None = None,
|
|
40
40
|
message: str | None = None,
|
|
41
|
-
search_results: list | None = None,
|
|
42
41
|
) -> str:
|
|
43
42
|
"""Provide an error message with the closest names matching the 'trial_name' from
|
|
44
43
|
the 'allowed_values' list."""
|
|
45
44
|
if not message:
|
|
46
45
|
message = f"'{context}' has no attribute '{trial_name}'"
|
|
47
46
|
message += ".\n"
|
|
48
|
-
matches = None
|
|
49
47
|
if allowed_values:
|
|
50
|
-
|
|
48
|
+
matches = None
|
|
49
|
+
if all(isinstance(item, str) for item in allowed_values) and allowed_values:
|
|
51
50
|
matches = closest_allowed_names(trial_name, allowed_values)
|
|
52
51
|
if matches:
|
|
53
52
|
message += f"The most similar names are: {', '.join(matches)}."
|
|
54
53
|
else:
|
|
55
54
|
message += f"The allowed values are: {allowed_values}."
|
|
56
|
-
elif search_results:
|
|
57
|
-
message = message + "\nThe most similar API names are:\n"
|
|
58
|
-
for search_result in search_results:
|
|
59
|
-
message += search_result + "\n"
|
|
60
55
|
|
|
61
56
|
return message
|
|
62
57
|
|
|
63
58
|
|
|
64
59
|
def allowed_values_error(
|
|
65
|
-
context: str, trial_name: str, allowed_values:
|
|
60
|
+
context: str, trial_name: str, allowed_values: Iterable[str | int]
|
|
66
61
|
) -> ValueError:
|
|
67
62
|
"""Provide an error message for disallowed values."""
|
|
68
63
|
return ValueError(
|