Miroslav Simko <ms@iolabs.ch> 2026-09-02T08:58:33+02:00
Commit #30 ยท 4 snippets
src/iolabs_image_analyzer_line_bitmap_inference/_config.py | 9 ++++++++- tests/test_config.py | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-)
| 14 | import os | 14 | import os |
| 15 | from pathlib import Path | 15 | from pathlib import Path |
| 16 | from typing import Any | 16 | from typing import Any |
| 17 | 17 | ||
| 18 | import pydantic | ||
| 18 | from iolabs.common import config_loader | 19 | from iolabs.common import config_loader |
| 19 | 20 | ||
| 20 | logger = logging.getLogger(__name__) | 21 | logger = logging.getLogger(__name__) |
| 21 | 22 |
| 46 | write_overlay: bool = False | 47 | write_overlay: bool = False |
| 47 | probabilities_dtype: str = "float16" | 48 | probabilities_dtype: str = "float16" |
| 48 | vectorization: VectorizationConfig = VectorizationConfig() | 49 | vectorization: VectorizationConfig = VectorizationConfig() |
| 49 | 50 | ||
| 51 | @pydantic.field_validator("vectorization", mode="before") | ||
| 52 | @classmethod | ||
| 53 | def _none_section_is_default(cls, value: Any) -> Any: | ||
| 54 | """Treat an explicit ``null`` section as "use the defaults".""" | ||
| 55 | return {} if value is None else value | ||
| 56 | |||
| 50 | 57 | ||
| 51 | class LineBitmapInferenceConfigError(config_loader.ConfigError): | 58 | class LineBitmapInferenceConfigError(config_loader.ConfigError): |
| 52 | """Raised when the inference config contains unsupported keys.""" | 59 | """Raised when the inference config contains unsupported keys or values.""" |
| 53 | 60 | ||
| 54 | 61 | ||
| 55 | def _config_path_for_load(config_path: str | Path | None) -> str | Path | None: | 62 | def _config_path_for_load(config_path: str | Path | None) -> str | Path | None: |
| 56 | """Return *config_path*, or the sibling JSON when this module is loaded loose.""" | 63 | """Return *config_path*, or the sibling JSON when this module is loaded loose.""" |
| 64 | 64 | ||
| 65 | def test_bool_is_rejected_for_int_field() -> None: | 65 | def test_bool_is_rejected_for_int_field() -> None: |
| 66 | with pytest.raises(MODULE.LineBitmapInferenceConfigError, match="tile_size"): | 66 | with pytest.raises(MODULE.LineBitmapInferenceConfigError, match="tile_size"): |
| 67 | MODULE.build_line_bitmap_inference_config(overrides={"tile_size": True}) | 67 | MODULE.build_line_bitmap_inference_config(overrides={"tile_size": True}) |
| 68 | |||
| 69 | |||
| 70 | def test_null_vectorization_section_falls_back_to_defaults() -> None: | ||
| 71 | config = MODULE.build_line_bitmap_inference_config(overrides={"vectorization": None}) | ||
| 72 | assert config["vectorization"]["min_component_pixels"] == 16 | ||
| 73 | assert config["vectorization"]["dash_max_gap_px"] == 40.0 |
| 64 | 64 | ||
| 65 | def test_bool_is_rejected_for_int_field() -> None: | 65 | def test_bool_is_rejected_for_int_field() -> None: |
| 66 | with pytest.raises(MODULE.LineBitmapInferenceConfigError, match="tile_size"): | 66 | with pytest.raises(MODULE.LineBitmapInferenceConfigError, match="tile_size"): |
| 67 | MODULE.build_line_bitmap_inference_config(overrides={"tile_size": True}) | 67 | MODULE.build_line_bitmap_inference_config(overrides={"tile_size": True}) |
| 68 | |||
| 69 | |||
| 70 | def test_null_vectorization_section_falls_back_to_defaults() -> None: | ||
| 71 | config = MODULE.build_line_bitmap_inference_config(overrides={"vectorization": None}) | ||
| 72 | assert config["vectorization"]["min_component_pixels"] == 16 | ||
| 73 | assert config["vectorization"]["dash_max_gap_px"] == 40.0 |
nullconfig sections fall back to section defaults (pre-migration behaviour), re-validation instead ofmodel_copy(update=), doc/test parity with the packaged JSON.