From 247146b60e2e46a8b0be4468076271686477be15 Mon Sep 17 00:00:00 2001 From: woutdenolf Date: Fri, 16 Oct 2026 18:41:31 +0200 Subject: [PATCH 1/2] deprecate non-list examples --- CHANGELOG.md | 8 ++++++++ src/ewokssphinx/ewoks_task_utils.py | 15 +++++++++++++-- src/ewokssphinx/tests/test_utils.py | 12 ++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9fa7a4..f306fca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `ewokstasks` directive displays task tags. +### Deprecated + +- Non-list `examples` in pydantic fields of task models. + +### Fixed + +- `ewokstasks` directive failed on pydantic fields with non-list `examples`. + ## [2.2.1] - 2026-04-21 ### Fixed diff --git a/src/ewokssphinx/ewoks_task_utils.py b/src/ewokssphinx/ewoks_task_utils.py index 2ac1491..9456116 100644 --- a/src/ewokssphinx/ewoks_task_utils.py +++ b/src/ewokssphinx/ewoks_task_utils.py @@ -1,6 +1,7 @@ import importlib import inspect import json +import warnings from functools import reduce from pathlib import Path from typing import Annotated @@ -123,7 +124,7 @@ def _parse_parameter_names( def _parse_pydantic_model(model: Type[BaseModel]) -> list[ParameterDescription]: return [ - _parse_pydantic_field(name, field_info) + _parse_pydantic_field(model, name, field_info) for name, field_info in model.model_fields.items() ] @@ -141,8 +142,18 @@ def _parse_doc(text) -> str: return inspect.cleandoc(text) if text else "" -def _parse_pydantic_field(name: str, field_info: FieldInfo) -> ParameterDescription: +def _parse_pydantic_field( + model: Type[BaseModel], name: str, field_info: FieldInfo +) -> ParameterDescription: examples = getattr(field_info, "examples", None) + if examples is not None and not isinstance(examples, list): + warnings.warn( + f"'examples' of field '{name}' of '{qualname(model)}' should be a list " + "(this will raise an exception in a future release)", + FutureWarning, + stacklevel=2, + ) + examples = [examples] default = getattr(field_info, "default", None) if default is PydanticUndefined: default = None diff --git a/src/ewokssphinx/tests/test_utils.py b/src/ewokssphinx/tests/test_utils.py index 389d644..95b185f 100644 --- a/src/ewokssphinx/tests/test_utils.py +++ b/src/ewokssphinx/tests/test_utils.py @@ -1,6 +1,9 @@ +import pytest from pydantic import BaseModel +from pydantic import Field from ewokssphinx.ewoks_task_utils import _extract_submodels +from ewokssphinx.ewoks_task_utils import _parse_pydantic_model class Type1(BaseModel): @@ -44,3 +47,12 @@ def test_extract_type_inputs(): "ewokssphinx.tests.dummy_tasks_nested.Coordinates", "ewokssphinx.tests.dummy_tasks_nested.Planet", ] + + +def test_parse_non_list_examples(): + class Model(BaseModel): + resources: dict = Field(default={}, examples={"cpu": 0.5}) + + with pytest.warns(FutureWarning, match="resources"): + (parameter,) = _parse_pydantic_model(Model) + assert parameter["examples"] == [{"cpu": 0.5}] From f9e819604206b26b83578bd8c2f0c6079dc190e5 Mon Sep 17 00:00:00 2001 From: woutdenolf Date: Thu, 22 Oct 2026 10:18:37 +0200 Subject: [PATCH 2/2] TypeError instead of warning --- CHANGELOG.md | 8 ++------ src/ewokssphinx/ewoks_task_utils.py | 10 +++------- src/ewokssphinx/tests/test_utils.py | 5 ++--- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f306fca..719f908 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,13 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `ewokstasks` directive displays task tags. -### Deprecated - -- Non-list `examples` in pydantic fields of task models. - -### Fixed +### Changed -- `ewokstasks` directive failed on pydantic fields with non-list `examples`. +- `ewokstasks` directive: clear error for pydantic fields with non-list `examples`. ## [2.2.1] - 2026-04-21 diff --git a/src/ewokssphinx/ewoks_task_utils.py b/src/ewokssphinx/ewoks_task_utils.py index 9456116..e49864f 100644 --- a/src/ewokssphinx/ewoks_task_utils.py +++ b/src/ewokssphinx/ewoks_task_utils.py @@ -1,7 +1,6 @@ import importlib import inspect import json -import warnings from functools import reduce from pathlib import Path from typing import Annotated @@ -147,13 +146,10 @@ def _parse_pydantic_field( ) -> ParameterDescription: examples = getattr(field_info, "examples", None) if examples is not None and not isinstance(examples, list): - warnings.warn( - f"'examples' of field '{name}' of '{qualname(model)}' should be a list " - "(this will raise an exception in a future release)", - FutureWarning, - stacklevel=2, + raise TypeError( + f"'examples' of field '{name}' of '{qualname(model)}' must be a list, " + f"got {type(examples).__name__}" ) - examples = [examples] default = getattr(field_info, "default", None) if default is PydanticUndefined: default = None diff --git a/src/ewokssphinx/tests/test_utils.py b/src/ewokssphinx/tests/test_utils.py index 95b185f..79f0cbd 100644 --- a/src/ewokssphinx/tests/test_utils.py +++ b/src/ewokssphinx/tests/test_utils.py @@ -53,6 +53,5 @@ def test_parse_non_list_examples(): class Model(BaseModel): resources: dict = Field(default={}, examples={"cpu": 0.5}) - with pytest.warns(FutureWarning, match="resources"): - (parameter,) = _parse_pydantic_model(Model) - assert parameter["examples"] == [{"cpu": 0.5}] + with pytest.raises(TypeError, match="resources"): + _parse_pydantic_model(Model)