-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor dates handling #121
Conversation
also rename target_date -> target_cycle
bc2a275
to
561a5a8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the design, there are some things to still address though.
@field_validator("type") | ||
@classmethod | ||
def is_file_or_dir(cls, value: str) -> str: | ||
""".""" | ||
valid_types = ("file", "dir") | ||
if value not in valid_types: | ||
msg = f"Must be one of {valid_types}" | ||
raise ValueError(msg) | ||
return value | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This validator is no longer useful and was removed in #120 .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this went unnoticed during rebasing because I changed the name of the file and #120 got merged after.
msg = f"For cycle {self.name!r} the period {self.period!r} is negative or zero." | ||
raise ValueError(msg) | ||
return self | ||
cycling: Annotated[Cycling, BeforeValidator(select_cycling)] = OneOff() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a great way of selecting which subclass to parse to 👍.
src/sirocco/pretty_print.py
Outdated
... cycle_point=DateCyclePoint( | ||
... start_date=datetime(1000, 1, 1), | ||
... stop_date=datetime(1000, 1, 2), | ||
... begin_date=datetime(1000, 1, 1), | ||
... end_date=datetime(1000, 1, 2), | ||
... ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It just occurred to me while reading this that I didn't see an explanation anywhere for the meaning of start_date / stop_date
vs begin_date / end_date
. I assume one of these pairs refers to the overall experiment and one to the cycle?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, exactly. It's missing some docstring. I followed the ICON convention for overall start/stop and then chose the other available pair, begin/end, for the current cycle. Currently, we set ICON start and stop at each cycle so I needed this info to be carried along. And it's very likely that other tasks will want to access begin/end dates somehow (#61 and #85)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work, @leclairm, thanks a lot! Both, code and YAML files now read much cleaner. LGTM (looks good to me let's get this merged)!
from __future__ import annotations | ||
|
||
from abc import ABC, abstractmethod | ||
from collections.abc import Iterator # noqa: TCH003 needed for pydantic |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rules should be TC002 and TC003, no?
https://docs.astral.sh/ruff/rules/typing-only-third-party-import/
Possibly, we could also add them here if they are relevant in more places as we use pydantic throughout:
Lines 71 to 75 in bb303d0
[tool.ruff.lint] | |
ignore = [ | |
"TID252", # prefer relative import over absolute | |
"TRY003", # write custom error messages for formatting | |
] |
But not sure yet 🤔
case Cycling(): | ||
return spec | ||
case dict(): | ||
if spec.keys() != {"start_date", "stop_date", "period"}: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if spec.keys() != {"start_date", "stop_date", "period"}: | |
if set(spec.keys()) != {"start_date", "stop_date", "period"}: |
Maybe explicitly convert to a set here, so we compare the same objects, and not set
and dict_keys
?
return spec | ||
case dict(): | ||
if not all(k in ("at", "before", "after") for k in spec): | ||
msg = "when keys can only be 'at', 'before' or 'after'" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
msg = "when keys can only be 'at', 'before' or 'after'" | |
msg = "'when' keys can only be 'at', 'before' or 'after'" |
case TargetCycle(): | ||
return spec | ||
case dict(): | ||
if tuple(spec.keys()) not in (("date",), ("lag",)): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if tuple(spec.keys()) not in (("date",), ("lag",)): | |
if not (("date" in spec) ^ ("lag" in spec)): |
Using XOR? I suppose there can be no other keys?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's actually making sure there are no other keys.
target_cycle: Annotated[TargetCycle, BeforeValidator(select_target_cycle)] = NoTargetCycle() | ||
when: Annotated[When, BeforeValidator(select_when)] = AnyWhen() | ||
parameters: Annotated[dict[str, Literal["all", "single"]], BeforeValidator(check_parameters_spec)] = {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice to read :3
@@ -613,6 +566,20 @@ def get_plugin_from_named_base_model( | |||
] | |||
|
|||
|
|||
def check_parameters_lists(data: Any) -> dict[str, list]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can move all the pydantic validators that are now defined as standalone functions into a dedicated file validators.py
? Kind of separation of concerns (ofc I know, though, that the validators are connected to the Data models). Just proposing this bc I found it a bit difficult to go through this file, switching between BaseModel
derived classes that define our data models, and functions that do some actions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like that to happen, but in a different PR to avoid conflicts right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I had the same thought while implementing. I even started by putting some validators in _utlis.py
but didn't want to go all the way. Actually _utils.py
is only validators.
Fix #119, #97 and incidentally #108
Short summary: essentially remove as many
None
as possible from date related attributes in order to make the code more understandable.Methods
None
cycling
andtarget_cycle
keywords in the config formatParts to treat
_WhenBasemodel