diff --git a/doc/changelog.d/4932.added.md b/doc/changelog.d/4932.added.md new file mode 100644 index 00000000000..056270e7e35 --- /dev/null +++ b/doc/changelog.d/4932.added.md @@ -0,0 +1 @@ +Call .name() if it's a SettingsBase + Path support diff --git a/src/ansys/fluent/core/codegen/settingsgen.py b/src/ansys/fluent/core/codegen/settingsgen.py index a8c95626685..6b088b7b01a 100644 --- a/src/ansys/fluent/core/codegen/settingsgen.py +++ b/src/ansys/fluent/core/codegen/settingsgen.py @@ -190,13 +190,13 @@ def _get_unique_name(name): "Integer": "int", "Real": "float | str", "String": "str", - "Filename": "str", + "Filename": "PathType", "BooleanList": "list[bool]", "IntegerList": "list[int]", "RealVector": "tuple[float | str, float | str, float | str]", "RealList": "list[float | str]", "StringList": "list[str]", - "FilenameList": "list[str]", + "FilenameList": "list[PathType]", } @@ -386,6 +386,7 @@ def generate(version: str, static_infos: dict, verbose: bool = False) -> None: header.write("# This is an auto-generated file. DO NOT EDIT!\n") header.write("#\n") header.write("\n") + header.write("from ansys.fluent.core._types import PathType\n") header.write("from ansys.fluent.core.solver.flobject import *\n\n") header.write("from ansys.fluent.core.solver.flobject import (\n") header.write(" ExposureLevel,\n") diff --git a/src/ansys/fluent/core/solver/flobject.py b/src/ansys/fluent/core/solver/flobject.py index db4232eaaff..cbe08df28a9 100644 --- a/src/ansys/fluent/core/solver/flobject.py +++ b/src/ansys/fluent/core/solver/flobject.py @@ -42,7 +42,7 @@ from __future__ import annotations import collections -from collections.abc import Callable +from collections.abc import Callable, Sequence from contextlib import contextmanager, nullcontext, suppress from enum import Enum import fnmatch @@ -62,6 +62,7 @@ Generic, Iterable, NewType, + Protocol, TypeVar, Union, _eval_type, @@ -71,6 +72,9 @@ import warnings import weakref +from typing_extensions import override + +from ansys.fluent.core._types import PathType from ansys.fluent.core.pyfluent_warnings import ( PyFluentDeprecationWarning, PyFluentUserWarning, @@ -813,24 +817,36 @@ def units(self) -> str | None: return get_si_unit_for_fluent_quantity(quantity) +class SettingsBaseWithName(Protocol): # pylint: disable=missing-class-docstring + __class__: type["SettingsBase"] # pyright: ignore[reportIncompatibleMethodOverride] + name: Callable[[], str] + + class Textual(Property): """Exposes attribute accessor on settings object - specific to string objects.""" - def set_state(self, state: StateT | None = None, **kwargs): + def set_state( + self, + state: str | VariableDescriptor | SettingsBaseWithName | None = None, + **kwargs, + ): """Set the state of the object. Parameters ---------- state - Either str or VariableDescriptor. + Either str, settings object with a ``name()`` method or VariableDescriptor. kwargs : Any Keyword arguments. Raises ------ TypeError - If state is not a string. + If state is not an appropriate type. """ + if isinstance(state, SettingsBase) and hasattr(state, "name"): + state = state.name() + allowed_types = (str, VariableDescriptor) if not isinstance(state, allowed_types): @@ -1088,6 +1104,12 @@ class Filename(SettingsBase[str], Textual): _state_type = str + @override + def set_state(self, state: PathType | None = None, **kwargs): + if state is not None: + state = os.fspath(state) + return super().set_state(state, **kwargs) + def file_purpose(self): """Specifies whether this file is used as input or output by Fluent.""" return self.get_attr(_InlineConstants.file_purpose) @@ -1098,6 +1120,15 @@ class FilenameList(SettingsBase[StringListType], Textual): _state_type = StringListType + @override + def set_state(self, state: Sequence[PathType] | None = None, **kwargs): + if state is not None: + if isinstance(state, (str, bytes)): + state = [os.fspath(state)] + else: + state = [os.fspath(path) for path in state] + return super().set_state(state, **kwargs) + def file_purpose(self): """Specifies whether this file is used as input or output by Fluent.""" return self.get_attr(_InlineConstants.file_purpose) @@ -1171,6 +1202,24 @@ class StringList(SettingsBase[StringListType], Textual): _state_type = StringListType + @override + def set_state( + self, + state: Sequence[str | SettingsBaseWithName | VariableDescriptor] | None = None, + **kwargs, + ): + if isinstance(state, Sequence): + state = [ + ( + entry.name() + if isinstance(entry, SettingsBase) and hasattr(entry, "name") + else entry + ) + for entry in state + ] + + return super().set_state(state, **kwargs) + class BooleanList(SettingsBase[BoolListType], Property): """A ``BooleanList`` object representing a Boolean list setting.""" diff --git a/tests/test_settings_api.py b/tests/test_settings_api.py index 087d8739d71..128e1c83195 100644 --- a/tests/test_settings_api.py +++ b/tests/test_settings_api.py @@ -21,6 +21,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from pathlib import Path +import tempfile from collections import UserList import warnings @@ -899,6 +901,50 @@ def test_read_only_command_execution(mixing_elbow_case_session): contour.display() +def test_setting_base_with_name(mixing_elbow_settings_session): + solver = mixing_elbow_settings_session + + with solver: + report_def = solver.settings.solution.report_definitions.surface.create( + "test-report-def" + ) + report_def.report_type = "surface-areaavg" + report_def.field = "temperature" + report_def.surface_names = ["hot-inlet"] + + report_file_obj = solver.settings.solution.monitor.report_files.create( + "test-file" + ) + report_file_obj.report_defs = [report_def] + + assert report_file_obj.report_defs() == [report_def.name()] + + +def test_filename_with_pathlib_path(mixing_elbow_settings_session): + solver = mixing_elbow_settings_session + + with tempfile.TemporaryDirectory() as tmpdir, solver: + path_obj = Path(tmpdir) / "test_output.dat" + path_str = str(path_obj) + + report_def = solver.settings.solution.report_definitions.surface.create( + "test-path-report" + ) + report_def.report_type = "surface-areaavg" + report_def.field = "pressure" + report_def.surface_names = ["cold-inlet"] + + report_file = solver.settings.solution.monitor.report_files.create( + "test-path-file" + ) + report_file.report_defs = "test-path-report" + + report_file.file_name = path_obj + + result_path = report_file.file_name() + assert result_path == path_str + + def test_copy_accepts_sequence_types(mixing_elbow_settings_session: Solver): solver = mixing_elbow_settings_session hot_inlet = solver.settings.setup.boundary_conditions.velocity_inlet["hot-inlet"]