Skip to content

Commit

Permalink
Icon namelist non optional (#109)
Browse files Browse the repository at this point in the history
make namelists entry compulsory for ICON tasks. we could remove the `| None` in the namelists type hint by adding kw-only=True to the dataclass. It was then decided to use kw-only=True for all dataclasses.
  • Loading branch information
leclairm authored Jan 30, 2025
1 parent 4cd4c0e commit 2e28ea4
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 25 deletions.
2 changes: 1 addition & 1 deletion src/sirocco/core/_tasks/icon_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from sirocco.parsing._yaml_data_models import ConfigIconTaskSpecs


@dataclass
@dataclass(kw_only=True)
class IconTask(ConfigIconTaskSpecs, Task):
core_namelists: dict[str, f90nml.Namelist] = field(default_factory=dict)

Expand Down
2 changes: 1 addition & 1 deletion src/sirocco/core/_tasks/shell_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
from sirocco.parsing._yaml_data_models import ConfigShellTaskSpecs


@dataclass
@dataclass(kw_only=True)
class ShellTask(ConfigShellTaskSpecs, Task):
pass
10 changes: 5 additions & 5 deletions src/sirocco/core/graph_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
)


@dataclass
@dataclass(kw_only=True)
class GraphItem:
"""base class for Data Tasks and Cycles"""

Expand All @@ -33,13 +33,13 @@ class GraphItem:
coordinates: dict


@dataclass
@dataclass(kw_only=True)
class Data(ConfigBaseDataSpecs, GraphItem):
"""Internal representation of a data node"""

color: ClassVar[str] = field(default="light_blue", repr=False)

available: bool | None = None # must get a default value because of dataclass inheritence
available: bool

@classmethod
def from_config(cls, config: ConfigBaseData, coordinates: dict) -> Self:
Expand All @@ -56,7 +56,7 @@ def from_config(cls, config: ConfigBaseData, coordinates: dict) -> Self:
BoundData: TypeAlias = tuple[Data, str | None]


@dataclass
@dataclass(kw_only=True)
class Task(ConfigBaseTaskSpecs, GraphItem):
"""Internal representation of a task node"""

Expand Down Expand Up @@ -129,7 +129,7 @@ def link_wait_on_tasks(self, taskstore: Store):
)


@dataclass
@dataclass(kw_only=True)
class Cycle(GraphItem):
"""Internal reprenstation of a cycle"""

Expand Down
67 changes: 50 additions & 17 deletions src/sirocco/parsing/_yaml_data_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ def check_period_is_not_negative_or_zero(self) -> ConfigCycle:
return self


@dataclass
@dataclass(kw_only=True)
class ConfigBaseTaskSpecs:
computer: str | None = None
host: str | None = None
Expand Down Expand Up @@ -352,7 +352,7 @@ def from_cli_argument(cls, arg: str) -> ShellCliArgument:
return cls(name, references_data_item, cli_option_of_data_item)


@dataclass
@dataclass(kw_only=True)
class ConfigShellTaskSpecs:
plugin: ClassVar[Literal["shell"]] = "shell"
command: str = ""
Expand Down Expand Up @@ -412,36 +412,69 @@ def parse_cli_arguments(cli_arguments: str) -> list[ShellCliArgument]:
return [ShellCliArgument.from_cli_argument(arg) for arg in ConfigShellTask.split_cli_arguments(cli_arguments)]


@dataclass
@dataclass(kw_only=True)
class ConfigNamelist:
"""Class for namelist specifications"""
"""Class for namelist specifications
- path is the path to the namelist file considered as template
- specs is a dictionnary containing the specifications of parameters
to change in the original namelist file
Example:
>>> path = "/some/path/to/icon.nml"
>>> specs = {
... "first_nml_block": {"first_param": "a string value", "second_param": 0},
... "second_nml_block": {"third_param": False},
... }
>>> config_nml = ConfigNamelist(path=path, specs=specs)
"""

path: Path
specs: dict | None = None


@dataclass
@dataclass(kw_only=True)
class ConfigIconTaskSpecs:
plugin: ClassVar[Literal["icon"]] = "icon"
namelists: dict[str, ConfigNamelist] | None = None
namelists: dict[str, ConfigNamelist]


class ConfigIconTask(ConfigBaseTask, ConfigIconTaskSpecs):
# validation done here and not in ConfigNamelist so that we can still
# import ConfigIconTaskSpecs in core._tasks.IconTask. Hence the iteration
# over the namelists that could be avoided with a more raw pydantic design
"""Class representing an ICON task configuration from a workflow file
Examples:
yaml snippet:
>>> import textwrap
>>> import pydantic_yaml
>>> snippet = textwrap.dedent(
... '''
... ICON:
... plugin: icon
... namelists:
... - path/to/icon_master.namelist
... - path/to/case_nml:
... block_1:
... param_name: param_value
... '''
... )
>>> icon_task_cfg = pydantic_yaml.parse_yaml_raw_as(ConfigIconTask, snippet)
"""

@field_validator("namelists", mode="before")
@classmethod
def check_nml(cls, nml_list: list[Any]) -> ConfigNamelist:
if nml_list is None:
msg = "ICON tasks need namelists, got none"
raise ValueError(msg)
if not isinstance(nml_list, list):
msg = f"expected a list got type {type(nml_list).__name__}"
def check_nmls(cls, nmls: dict[str, ConfigNamelist] | list[Any]) -> dict[str, ConfigNamelist]:
# Make validator idempotent even if not used yet
if isinstance(nmls, dict):
return nmls
if not isinstance(nmls, list):
msg = f"expected a list got type {type(nmls).__name__}"
raise TypeError(msg)
namelists = {}
master_found = False
for nml in nml_list:
for nml in nmls:
msg = f"was expecting a dict of length 1 or a string, got {nml}"
if not isinstance(nml, (str, dict)):
raise TypeError(msg)
Expand All @@ -465,7 +498,7 @@ class DataType(enum.StrEnum):
DIR = enum.auto()


@dataclass
@dataclass(kw_only=True)
class ConfigBaseDataSpecs:
type: DataType
src: str
Expand Down
2 changes: 1 addition & 1 deletion src/sirocco/pretty_print.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from sirocco import core


@dataclasses.dataclass
@dataclasses.dataclass(kw_only=True)
class PrettyPrinter:
"""
Pretty print unrolled workflow graph elements in a reproducible and human readable format.
Expand Down

0 comments on commit 2e28ea4

Please sign in to comment.