feat: call .name if it's a SettingsBase + Path support#4932
feat: call .name if it's a SettingsBase + Path support#4932
Conversation
There was a problem hiding this comment.
Pull request overview
This PR aims to reduce “stringly typed” usage in the settings API by allowing string-valued settings to accept a settings object reference and automatically convert it to its .name().
Changes:
- Update
Textual.set_stateto convertSettingsBaseinputs with.name()into a string before setting state. - Introduce a
SettingsBaseWithNameProtocolto reflect the new accepted input shape in type hints. - Add a
Filename.set_stateoverride intending to acceptpathlib.Pathinputs.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if isinstance(state, SettingsBase) and hasattr(state, "name"): | ||
| state = state.name() |
There was a problem hiding this comment.
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.
| override, | ||
| ) |
There was a problem hiding this comment.
typing.override is only available in Python 3.12+, but this project supports Python >=3.10 (pyproject.toml requires-python = ">=3.10"). Importing it from typing will raise ImportError on 3.10/3.11. Use typing_extensions.override (with a dependency) or remove the decorator / guard the import.
| override, | |
| ) | |
| ) | |
| from typing_extensions import override |
| _state_type = str | ||
|
|
||
| @override | ||
| def set_state(self, state: Path | str | None = None, **kwargs): |
There was a problem hiding this comment.
Filename.set_state now accepts a Path, but it forwards the Path directly to Textual.set_state, which only allows str/VariableDescriptor and will raise TypeError. Convert Path to str (or extend the allowed types) before calling super().set_state.
| def set_state(self, state: Path | str | None = None, **kwargs): | |
| def set_state(self, state: Path | str | None = None, **kwargs): | |
| if isinstance(state, Path): | |
| state = str(state) |
| @@ -691,6 +699,9 @@ def set_state(self, state: StateT | None = None, **kwargs): | |||
| TypeError | |||
| If state is not a string. | |||
| """ | |||
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if isinstance(state, SettingsBase) and hasattr(state, "name"): | ||
| state = state.name() |
There was a problem hiding this comment.
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.
| if isinstance(state, SettingsBase) and hasattr(state, "name"): | |
| state = state.name() | |
| name_attr = getattr(state, "name", None) | |
| if callable(name_attr): | |
| state = name_attr() |
|
|
||
|
|
||
| class Filename(SettingsBase[str], Textual): | ||
| class Filename(SettingsBase[PathType], Textual): |
There was a problem hiding this comment.
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.
| class Filename(SettingsBase[PathType], Textual): | |
| class Filename(SettingsBase[str], Textual): |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def set_state(self, state: PathType | None = None, **kwargs): | ||
| if state is not None: | ||
| state = os.fspath(state) | ||
| return super().set_state(state, **kwargs) |
There was a problem hiding this comment.
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.).
Context
Currently you have to retype the name of a setting object if you reference that same object in another place. E.g. think report plots and report files. Closes #4844
Change Summary
A small check to set_state to call .name if it's a SettingsObject.
Rationale
This makes the settings api less stringly typed
Impact
Just flobject and a few tests.
Further Expansion of this could be to type the dependant relations e.g. a report file can only take a report plot object and not another settings object.