Prefect Dev Log

More effectively coping with Python's 'type system'

By Nate Nowack

This week, when reviewing a PR, I observed that Guidry had added a new field to the RunDeployment action: schedule_after. This new field allows users to schedule a deployment run some fixed time after a condition is met.

Due to reasons orthogonal to this post, we have at least 3 places we need to update schemas when we add a new field like this:

It’s not important why this is true, but the fact of the matter is that we have 3 separate BaseModel subclasses that all will get this new field.

A “delay” must be a positive amount of time, so we want to assert that schedule_after must be a non-negative timedelta. Therefore, as we are pydantic users, the PR included 3 separate but identical @field_validator implementations that checked (for each schema) that the value was non-negative.

This could be fine, but I am also haunted by the experience of migrating from pydantic v1 to v2, which included a great deal of moving these functionally identical validators around on multiple schemas.

Therefore, we’ve begun to use Annotated types more often to bind validation logic to field types themselves, so that we don’t need to repeat said validation logic on multiple schemas that use those types.

So, that’s what I suggested and that’s what we did!

It works out really clean (if I do say so myself), and looks like this:

 1# src/prefect/types/__init__.py
 2
 3from datetime import timedelta
 4from typing import Annotated
 5
 6from pydantic import AfterValidator
 7
 8def _validate_non_negative_timedelta(v: timedelta) -> timedelta:
 9    if v < timedelta(seconds=0):
10        raise ValueError("timedelta must be non-negative")
11    return v
12
13NonNegativeTimedelta = Annotated[
14    timedelta,
15    AfterValidator(_validate_non_negative_timedelta)
16]
17
18# src/prefect/server/schemas/actions.py
19
20from prefect.types import NonNegativeTimedelta
21
22class RunDeployment(BaseModel):
23    # ...
24    schedule_after: NonNegativeTimedelta

… and so on for the rest of the schemas.

The nice things are that:

Here’s a (entirely unedited 🙃) youtube video that I made about this (checks calendar) almost a couple years ago now:

<< Previous Post

|

Next Post >>

#Python #Types #Pydantic