diff --git a/doc/changelog.d/4932.added.md b/doc/changelog.d/4932.added.md new file mode 100644 index 000000000000..2ffdae3d5318 --- /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 358134dbdafd..6f0dffee8f83 100644 --- a/src/ansys/fluent/core/codegen/settingsgen.py +++ b/src/ansys/fluent/core/codegen/settingsgen.py @@ -188,13 +188,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]", } @@ -381,6 +381,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(" _ChildNamedObjectAccessorMixin,\n") diff --git a/src/ansys/fluent/core/solver/flobject.py b/src/ansys/fluent/core/solver/flobject.py index bc035d7487f3..941a2350803c 100644 --- a/src/ansys/fluent/core/solver/flobject.py +++ b/src/ansys/fluent/core/solver/flobject.py @@ -40,7 +40,8 @@ from __future__ import annotations -import collections +import collections.abc +from collections.abc import Sequence from contextlib import contextmanager, nullcontext import fnmatch import hashlib @@ -61,6 +62,7 @@ Generic, List, NewType, + Protocol, Tuple, TypeVar, Union, @@ -71,6 +73,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, @@ -673,24 +678,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): @@ -943,11 +960,17 @@ class String(SettingsBase[str], Textual): set_state = Textual.set_state -class Filename(SettingsBase[str], Textual): +class Filename(SettingsBase[PathType], Textual): """A ``Filename`` object representing a file name.""" _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) @@ -958,6 +981,12 @@ class FilenameList(SettingsBase[StringListType], Textual): _state_type = StringListType + @override + def set_state(self, state: Sequence[PathType] | None = None, **kwargs): + if state is not None: + 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) @@ -1031,6 +1060,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 3967e31dbf5d..30f13c6b0e1a 100644 --- a/tests/test_settings_api.py +++ b/tests/test_settings_api.py @@ -20,6 +20,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from pathlib import Path +import tempfile import warnings import pytest @@ -797,7 +799,7 @@ def test_named_object_commands(mixing_elbow_settings_session): solver = mixing_elbow_settings_session inlets = VelocityInlets(solver) inlets.list() - inlets.list_properties(object_name="hot-inlet") + inlets.list_properties(object_name="hot-i nlet") if solver.get_fluent_version() >= FluentVersion.v261: NamedObject.list(inlets) NamedObject.list_properties(inlets, object_name="hot-inlet") @@ -858,3 +860,47 @@ def test_read_only_command_execution(mixing_elbow_case_session): assert contour.display.is_read_only() is True with pytest.raises(ReadOnlyActionError): 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