Type signature for Field() gt/lt/gte/lte validators should include timedelta
Initial Checks
- [X] I confirm that I'm using Pydantic V2
Description
The type signature for the numeric min/max value constraints on Field() (i.e. gt, lt, gte, lte ) specified here is too restrictive, allowing only float or None.
In actual use, the validation works correctly as expected with timedelta() instances, but static type checkers (at least Pyright) object.
Example Code
from datetime import timedelta
from pydantic import BaseModel, Field
class Duration(BaseModel):
td: timedelta = Field(timedelta(0), gt=timedelta(0))
Duration(td=timedelta(seconds=1))
Duration(td=timedelta(seconds=-1))
# ValidationError: 1 validation error for Duration
# td
# Input should be greater than 0 seconds [type=greater_than, input_value=datetime.timedelta(days=-1), input_type=timedelta]
# For further information visit https://errors.pydantic.dev/2.7/v/greater_than
# This is all perfectly valid code, but Pyright (and I assume also mypy) complains about the type signature
# $ pyright example.py
# /home/bjmc/Sandbox/example.py
# /home/bjmc/Sandbox/example.py:6:44 - error: Argument of type "timedelta" cannot be assigned to # parameter "gt" of type "float | None" in function "Field"
# Type "timedelta" is incompatible with type "float | None"
# "timedelta" is incompatible with "float"
# "timedelta" is incompatible with "None" (reportArgumentType)
# 1 error, 0 warnings, 0 informations
Python, Pydantic & OS Version
pydantic version: 2.7.1
pydantic-core version: 2.18.2
pydantic-core build: profile=release pgo=true
install path: /home/bjmc/.cache/pypoetry/virtualenvs/ska-oso-pdm-zPbyUnCb-py3.10/lib/python3.10/site-packages/pydantic
python version: 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0]
platform: Linux-6.5.0-1022-oem-x86_64-with-glibc2.35
related packages: typing_extensions-4.11.0 pyright-1.1.363
commit: unknown
I'm not a typing wizard, but I wonder if a good resolution here would be to use the comparison protocols defined by typeshed for these field validators?
I'm happy to open an PR if people like that approach, or have another suggestion.
@bjmc,
Hmm, I'm intrigued - feel free to open a PR and I can review. I don think it could be good to offer more general support here!
Seems reasonable to use the comparison protocols, internally Pydantic uses the functions defined in pydantic._internal._validators (e.g. greater_than_validator), so this seems like a safe improvement.
Two options here:
- Use the protocols defined in
annotated_types(best option imo). - Import the protocols from
_typeshedin anif TYPE_CHECKING:block. They are not defined as being stable.
Let's go with the annotated_types protocols
I agree that annotated_types is better since it's already a dependency.
@sydney-runkle I've got a draft PR up. I'm not quite sure where to add a test for this, since it's only a type-checking issue. Suggestions?
@bjmc,
Perhaps you could just add a before / after example to this PR running pyright on a file that uses a timedelta gt constraint as a proof of concept? PR looks great to me!
Update: I've added some notes to your PR re testing + some change requests!