Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changelog.d/4932.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Call .name if it's a SettingsBase + Path support
5 changes: 3 additions & 2 deletions src/ansys/fluent/core/codegen/settingsgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]",
}


Expand Down Expand Up @@ -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")
Expand Down
57 changes: 52 additions & 5 deletions src/ansys/fluent/core/solver/flobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -61,6 +62,7 @@
Generic,
List,
NewType,
Protocol,
Tuple,
TypeVar,
Union,
Expand All @@ -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,
Expand Down Expand Up @@ -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.
"""
Comment on lines 694 to 707
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Textual.set_state docstring and Raises section no longer match the accepted inputs: the signature now includes SettingsBaseWithName, and the runtime also accepts VariableDescriptor, but the docs still say "Either str or VariableDescriptor" and "If state is not a string." Update the documentation/error wording to reflect the actual accepted types/behavior.

Copilot uses AI. Check for mistakes.
if isinstance(state, SettingsBase) and hasattr(state, "name"):
state = state.name()
Comment on lines +708 to +709
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New behavior: allowing a SettingsBase instance to be passed into Textual.set_state and converting it via .name() is not covered by the existing flobject unit tests (e.g., tests/test_flobject.py exercises string settings but never passes another settings object). Add a focused test that passes a named settings object and asserts the resulting stored string matches its name, including a negative case where .name() is absent.

Copilot uses AI. Check for mistakes.
Comment on lines +708 to +709
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no tests added to verify that the new feature works correctly. Based on the issue description and PR description, users should now be able to pass settings objects directly (e.g., param_1.report_definition = drag instead of param_1.report_definition = drag.name()). Consider adding tests to verify this functionality works as expected, for example by creating a settings object with a name() method and passing it directly to another setting that expects a string.

Suggested change
if isinstance(state, SettingsBase) and hasattr(state, "name"):
state = state.name()
name_attr = getattr(state, "name", None)
if callable(name_attr):
state = name_attr()

Copilot uses AI. Check for mistakes.

allowed_types = (str, VariableDescriptor)

if not isinstance(state, allowed_types):
Expand Down Expand Up @@ -943,11 +960,17 @@ class String(SettingsBase[str], Textual):
set_state = Textual.set_state


class Filename(SettingsBase[str], Textual):
class Filename(SettingsBase[PathType], Textual):
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generic type parameter has been changed from SettingsBase[str] to SettingsBase[PathType], but this creates a type inconsistency. The get_state() method inherited from SettingsBase is typed to return StateT (which would be PathType here), but Fluent's API actually returns strings, not PathType objects. The _state_type = str on line 967 correctly indicates that Fluent stores strings internally. Consider keeping the generic parameter as SettingsBase[str] since that's what Fluent actually returns, and handle PathType conversion only in set_state() (which is already done). The type hint for set_state can still accept PathType as shown on line 970.

Suggested change
class Filename(SettingsBase[PathType], Textual):
class Filename(SettingsBase[str], Textual):

Copilot uses AI. Check for mistakes.
"""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)
Comment on lines +969 to +972
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Path support added to Filename.set_state() lacks test coverage. Consider adding a test that verifies pathlib.Path objects and other PathLike types can be passed to Filename settings objects. This would ensure the os.fspath() conversion works correctly for different PathType variants (Path objects, strings, bytes, etc.).

Copilot uses AI. Check for mistakes.

def file_purpose(self):
"""Specifies whether this file is used as input or output by Fluent."""
return self.get_attr(_InlineConstants.file_purpose)
Expand All @@ -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)
Expand Down Expand Up @@ -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."""
Expand Down
48 changes: 47 additions & 1 deletion tests/test_settings_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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
Loading