Simplify setting model validation
This commit is contained in:
parent
63338f8ea0
commit
2ae4f832bf
3 changed files with 20 additions and 55 deletions
|
|
@ -16,7 +16,7 @@ from types import TracebackType
|
|||
from typing import Literal, Mapping, Optional, Self, Sequence
|
||||
|
||||
import numpy as np
|
||||
from pydantic import BaseModel, computed_field, field_validator, model_validator
|
||||
from pydantic import BaseModel, Field, computed_field, field_validator, model_validator
|
||||
|
||||
import labthings_fastapi as lt
|
||||
from labthings_fastapi.types.numpy import NDArray
|
||||
|
|
@ -59,12 +59,12 @@ class StackParams(BaseModel):
|
|||
"""A class for holding stack parameters, and returning computed ones."""
|
||||
|
||||
stack_dz: int
|
||||
images_to_save: int
|
||||
images_to_save: int = Field(gt=0)
|
||||
|
||||
# Using docstrings under variables as this is how pdoc would expect
|
||||
# attributed to be documented
|
||||
|
||||
settling_time: float = 0.3
|
||||
settling_time: float = Field(default=0.3, ge=0)
|
||||
"""Time (in seconds) between moving and capturing an image"""
|
||||
|
||||
backlash_correction: int = 250
|
||||
|
|
@ -75,22 +75,6 @@ class StackParams(BaseModel):
|
|||
origin: StackOrigin = StackOrigin.START
|
||||
"""Where the stack is positioned relative to the current z position."""
|
||||
|
||||
@field_validator("images_to_save")
|
||||
@classmethod
|
||||
def validate_images_to_save(cls, value: int) -> int:
|
||||
"""Validate that the number of images to save is greater than zero."""
|
||||
if value <= 0:
|
||||
raise ValueError(f"Invalid number of images to save: {value}. Must be > 0.")
|
||||
return value
|
||||
|
||||
@field_validator("settling_time")
|
||||
@classmethod
|
||||
def validate_settling_time(cls, value: float) -> float:
|
||||
"""Validate that settling time is zero or positive."""
|
||||
if value < 0:
|
||||
raise ValueError(f"Invalid settling time: {value}. Must be positive or 0.")
|
||||
return value
|
||||
|
||||
|
||||
class SmartStackParams(StackParams):
|
||||
"""A class for holding smart stack parameters, and returning computed ones."""
|
||||
|
|
|
|||
|
|
@ -15,12 +15,12 @@ import tempfile
|
|||
import time
|
||||
from datetime import datetime
|
||||
from types import TracebackType
|
||||
from typing import Any, Literal, Mapping, Optional, Self, Tuple
|
||||
from typing import Annotated, Any, Literal, Mapping, Optional, Self, Tuple
|
||||
|
||||
import numpy as np
|
||||
import piexif
|
||||
from PIL import Image
|
||||
from pydantic import BaseModel, field_validator
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
import labthings_fastapi as lt
|
||||
from labthings_fastapi.types.numpy import NDArray
|
||||
|
|
@ -48,34 +48,15 @@ class CaptureError(RuntimeError):
|
|||
"""An error trying to capture from a CameraThing."""
|
||||
|
||||
|
||||
PositiveInt = Annotated[int, Field(ge=1)]
|
||||
NonEmptyString = Annotated[str, Field(min_length=1)]
|
||||
|
||||
|
||||
class CaptureParams(BaseModel):
|
||||
"""A class for capturing at least a single image."""
|
||||
|
||||
images_dir: str
|
||||
save_resolution: tuple[int, int]
|
||||
|
||||
@field_validator("save_resolution")
|
||||
@classmethod
|
||||
def validate_save_resolution(cls, value: Tuple[int, int]) -> Tuple[int, int]:
|
||||
"""Validate that save_resolution is a tuple with exactly two positive integers."""
|
||||
if not isinstance(value, tuple):
|
||||
raise TypeError("Save resolution should be a tuple")
|
||||
if len(value) != 2 or any(not isinstance(x, int) or x <= 0 for x in value):
|
||||
raise ValueError(
|
||||
f"Invalid save_resolution: {value}. "
|
||||
"Must be a tuple of two positive integers."
|
||||
)
|
||||
return value
|
||||
|
||||
@field_validator("images_dir")
|
||||
@classmethod
|
||||
def validate_images_dir(cls, value: str) -> str:
|
||||
"""Validate that images_dir is a non-empty string."""
|
||||
if not isinstance(value, str) or not value:
|
||||
raise ValueError(
|
||||
f"Invalid images_dir: {value}. Must be a non-empty string."
|
||||
)
|
||||
return value
|
||||
images_dir: NonEmptyString
|
||||
save_resolution: tuple[PositiveInt, PositiveInt]
|
||||
|
||||
|
||||
class NoImageInMemoryError(RuntimeError):
|
||||
|
|
|
|||
|
|
@ -109,8 +109,8 @@ def test_stack_params_negative_images_to_save(save_ims, extra_ims):
|
|||
# Depending on the values multiple messages are possible
|
||||
match = (
|
||||
"(Can't test for focus with fewer than 3 images|"
|
||||
"Images to save must be positive and odd)|"
|
||||
"Invalid number of images to save"
|
||||
"Images to save must be positive and odd|"
|
||||
"Input should be greater than 0)"
|
||||
)
|
||||
with pytest.raises(ValueError, match=match):
|
||||
SmartStackParams(
|
||||
|
|
@ -154,8 +154,8 @@ def test_even_images_to_save(save_ims, extra_ims):
|
|||
"""
|
||||
match = (
|
||||
"(Can't test for focus with fewer than 3 images|"
|
||||
"Images to save must be positive and odd)|"
|
||||
"Invalid number of images to save"
|
||||
"Images to save must be positive and odd|"
|
||||
"Input should be greater than 0)"
|
||||
)
|
||||
with pytest.raises(ValueError, match=match):
|
||||
SmartStackParams(
|
||||
|
|
@ -948,7 +948,7 @@ def test_run_basic_stack_end_origin(
|
|||
def test_invalid_stack_images_raises():
|
||||
"""Test basic stack raises expected error for negative or zero image count."""
|
||||
for capture_count in [-3, 0]:
|
||||
with pytest.raises(ValueError, match="Invalid number of images to save"):
|
||||
with pytest.raises(ValueError, match="Input should be greater than 0"):
|
||||
StackParams(
|
||||
stack_dz=10,
|
||||
images_to_save=capture_count,
|
||||
|
|
@ -960,7 +960,7 @@ def test_invalid_stack_images_raises():
|
|||
|
||||
def test_invalid_stack_settling_raises():
|
||||
"""Test basic stack raises expected error for negative settling time."""
|
||||
with pytest.raises(ValueError, match="Invalid settling time"):
|
||||
with pytest.raises(ValueError, match="Input should be greater than or equal to 0"):
|
||||
StackParams(
|
||||
stack_dz=10,
|
||||
images_to_save=1,
|
||||
|
|
@ -973,7 +973,7 @@ def test_invalid_stack_settling_raises():
|
|||
@pytest.mark.parametrize(
|
||||
("bad_path", "match_err"),
|
||||
[
|
||||
("", "Must be a non-empty string"),
|
||||
("", "String should have at least 1 character"),
|
||||
(None, "Input should be a valid string"),
|
||||
(67, "Input should be a valid string"),
|
||||
],
|
||||
|
|
@ -987,8 +987,8 @@ def test_invalid_capture_dir_raises(bad_path, match_err):
|
|||
@pytest.mark.parametrize(
|
||||
("bad_res", "match_err"),
|
||||
[
|
||||
((-100, 50), "Invalid save_resolution:"),
|
||||
((20, 0), "Invalid save_resolution:"),
|
||||
((-100, 50), "Input should be greater than or equal to 1"),
|
||||
((20, 0), "Input should be greater than or equal to 1"),
|
||||
("", "Input should be a valid tuple"),
|
||||
(None, "Input should be a valid tuple"),
|
||||
(67, "Input should be a valid tuple"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue