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.

Files changed (77) hide show
  1. ansys/fluent/core/__init__.py +14 -3
  2. ansys/fluent/core/codegen/datamodelgen.py +17 -3
  3. ansys/fluent/core/codegen/settingsgen.py +26 -4
  4. ansys/fluent/core/codegen/walk_api.py +11 -1
  5. ansys/fluent/core/exceptions.py +7 -4
  6. ansys/fluent/core/generated/api_tree/api_objects.json +1 -1
  7. ansys/fluent/core/generated/datamodel_242/meshing_utilities.pyi +990 -0
  8. ansys/fluent/core/generated/datamodel_251/meshing_utilities.pyi +1002 -0
  9. ansys/fluent/core/generated/datamodel_252/meshing.py +7 -0
  10. ansys/fluent/core/generated/datamodel_252/meshing_utilities.pyi +1007 -0
  11. ansys/fluent/core/generated/datamodel_252/preferences.py +132 -3
  12. ansys/fluent/core/generated/fluent_version_252.py +3 -3
  13. ansys/fluent/core/generated/meshing/tui_252.py +111 -12
  14. ansys/fluent/core/generated/solver/settings_222.py +166 -170
  15. ansys/fluent/core/generated/solver/settings_222.pyi +10 -8
  16. ansys/fluent/core/generated/solver/settings_231.py +551 -598
  17. ansys/fluent/core/generated/solver/settings_231.pyi +40 -38
  18. ansys/fluent/core/generated/solver/settings_232.py +689 -736
  19. ansys/fluent/core/generated/solver/settings_232.pyi +39 -37
  20. ansys/fluent/core/generated/solver/settings_241.py +1365 -1439
  21. ansys/fluent/core/generated/solver/settings_241.pyi +1071 -1074
  22. ansys/fluent/core/generated/solver/settings_242.py +2852 -3098
  23. ansys/fluent/core/generated/solver/settings_242.pyi +2059 -2151
  24. ansys/fluent/core/generated/solver/settings_251.py +3656 -3914
  25. ansys/fluent/core/generated/solver/settings_251.pyi +2915 -3008
  26. ansys/fluent/core/generated/solver/settings_252.py +5894 -5707
  27. ansys/fluent/core/generated/solver/settings_252.pyi +4411 -4297
  28. ansys/fluent/core/generated/solver/tui_252.py +205 -25
  29. ansys/fluent/core/get_build_details.py +2 -2
  30. ansys/fluent/core/launcher/container_launcher.py +7 -1
  31. ansys/fluent/core/launcher/fluent_container.py +3 -2
  32. ansys/fluent/core/launcher/pim_launcher.py +7 -1
  33. ansys/fluent/core/launcher/slurm_launcher.py +7 -1
  34. ansys/fluent/core/launcher/standalone_launcher.py +7 -1
  35. ansys/fluent/core/post_objects/meta.py +6 -6
  36. ansys/fluent/core/services/datamodel_se.py +28 -23
  37. ansys/fluent/core/services/field_data.py +17 -5
  38. ansys/fluent/core/session_base_meshing.py +3 -3
  39. ansys/fluent/core/session_meshing.py +4 -4
  40. ansys/fluent/core/session_meshing.pyi +9 -9
  41. ansys/fluent/core/session_pure_meshing.pyi +9 -9
  42. ansys/fluent/core/session_solver.py +1 -1
  43. ansys/fluent/core/session_solver.pyi +5 -5
  44. ansys/fluent/core/solver/_docstrings.py +244 -0
  45. ansys/fluent/core/solver/error_message.py +7 -12
  46. ansys/fluent/core/solver/flobject.py +40 -15
  47. ansys/fluent/core/utils/fluent_version.py +2 -3
  48. ansys/fluent/core/workflow.py +1 -0
  49. {ansys_fluent_core-0.30.dev1.dist-info → ansys_fluent_core-0.30.dev3.dist-info}/METADATA +4 -4
  50. {ansys_fluent_core-0.30.dev1.dist-info → ansys_fluent_core-0.30.dev3.dist-info}/RECORD +76 -73
  51. ansys/fluent/core/_version.py +0 -40
  52. /ansys/fluent/core/generated/datamodel_222/{PartManagement.py → part_management.py} +0 -0
  53. /ansys/fluent/core/generated/datamodel_222/{PMFileManagement.py → pm_file_management.py} +0 -0
  54. /ansys/fluent/core/generated/datamodel_231/{PartManagement.py → part_management.py} +0 -0
  55. /ansys/fluent/core/generated/datamodel_231/{PMFileManagement.py → pm_file_management.py} +0 -0
  56. /ansys/fluent/core/generated/datamodel_231/{solverworkflow.py → solver_workflow.py} +0 -0
  57. /ansys/fluent/core/generated/datamodel_232/{PartManagement.py → part_management.py} +0 -0
  58. /ansys/fluent/core/generated/datamodel_232/{PMFileManagement.py → pm_file_management.py} +0 -0
  59. /ansys/fluent/core/generated/datamodel_232/{solverworkflow.py → solver_workflow.py} +0 -0
  60. /ansys/fluent/core/generated/datamodel_241/{PartManagement.py → part_management.py} +0 -0
  61. /ansys/fluent/core/generated/datamodel_241/{PMFileManagement.py → pm_file_management.py} +0 -0
  62. /ansys/fluent/core/generated/datamodel_241/{solverworkflow.py → solver_workflow.py} +0 -0
  63. /ansys/fluent/core/generated/datamodel_242/{MeshingUtilities.py → meshing_utilities.py} +0 -0
  64. /ansys/fluent/core/generated/datamodel_242/{PartManagement.py → part_management.py} +0 -0
  65. /ansys/fluent/core/generated/datamodel_242/{PMFileManagement.py → pm_file_management.py} +0 -0
  66. /ansys/fluent/core/generated/datamodel_242/{solverworkflow.py → solver_workflow.py} +0 -0
  67. /ansys/fluent/core/generated/datamodel_251/{MeshingUtilities.py → meshing_utilities.py} +0 -0
  68. /ansys/fluent/core/generated/datamodel_251/{PartManagement.py → part_management.py} +0 -0
  69. /ansys/fluent/core/generated/datamodel_251/{PMFileManagement.py → pm_file_management.py} +0 -0
  70. /ansys/fluent/core/generated/datamodel_251/{solverworkflow.py → solver_workflow.py} +0 -0
  71. /ansys/fluent/core/generated/datamodel_252/{MeshingUtilities.py → meshing_utilities.py} +0 -0
  72. /ansys/fluent/core/generated/datamodel_252/{PartManagement.py → part_management.py} +0 -0
  73. /ansys/fluent/core/generated/datamodel_252/{PMFileManagement.py → pm_file_management.py} +0 -0
  74. /ansys/fluent/core/generated/datamodel_252/{solverworkflow.py → solver_workflow.py} +0 -0
  75. /ansys/fluent/core/{systemcoupling.py → system_coupling.py} +0 -0
  76. {ansys_fluent_core-0.30.dev1.dist-info → ansys_fluent_core-0.30.dev3.dist-info}/LICENSE +0 -0
  77. {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
- cached_attrs={},
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.cached_attrs.get(attrib)
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.cached_attrs, attrib),
1080
+ functools.partial(dict.__setitem__, self._cached_attrs, attrib),
1081
1081
  )
1082
- self.cached_attrs[attrib] = cached_val
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", "cached_attrs")
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
- if validate_inputs and not self.is_valid(surface_name):
341
- raise DisallowedValuesError("surface", surface_name, self())
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
- surface_ids.append(surf)
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"MeshingUtilities_{self._version}",
121
+ f"meshing_utilities_{self._version}",
122
122
  CODEGEN_OUTDIR
123
123
  / f"datamodel_{self._version}"
124
- / "MeshingUtilities.py",
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, "meshing_utilities"
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.switched = False
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.switched = True
92
+ self._switched = True
93
93
  return solver_session
94
94
 
95
95
  def __getattribute__(self, item: str):
96
- if item == "switched":
96
+ if item == "_switched":
97
97
  return super(Meshing, self).__getattribute__(item)
98
98
 
99
- if self.switched and item != "exit":
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.datamodel_242.MeshingUtilities import (
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.datamodel_242.PMFileManagement import (
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.datamodel_242.meshing import Root as meshing_root
33
- from ansys.fluent.core.generated.datamodel_242.preferences import (
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.datamodel_242.workflow import Root as workflow_root
37
- from ansys.fluent.core.generated.meshing.tui_242 import main_menu
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.datamodel_242.MeshingUtilities import (
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.datamodel_242.PMFileManagement import (
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.datamodel_242.meshing import Root as meshing_root
33
- from ansys.fluent.core.generated.datamodel_242.preferences import (
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.datamodel_242.workflow import Root as workflow_root
37
- from ansys.fluent.core.generated.meshing.tui_242 import main_menu
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.systemcoupling import SystemCoupling
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.datamodel_242.preferences import (
23
+ from ansys.fluent.core.generated.datamodel_251.preferences import (
24
24
  Root as preferences_root,
25
25
  )
26
- from ansys.fluent.core.generated.datamodel_242.workflow import Root as workflow_root
27
- import ansys.fluent.core.generated.solver.settings_242 as settings_root
28
- from ansys.fluent.core.generated.solver.tui_242 import main_menu
29
- from ansys.fluent.core.systemcoupling import SystemCoupling
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, List
27
+ from typing import Any, Iterable
28
28
 
29
29
 
30
- def closest_allowed_names(trial_name: str, allowed_names: str) -> List[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: str | None = None,
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
- if isinstance(allowed_values, list) and isinstance(allowed_values[0], str):
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: List[str]
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(