diff --git a/pyproject.toml b/pyproject.toml index d5863fc6..2fcaa693 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,6 +75,8 @@ path = "src/sirocco/__init__.py" extra-dependencies = [ "ipdb" ] +default-args = [] +extra-args = ["--doctest-modules"] [[tool.hatch.envs.hatch-test.matrix]] python = ["3.12"] diff --git a/src/sirocco/parsing/_yaml_data_models.py b/src/sirocco/parsing/_yaml_data_models.py index 976f7fa7..70642a20 100644 --- a/src/sirocco/parsing/_yaml_data_models.py +++ b/src/sirocco/parsing/_yaml_data_models.py @@ -8,7 +8,15 @@ from isoduration import parse_duration from isoduration.types import Duration # pydantic needs type # noqa: TCH002 -from pydantic import BaseModel, ConfigDict, Discriminator, Field, Tag, field_validator, model_validator +from pydantic import ( + BaseModel, + ConfigDict, + Discriminator, + Field, + Tag, + field_validator, + model_validator, +) from sirocco.parsing._utils import TimeUtils @@ -387,6 +395,39 @@ def get_plugin_from_named_base_model(data: dict) -> str: class ConfigWorkflow(BaseModel): + """ + The root of the configuration tree. + + Examples: + + minimal yaml to generate: + + >>> import textwrap + >>> import pydantic_yaml + >>> config = textwrap.dedent( + ... ''' + ... cycles: + ... - minimal_cycle: + ... tasks: + ... - task_a: + ... tasks: + ... - task_b: + ... plugin: shell + ... data: + ... available: + ... - foo: + ... generated: + ... - bar: + ... ''' + ... ) + >>> wf = pydantic_yaml.parse_yaml_raw_as(ConfigWorkflow, config) + + minimum programmatically created instance + + >>> empty_wf = ConfigWorkflow(cycles=[], tasks=[], data={}) + + """ + name: str | None = None rootdir: Path | None = None cycles: list[ConfigCycle] diff --git a/src/sirocco/pretty_print.py b/src/sirocco/pretty_print.py index 4100b15c..cf482f51 100644 --- a/src/sirocco/pretty_print.py +++ b/src/sirocco/pretty_print.py @@ -31,7 +31,7 @@ def as_block(self, header: str, body: str) -> str: Example: - >>> print(PrettyPrinter().as_block("header", "foo\nbar")) + >>> print(PrettyPrinter().as_block("header", "foo\\nbar")) header: foo bar @@ -50,7 +50,7 @@ def as_item(self, content: str) -> str: - foo >>> pp = PrettyPrinter() - >>> print(pp.as_item(pp.as_block("header", "multiple\nlines\nof text"))) + >>> print(pp.as_item(pp.as_block("header", "multiple\\nlines\\nof text"))) - header: multiple lines @@ -87,14 +87,13 @@ def format_basic(self, obj: core.GraphItem) -> str: >>> from datetime import datetime >>> print( ... PrettyPrinter().format_basic( - ... Task( - ... name=foo, + ... core.Task( + ... name="foo", ... coordinates={"date": datetime(1000, 1, 1).date()}, - ... workflow=None, ... ) ... ) ... ) - foo [1000-01-01] + foo [date: 1000-01-01] """ name = obj.name if obj.coordinates: diff --git a/tests/unit_tests/__init__.py b/tests/unit_tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit_tests/core/__init__.py b/tests/unit_tests/core/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit_tests/core/test_workflow.py b/tests/unit_tests/core/test_workflow.py new file mode 100644 index 00000000..ab1394fb --- /dev/null +++ b/tests/unit_tests/core/test_workflow.py @@ -0,0 +1,23 @@ +from sirocco import pretty_print +from sirocco.core import workflow +from sirocco.parsing import _yaml_data_models as models + + +def test_minimal_workflow(): + minimal_config = models.ConfigWorkflow( + cycles=[], + tasks=[{"some_task": {"plugin": "shell"}}], + data=models.ConfigData( + available=[models.ConfigAvailableData(foo={})], + generated=[models.ConfigGeneratedData(bar={})], + ), + ) + + testee = workflow.Workflow(minimal_config) + + pretty_print.PrettyPrinter().format(testee) + + assert testee.name is None + assert len(list(testee.tasks)) == 0 + assert len(list(testee.cycles)) == 0 + assert testee.data[("foo", {})].available diff --git a/tests/unit_tests/parsing/__init__.py b/tests/unit_tests/parsing/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit_tests/parsing/test_yaml_data_models.py b/tests/unit_tests/parsing/test_yaml_data_models.py new file mode 100644 index 00000000..27464f60 --- /dev/null +++ b/tests/unit_tests/parsing/test_yaml_data_models.py @@ -0,0 +1,41 @@ +import textwrap + +from sirocco.parsing import _yaml_data_models as models + + +def test_workflow_test_internal_dicts(): + testee = models.ConfigWorkflow( + cycles=[], + tasks=[{"some_task": {"plugin": "shell"}}], + data=models.ConfigData( + available=[models.ConfigAvailableData(foo={})], + generated=[models.ConfigGeneratedData(bar={})], + ), + ) + assert testee.data_dict["foo"].name == "foo" + assert testee.data_dict["bar"].name == "bar" + assert testee.task_dict["some_task"].name == "some_task" + + +def test_load_workflow_config(tmp_path): + minimal_config = textwrap.dedent( + """ + cycles: + - minimal: + tasks: + - a: + tasks: + - b: + plugin: shell + data: + available: + - c: + generated: + - d: + """ + ) + minimal = tmp_path / "minimal.yml" + minimal.write_text(minimal_config) + testee = models.load_workflow_config(str(minimal)) + assert testee.name == "minimal" + assert testee.rootdir == tmp_path