Skip to content
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

Improve yaml models: ConfigTasks #122

Merged
merged 11 commits into from
Feb 13, 2025
Merged

Conversation

DropD
Copy link
Collaborator

@DropD DropD commented Feb 11, 2025

Fix #84:

  • Introduce a pydantic validated namelists model ConfigNamelist
  • Refactor _NamedBaseModel key extraction class method to a free function for reuse in ConfigNamelist
  • Simplify ConfigIconTask validator
  • allow _tasks.Task subclasses to hook into how they are created from the equivalent config.ConfigTask subclass.
    • required to convert namelist model to data class in config.ConfigIconTask -> _task.IconTask.

General Improvements:

@DropD DropD requested review from agoscinski and leclairm February 11, 2025 14:00
Comment on lines +105 to +106
if not isinstance(config, models.ConfigIconTask):
raise TypeError
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This check is here to satisfy mypy, because we can not put this as a type hint in the signature (would break Liskof substitution principle -> mypy complains). Not necessary for ShellTask because we really check in Task that the subclasses get the correct types.

Copy link
Collaborator

@GeigerJ2 GeigerJ2 left a comment

Choose a reason for hiding this comment

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

LGTM. With the explanations from the meeting, very clear. Just some comments to aid my own understanding. Thanks!

src/sirocco/core/_tasks/icon_task.py Outdated Show resolved Hide resolved
@@ -93,3 +97,17 @@ def section_index(section_name) -> tuple[str, int | None]:
if m := multi_section_pattern.match(section_name):
return m.group(1), int(m.group(2)) - 1
return section_name, None

@classmethod
def build_from_config(cls: type[Self], config: ConfigTask, **kwargs: Any) -> Self:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Also, just for my understanding: We're supporting Python >= 3.10, but Self type was introduced in 3.11. Is that an issue, or can we use newer Python features in type hinting? According to ChatGPT:

"""
You're correct—Self was introduced in Python 3.11, not 3.12. Since your package claims to support Python 3.10+, using Self directly would be a problem for users on 3.10.

Can you use newer Python concepts in type annotations?
Yes! Starting with PEP 563 (Python 3.7) and PEP 649 (Python 3.10), Python supports postponed evaluation of annotations, meaning type hints are stored as strings instead of being evaluated immediately. This allows you to use newer type hints while keeping compatibility with older versions, as long as they aren't used at runtime.

Solution:
If you add this import at the top of your module:
from __future__ import annotations
Then the Self annotation will be treated as a string ("Self") and won't cause syntax errors in Python 3.10. However, tools like mypy will still understand it.
"""

Copy link
Collaborator Author

@DropD DropD Feb 13, 2025

Choose a reason for hiding this comment

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

GPT is correct in that it was introduced later and in that in our case it works (at runtime) because it is treated as a string. Here's how that applies to Sirocco:

For now, we have set the hatch project to run type analysis on 3.12 and we use the future annotations. Therefore it is type checked as if it was supporting 3.12+ (ok at analysis time), while the interpreter treats all the type hints as just strings (ok at run time).

If we were to lower that to 3.10, we would have to import Self from typing_extensions (making that a dependency), if we choose to keep using it. It would also affect other things (not touched in this PR) like class Store[T], which would have to turn into class Store(typing.Generic[T]). The backports in typing_extensions can catch some of this but not syntax changes like class Store[T] (which I am honestly suprised that it works at runtime). I am also wondering why from typing import Self does not cause an import error in 3.10, but it seems not to.

return data
if len(data) == 1:
key, value = next(iter(data.items()))
match key:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Interesting. Haven't really used such match/case constructs before, but makes the structure clearer than using isinstance.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree. Now, if there was a match expression, the result of which can be assigned to a variable (like in Rust), I would love it even more...

src/sirocco/parsing/yaml_data_models.py Outdated Show resolved Hide resolved
@DropD DropD merged commit d1fe64d into C2SM:main Feb 13, 2025
7 checks passed
@DropD DropD deleted the improve-models-tasks branch February 13, 2025 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Separate namelist validation inConfigIconTask to dedicated namelist class
2 participants