Skip to content

Commit 4f1995c

Browse files
committed
Fix up pyproject model to better handle actions explicitly being given to the nested models
1 parent 69e3f6d commit 4f1995c

File tree

1 file changed

+21
-15
lines changed

1 file changed

+21
-15
lines changed

streamdeck/models/configs.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
ImportString,
1111
ValidationInfo,
1212
field_validator,
13+
model_validator,
1314
)
1415

1516
from streamdeck.actions import ActionBase
@@ -33,14 +34,32 @@ def validate_from_toml_file(cls, filepath: Path, action_scripts: list[str] | Non
3334
with filepath.open("rb") as f:
3435
pyproject_configs = toml.load(f)
3536

36-
# Pass the action scripts to the context dictionary if they are provided, so they can be used in the before-validater for the nested StreamDeckToolConfig model.
37+
# Pass the action scripts to the context dictionary if they are provided,
38+
# so they can be used in the before-validater for the nested StreamDeckToolConfig model.
3739
ctx = {"action_scripts": action_scripts} if action_scripts else None
3840

3941
# Return the loaded PyProjectConfigs model instance.
4042
return cls.model_validate(pyproject_configs, context=ctx)
4143

44+
@model_validator(mode="before")
45+
@classmethod
46+
def overwrite_action_scripts(cls, data: object, info: ValidationInfo) -> object:
47+
"""If action scripts were provided as a context variable, overwrite the action_scripts field in the PyProjectConfigs model."""
48+
context = info.context
49+
50+
# If no action scripts were provided, return the data as-is.
51+
if context is None or "action_scripts" not in context:
52+
return data
53+
54+
# If data isn't a dict as expected, let Pydantic's validation handle them as usual in its next validations step.
55+
if isinstance(data, dict):
56+
# We also need to ensure the "tool" and "streamdeck" sections exist in the data dictionary in case they were not defined in the PyProject.toml file.
57+
data.setdefault("tool", {}).setdefault("streamdeck", {})["action_scripts"] = context["action_scripts"]
58+
59+
return data
60+
4261
@property
43-
def streamdeck_plugin_actions(self) -> Generator[type[ActionBase], Any, None]:
62+
def streamdeck_plugin_actions(self) -> Generator[ActionBase, Any, None]:
4463
"""Reach into the [tool.streamdeck] section of the PyProject.toml file and yield the plugin's actions configured by the developer."""
4564
for loaded_action_script in self.tool.streamdeck.action_script_modules:
4665
for object_name in dir(loaded_action_script):
@@ -72,19 +91,6 @@ class StreamDeckToolConfig(BaseModel, arbitrary_types_allowed=True):
7291
This field is filtered to only include objects that are subclasses of ActionBase (as well as the built-in magic methods and attributes typically found in a module).
7392
"""
7493

75-
@field_validator("action_script_modules", mode="before")
76-
@classmethod
77-
def overwrite_action_scripts_with_user_provided_data(cls, value: list[str], info: ValidationInfo) -> list[str]:
78-
"""Overwrite the list of action script modules with the user-provided data.
79-
80-
NOTE: This is a before-validator that runs before the next field_validator method on the same field.
81-
"""
82-
# If the user provided action_scripts to load, use that instead of the value from the PyProject.toml file.
83-
if info.context is not None and "action_scripts" in info.context:
84-
return info.context["action_scripts"]
85-
86-
return value
87-
8894
@field_validator("action_script_modules", mode="after")
8995
@classmethod
9096
def filter_module_objects(cls, value: list[ModuleType]) -> list[ModuleType]:

0 commit comments

Comments
 (0)