-
Notifications
You must be signed in to change notification settings - Fork 67
feat: add get and create methods and **kwarg support for create #4866
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 9 commits
df68d26
61d4c66
f274df3
4e5c6be
269c59e
c40e9ed
91882b2
9430310
c38d797
9d6dacc
73cafbe
d42a24a
779bb60
51664a4
c3c5aee
20ce7f9
ae11446
fedf799
fb48b06
a81582c
dae1864
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Add get and create methods and **kwarg support for create | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,7 +34,6 @@ | |
| from ansys.fluent.core.utils.fluent_version import all_versions | ||
|
|
||
| _PY_FILE = config.codegen_outdir / "solver" / "settings_builtin.py" | ||
| _PYI_FILE = config.codegen_outdir / "solver" / "settings_builtin.pyi" | ||
|
|
||
|
|
||
| def _get_settings_root(version: str): | ||
|
|
@@ -74,6 +73,21 @@ def _get_named_objects_in_path(root, path, kind): | |
| return named_objects, final_type | ||
|
|
||
|
|
||
| def _has_create_method(root, path): | ||
| """Check if a setting object has a create method.""" | ||
| try: | ||
| cls = root | ||
| comps = path.split(".") | ||
| for comp in comps: | ||
| cls = cls._child_classes[comp] | ||
| # Check if the class has 'create' in its child classes or command names | ||
| return "create" in getattr(cls, "_child_classes", {}) or "create" in getattr( | ||
| cls, "command_names", [] | ||
|
Comment on lines
+93
to
+94
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please can we comment why we check both of these. Otherwise, it looks speculative. |
||
| ) | ||
| except (KeyError, AttributeError): | ||
| return False | ||
|
|
||
|
|
||
| def generate(version: str): | ||
| """Generate builtin setting classes.""" | ||
| print("Generating builtin settings...") | ||
|
|
@@ -142,25 +156,82 @@ def generate(version: str): | |
| ) | ||
| f.write(" return instance(**kwargs)\n\n") | ||
|
|
||
| # Generate version-specific .pyi files | ||
| _PYI_FILE = ( | ||
| config.codegen_outdir / "solver" / f"settings_builtin_{version.number}.pyi" | ||
| ) | ||
| with open(_PYI_FILE, "w") as f: | ||
|
Gobot1234 marked this conversation as resolved.
Outdated
|
||
| for version in FluentVersion: | ||
| f.write( | ||
| f"from ansys.fluent.core.generated.solver.settings_{version.number} import root as settings_root_{version.number}\n" | ||
| ) | ||
| # Import base classes | ||
| f.write( | ||
| "from ansys.fluent.core.solver.settings_builtin_bases import _SingletonSetting, _CreatableNamedObjectSetting, _NonCreatableNamedObjectSetting, _CommandSetting\n" | ||
| ) | ||
| # Import version-specific root for type hints | ||
| f.write( | ||
| f"from ansys.fluent.core.generated.solver.settings_{version.number} import root as settings_root_{version.number}\n" | ||
| ) | ||
| f.write("\n\n") | ||
| for name, v in DATA.items(): | ||
| kind, path = v | ||
| f.write(f"class {name}(\n") | ||
| if isinstance(path, str): | ||
| path = {all_versions(): path} | ||
| for version_set, p in path.items(): | ||
| if kind == "NamedObject": | ||
| p = f"{p}.child_object_type" | ||
| for v in reversed(list(version_set)): | ||
| f.write(f" type(settings_root_{v.number}.{p}),\n") | ||
| f.write("): ...\n\n") | ||
| if isinstance(path, dict): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not easy to see what the significance is of
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Path here is a dict[FluentVersionSet, str]. It's only used by a small handful of things {
since(FluentVersion.v251): "setup.dynamic_mesh",
},
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it expected to be a
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No it's just a dict when there's a version constraint on the path and the name changes between versions.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, please could you add that as a code comment. |
||
| version_supported = False | ||
| for version_set, p in path.items(): | ||
| if version in version_set: | ||
| path = p | ||
| version_supported = True | ||
| break | ||
| if not version_supported: | ||
| continue | ||
| named_objects, final_type = _get_named_objects_in_path(root, path, kind) | ||
| if kind == "NamedObject": | ||
| kind = f"{final_type}NamedObject" | ||
| path_with_child = f"{path}.child_object_type" | ||
| f.write(f"class {name}(\n") | ||
| f.write(f" _{kind}Setting,\n") | ||
| f.write( | ||
| f" type(settings_root_{version.number}.{path_with_child}),\n" | ||
| ) | ||
| f.write("):\n") | ||
| if final_type == "Creatable": | ||
| f.write( | ||
| f" create = settings_root_{version.number}.{path}.create\n" | ||
| ) | ||
| else: | ||
| f.write(" ...\n") | ||
| f.write("\n") | ||
| else: | ||
| # For Singleton and Command types | ||
| f.write(f"class {name}(\n") | ||
| f.write(f" _{kind}Setting,\n") | ||
| f.write(f" type(settings_root_{version.number}.{path}),\n") | ||
| f.write("):\n") | ||
| # Check if singleton has create method | ||
| if kind == "Singleton" and _has_create_method(root, path): | ||
| f.write( | ||
| f" create = settings_root_{version.number}.{path}.create\n" | ||
| ) | ||
| else: | ||
| f.write(" ...\n") | ||
| f.write("\n") | ||
|
|
||
|
|
||
| def generate_main_pyi(version: str): | ||
| """Generate main settings_builtin.pyi that imports from a specific version.""" | ||
| _MAIN_PYI_FILE = config.codegen_outdir / "solver" / "settings_builtin.pyi" | ||
| version_obj = FluentVersion(version) | ||
| with open(_MAIN_PYI_FILE, "w") as f: | ||
|
Gobot1234 marked this conversation as resolved.
|
||
| f.write(f"# Re-export from version {version}\n") | ||
|
Gobot1234 marked this conversation as resolved.
Outdated
|
||
| f.write( | ||
| f"from ansys.fluent.core.generated.solver.settings_builtin_{version_obj.number} import *\n" | ||
| ) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| version = "261" # for development | ||
| generate(version) | ||
| # Generate for all available versions | ||
| versions = sorted([v.number for v in all_versions()]) | ||
|
Comment on lines
+335
to
+336
|
||
| for version in versions: | ||
| try: | ||
| generate(str(version)) | ||
| except Exception as e: | ||
| print(f"Failed to generate for version {version}: {e}") | ||
| # Generate main .pyi that imports from the latest version | ||
| generate_main_pyi(str(versions[-1])) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1389,7 +1389,7 @@ def to_python_keys(cls, value): | |
| query_names = [] | ||
| _child_aliases = {} | ||
|
|
||
| def _create_child_object(self, cname: str): | ||
| def _create_child_object(self, cname: str, **kwargs: Any): | ||
| ret = self._objects.get(cname) | ||
| if not ret: | ||
| cls = self.__class__.child_object_type | ||
|
|
@@ -1399,6 +1399,7 @@ def _create_child_object(self, cname: str): | |
| "rename", | ||
| types.MethodType(lambda obj, name: _rename(self, name, cname), ret), | ||
| ) | ||
| ret.set_state(kwargs) | ||
|
|
||
| return ret | ||
|
|
||
| def _update_objects(self): | ||
|
|
@@ -1451,7 +1452,7 @@ def get_object_names(self): | |
| obj_names_list = obj_names if isinstance(obj_names, list) else list(obj_names) | ||
| return obj_names_list | ||
|
|
||
| def __getitem__(self, name: str) -> ChildTypeT: | ||
| def __getitem__(self, name: str, **kwargs: Any) -> ChildTypeT: | ||
|
Gobot1234 marked this conversation as resolved.
Gobot1234 marked this conversation as resolved.
|
||
| if name not in self.get_object_names(): | ||
| if self.flproxy.has_wildcard(name): | ||
| child_cls = self.__class__.child_object_type | ||
|
|
@@ -1473,7 +1474,7 @@ def __getitem__(self, name: str) -> ChildTypeT: | |
|
|
||
| obj = self._objects.get(name) | ||
| if not obj: | ||
| obj = self._create_child_object(name) | ||
| obj = self._create_child_object(name, **kwargs) | ||
|
|
||
| return obj | ||
|
|
||
| def get(self, name: str) -> ChildTypeT: | ||
|
|
@@ -1727,7 +1728,7 @@ def _get_new_keywords(obj, *args, **kwds): | |
| newkwds[alias] = v | ||
| elif k in obj.argument_names: | ||
| newkwds[k] = v | ||
| else: | ||
| elif k not in obj.get_active_child_names(): | ||
|
||
| unknown_keywords.add(k) | ||
| for k in unknown_keywords: | ||
| # Noisily ignore unknown keywords | ||
|
|
@@ -1843,7 +1844,8 @@ def execute_command(self, *args, **kwds): | |
| and isinstance(self._parent, NamedObject) | ||
| and ret in self._parent | ||
| ): | ||
|
Gobot1234 marked this conversation as resolved.
|
||
| return self._parent[ret] | ||
| child_attributes = {k: v for k, v in kwds.items() if k != "name"} | ||
| return self._parent.__getitem__(ret, **child_attributes) | ||
| return_t = getattr(self, "return_type", None) | ||
| if return_t: | ||
| base_t = _baseTypes.get(return_t) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,7 +22,9 @@ | |
|
|
||
| """Base classes for builtin setting classes.""" | ||
|
|
||
| from typing import Protocol, runtime_checkable | ||
| from typing import Any, Protocol, runtime_checkable | ||
|
|
||
| from typing_extensions import Self | ||
|
|
||
| from ansys.fluent.core.solver.flobject import ( | ||
|
Comment on lines
+27
to
30
|
||
| InactiveObjectError, | ||
|
|
@@ -82,19 +84,25 @@ def _get_settings_obj(settings_root, builtin_settings_obj): | |
| return obj | ||
|
|
||
|
|
||
| def _initialize_settings(instance, defaults: dict, settings_source=None, **kwargs): | ||
| active_session = _get_active_session() | ||
| instance.__dict__.update(defaults | kwargs) | ||
| if settings_source is not None: | ||
| instance.settings_source = settings_source | ||
| elif active_session: | ||
| instance.settings_source = active_session | ||
| class _SettingsObjectMixin: | ||
| def __init__( | ||
| self, | ||
| defaults: dict, | ||
| settings_source: SettingsBase | Solver | None = None, | ||
| **kwargs: Any, | ||
| ): | ||
| active_session = _get_active_session() | ||
| self.__dict__.update(defaults | kwargs) | ||
| if settings_source is not None: | ||
| self.settings_source = settings_source | ||
| elif active_session: | ||
| self.settings_source = active_session | ||
|
|
||
|
|
||
| class _SingletonSetting: | ||
| class _SingletonSetting(_SettingsObjectMixin): | ||
| # Covers groups, named-object containers and commands. | ||
| def __init__(self, settings_source: SettingsBase | Solver | None = None, **kwargs): | ||
| _initialize_settings(self, {"settings_source": None}, settings_source, **kwargs) | ||
| super().__init__({"settings_source": None}, settings_source, **kwargs) | ||
|
|
||
| def __setattr__(self, name, value): | ||
| if name == "settings_source": | ||
|
|
@@ -107,12 +115,12 @@ def __setattr__(self, name, value): | |
| super().__setattr__(name, value) | ||
|
|
||
|
|
||
| class _NonCreatableNamedObjectSetting: | ||
| class _NonCreatableNamedObjectSetting(_SettingsObjectMixin): | ||
| def __init__( | ||
| self, name: str, settings_source: SettingsBase | Solver | None = None, **kwargs | ||
| ): | ||
| _initialize_settings( | ||
| self, {"settings_source": None, "name": name}, settings_source, **kwargs | ||
| super().__init__( | ||
| {"settings_source": None, "name": name}, settings_source, **kwargs | ||
| ) | ||
|
|
||
| def __setattr__(self, name, value): | ||
|
|
@@ -126,8 +134,27 @@ def __setattr__(self, name, value): | |
| else: | ||
| super().__setattr__(name, value) | ||
|
|
||
|
|
||
| class _CreatableNamedObjectSetting: | ||
| @classmethod | ||
| def get( | ||
| cls, | ||
| settings_source: SettingsBase | Solver | None = None, | ||
| /, | ||
| *, | ||
| name: str, | ||
| ) -> Self: | ||
| """Get and return the singleton instance of this object in Fluent. | ||
|
Gobot1234 marked this conversation as resolved.
Outdated
|
||
|
|
||
| Parameters | ||
| ---------- | ||
| settings_source | ||
| Something with a ``settings`` attribute. If omitted the active session is assumed from the :func:`using` context manager. | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't use assumed here |
||
| name | ||
| Name of the object to get, if applicable, can be a wildcard pattern. | ||
| """ | ||
| return cls(settings_source=settings_source, name=name) | ||
|
|
||
|
|
||
| class _CreatableNamedObjectSetting(_SettingsObjectMixin): | ||
| def __init__( | ||
| self, | ||
| settings_source: SettingsBase | Solver | None = None, | ||
|
|
@@ -137,8 +164,7 @@ def __init__( | |
| ): | ||
| if name and new_instance_name: | ||
| raise ValueError("Cannot specify both name and new_instance_name.") | ||
| _initialize_settings( | ||
| self, | ||
| super().__init__( | ||
| { | ||
| "settings_source": None, | ||
| "name": name, | ||
|
|
@@ -148,6 +174,52 @@ def __init__( | |
| **kwargs, | ||
| ) | ||
|
|
||
| @classmethod | ||
| def get( | ||
| cls, | ||
| settings_source: SettingsBase | Solver | None = None, | ||
| /, | ||
| *, | ||
| name: str, | ||
| ) -> Self: | ||
| """Get and return the singleton instance of this object in Fluent. | ||
|
||
|
|
||
| Parameters | ||
| ---------- | ||
| settings_source | ||
| Something with a ``settings`` attribute. If omitted the active session is assumed from the :func:`using` context manager. | ||
| name | ||
| Name of the object to get, if applicable, can be a wildcard pattern. | ||
| """ | ||
| return cls(settings_source=settings_source, name=name) | ||
|
|
||
| @classmethod | ||
| def create( | ||
| cls, | ||
| settings_source: SettingsBase | Solver | None = None, | ||
| /, | ||
| name: str | None = None, | ||
| **kwargs: Any, | ||
| ) -> Self: | ||
| """Create and return an instance of this object in Fluent. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| settings_source | ||
| Something with a ``settings`` attribute. If omitted the active session is assumed from the :func:`using` context manager. | ||
| name | ||
| Name of the new object to create. If omitted, a default name will be assigned by Fluent. | ||
| **kwargs | ||
| Additional attributes to set on the created object. This only works for direct value assignments, not for nested objects. | ||
|
Gobot1234 marked this conversation as resolved.
|
||
| """ | ||
| self = cls( | ||
| settings_source=settings_source, | ||
| new_instance_name=name, | ||
| ) | ||
| self.set_state(kwargs) | ||
|
|
||
| return self | ||
|
|
||
| def __setattr__(self, name, value): | ||
| if name == "settings_source": | ||
| settings_root = _get_settings_root(value) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.