Skip to content

Commit

Permalink
[pydantic#12] add pendulum dt support
Browse files Browse the repository at this point in the history
  • Loading branch information
theunkn0wn1 committed Jan 11, 2024
1 parent 634487f commit a83037e
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 0 deletions.
40 changes: 40 additions & 0 deletions pydantic_extra_types/pendulum_dt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
try:
from pendulum import DateTime as _DateTime
from pendulum import parse
except ModuleNotFoundError: # pragma: no cover
raise RuntimeError(
'The `pendulum_dt` module requires "pendulum" to be installed. You can install it with "pip install pendulum".'
)
from typing import Any, List, Type

from pydantic import GetCoreSchemaHandler
from pydantic_core import PydanticCustomError, core_schema


class DateTime(_DateTime):
"""
A pendulum.DateTime object.
At runtime, this type decomposes into pendulum.DateTime automagically.
This type exists because Pydantic throws a fit on unknown types.
"""

__slots__: List[str] = []

@classmethod
def __get_pydantic_core_schema__(cls, source: Type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
return core_schema.no_info_wrap_validator_function(
cls._validate_datetime, core_schema.is_instance_schema(_DateTime)
)

@classmethod
def _validate_datetime(cls, value: Any, handler: core_schema.ValidatorFunctionWrapHandler) -> Any:
# if we are passed an existing instance, pass it straight through.
if isinstance(value, _DateTime):
return handler(value)

# otherwise, parse it.
try:
data = parse(value)
except Exception as exc:
raise PydanticCustomError('value_error', 'value is not a valid timestamp') from exc
return handler(data)
39 changes: 39 additions & 0 deletions tests/test_pendulum_dt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import pendulum
import pytest
from pydantic import BaseModel, ValidationError

from pydantic_extra_types.pendulum_dt import DateTime


class Model(BaseModel):
dt: DateTime


def test_pendulum_dt_existing_instance():
"""
Verifies that constructing a model with an existing pendulum dt doesn't throw.
"""
now = pendulum.now()
model = Model(dt=now)
assert model.dt == now


@pytest.mark.parametrize(
'dt', [pendulum.now().to_iso8601_string(), pendulum.now().to_w3c_string(), pendulum.now().to_iso8601_string()]
)
def test_pendulum_dt_from_serialized(dt):
"""
Verifies that building an instance from serialized, well-formed strings decode properly.
"""
dt_actual = pendulum.parse(dt)
model = Model(dt=dt)
assert model.dt == dt_actual


@pytest.mark.parametrize('dt', [None, 'malformed', pendulum.now().to_iso8601_string()[:5], 42])
def test_pendulum_dt_malformed(dt):
"""
Verifies that the instance fails to validate if malformed dt are passed.
"""
with pytest.raises(ValidationError):
Model(dt=dt)

0 comments on commit a83037e

Please sign in to comment.