1
0
Fork 0

Adding upstream version 2.10.2.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-05 14:09:17 +01:00
parent c260aa144d
commit 74c2552bd7
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
20 changed files with 176 additions and 49 deletions

View file

@ -19,7 +19,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- uses: astral-sh/setup-uv@v5
with:
enable-cache: true
@ -39,7 +39,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
env:
UV_PYTHON: ${{ matrix.python-version }}
@ -47,7 +47,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- uses: astral-sh/setup-uv@v5
with:
enable-cache: true
@ -74,7 +74,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- uses: astral-sh/setup-uv@v5
with:
enable-cache: true
@ -111,7 +111,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- uses: astral-sh/setup-uv@v5
with:
enable-cache: true

View file

@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v5.0.0
hooks:
- id: no-commit-to-branch # prevent direct commits to the `main` branch
- id: check-yaml
@ -24,3 +24,10 @@ repos:
types: [python]
language: system
pass_filenames: false
- id: Typecheck
name: Typecheck
entry: make
args: [typecheck]
types: [python]
language: system
pass_filenames: false

View file

@ -2,6 +2,50 @@
## Latest Changes
## 2.10.2
* Add back Python 3.8 support by @Viicos in https://github.com/pydantic/pydantic-extra-types/pull/249
* ⬆ Bump astral-sh/setup-uv from 4 to 5 by @dependabot in https://github.com/pydantic/pydantic-extra-types/pull/282
* Preserve months when using the Pendulum Duration type by @gareththackeray in https://github.com/pydantic/pydantic-extra-types/pull/283
* ✨ Add type checking support and improve type hints across the codebase by @yezz123 in https://github.com/pydantic/pydantic-extra-types/pull/285
* 📝 Add additional installation information to README by @oakhan3 in https://github.com/pydantic/pydantic-extra-types/pull/233
## 2.10.1
* Allow build with python-ulid 3.0.0 by @sunpoet in https://github.com/pydantic/pydantic-extra-types/pull/225
* 🔨 added automatic syntax-upgrade hook ~ pyupgrade by @janas-adam in https://github.com/pydantic/pydantic-extra-types/pull/229
* :fire: Revert adding pyupgrade as a hook in pre-commit by @yezz123 in https://github.com/pydantic/pydantic-extra-types/pull/230
* isolate url in Currency by @edasubert in https://github.com/pydantic/pydantic-extra-types/pull/235
* lower case currency is valid by @edasubert in https://github.com/pydantic/pydantic-extra-types/pull/236
* Update SemanticVersion by @viccie30 in https://github.com/pydantic/pydantic-extra-types/pull/237
* Epoch - unix timestamp by @commonism in https://github.com/pydantic/pydantic-extra-types/pull/240
* :recycle: Migrate Pydantic Extra Types to use uv by @yezz123 in https://github.com/pydantic/pydantic-extra-types/pull/241
* ⬆ Bump astral-sh/setup-uv from 3 to 4 by @dependabot in https://github.com/pydantic/pydantic-extra-types/pull/245
* ⬆ Bump pre-commit/action from 3.0.0 to 3.0.1 by @dependabot in https://github.com/pydantic/pydantic-extra-types/pull/244
* 🔖 Release version 2.10.1 by @yezz123 in https://github.com/pydantic/pydantic-extra-types/pull/246
* Fix check python version for release by @hramezani in https://github.com/pydantic/pydantic-extra-types/pull/247
## 2.10.0
### Types
* Add semantic version type by @jbkroner in https://github.com/pydantic/pydantic-extra-types/pull/199
* feat: add S3Path by @lucianosrp in https://github.com/pydantic/pydantic-extra-types/pull/206
### Refactor
* feature: Improve phone number validator by @mZbZ in https://github.com/pydantic/pydantic-extra-types/pull/202
* Feature: Add phone number validator by @mZbZ in https://github.com/pydantic/pydantic-extra-types/pull/203
* Domain name string type by @matter1-git in https://github.com/pydantic/pydantic-extra-types/pull/212
* Adjust test_json_schema() for Pydantic 2.9 by @musicinmybrain in https://github.com/pydantic/pydantic-extra-types/pull/215
* Allow python-ulid 3.0 by @musicinmybrain in https://github.com/pydantic/pydantic-extra-types/pull/222
### Dependencies
* ⬆ Bump the python-packages group with 5 updates by @dependabot in https://github.com/pydantic/pydantic-extra-types/pull/201
* ✨ deprecate `semver` in favor of `semantic_version` by @07pepa in https://github.com/pydantic/pydantic-extra-types/pull/209
* 🔖 Release version 2.10.0 by @yezz123 in https://github.com/pydantic/pydantic-extra-types/pull/224
## 2.9.0
### Types

View file

@ -25,6 +25,10 @@ lint:
uv run ruff format --check
uv run ruff check
.PHONY: typecheck # Typecheck the code
typecheck:
uv run mypy pydantic_extra_types
.PHONY: test
test:
uv run pytest

View file

@ -8,3 +8,17 @@
A place for pydantic types that probably shouldn't exist in the main pydantic lib.
See [pydantic/pydantic#5012](https://github.com/pydantic/pydantic/issues/5012) for more info.
## Installation
Install this library with the desired extras dependencies as listed in [project.optional-dependencies](./pyproject.toml).
For example, if pendulum support was desired:
```shell
# via uv
$ uv add "pydantic-extra-types[pendulum]"
# via pip
$ pip install -U "pydantic-extra-types[pendulum]"
```

View file

@ -1 +1 @@
__version__ = '2.10.1'
__version__ = '2.10.2'

View file

@ -12,16 +12,16 @@ from __future__ import annotations
import math
import re
from colorsys import hls_to_rgb, rgb_to_hls
from typing import Any, Callable, Literal, Union, cast
from typing import Any, Callable, Literal, Tuple, Union, cast
from pydantic import GetJsonSchemaHandler
from pydantic._internal import _repr
from pydantic.json_schema import JsonSchemaValue
from pydantic_core import CoreSchema, PydanticCustomError, core_schema
ColorTuple = Union[tuple[int, int, int], tuple[int, int, int, float]]
ColorTuple = Union[Tuple[int, int, int], Tuple[int, int, int, float]]
ColorType = Union[ColorTuple, str, 'Color']
HslColorTuple = Union[tuple[float, float, float], tuple[float, float, float, float]]
HslColorTuple = Union[Tuple[float, float, float], Tuple[float, float, float, float]]
class RGBA:
@ -115,7 +115,7 @@ class Color(_repr.Representation):
"""
if self._rgba.alpha is not None:
return self.as_hex()
rgb = cast(tuple[int, int, int], self.as_rgb_tuple())
rgb = cast('tuple[int, int, int]', self.as_rgb_tuple())
if rgb in COLORS_BY_VALUE:
return COLORS_BY_VALUE[rgb]

View file

@ -3,8 +3,10 @@
[`Coordinate`][pydantic_extra_types.coordinate.Coordinate] data types.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, ClassVar
from typing import Any, ClassVar, Tuple
from pydantic import GetCoreSchemaHandler
from pydantic._internal import _repr
@ -89,7 +91,7 @@ class Coordinate(_repr.Representation):
```
"""
_NULL_ISLAND: ClassVar[tuple[float, float]] = (0.0, 0.0)
_NULL_ISLAND: ClassVar[Tuple[float, float]] = (0.0, 0.0)
latitude: Latitude
longitude: Longitude
@ -100,7 +102,7 @@ class Coordinate(_repr.Representation):
core_schema.no_info_wrap_validator_function(cls._parse_str, core_schema.str_schema()),
core_schema.no_info_wrap_validator_function(
cls._parse_tuple,
handler.generate_schema(tuple[float, float]),
handler.generate_schema(Tuple[float, float]),
),
handler(source),
]

View file

@ -5,7 +5,6 @@ This class depends on the `pydantic` package and implements custom validation fo
from __future__ import annotations
import re
from collections.abc import Mapping
from typing import Any
from pydantic import GetCoreSchemaHandler
@ -54,5 +53,6 @@ class DomainStr(str):
@classmethod
def __get_pydantic_json_schema__(
cls, schema: core_schema.CoreSchema, handler: GetCoreSchemaHandler
) -> Mapping[str, Any]:
return handler(schema)
) -> dict[str, Any]:
# Cast the return value to dict[str, Any]
return dict(handler(schema))

View file

@ -2,6 +2,8 @@
CoreSchema implementation. This allows Pydantic to validate the DateTime object.
"""
from __future__ import annotations
try:
from pendulum import Date as _Date
from pendulum import DateTime as _DateTime
@ -63,7 +65,7 @@ class DateTime(_DateTime, metaclass=DateTimeSettings):
return core_schema.no_info_wrap_validator_function(cls._validate, core_schema.datetime_schema())
@classmethod
def _validate(cls, value: Any, handler: core_schema.ValidatorFunctionWrapHandler) -> 'DateTime':
def _validate(cls, value: Any, handler: core_schema.ValidatorFunctionWrapHandler) -> DateTime:
"""Validate the datetime object and return it.
Args:
@ -128,7 +130,7 @@ class Date(_Date):
return core_schema.no_info_wrap_validator_function(cls._validate, core_schema.date_schema())
@classmethod
def _validate(cls, value: Any, handler: core_schema.ValidatorFunctionWrapHandler) -> 'Date':
def _validate(cls, value: Any, handler: core_schema.ValidatorFunctionWrapHandler) -> Date:
"""Validate the date object and return it.
Args:
@ -187,7 +189,7 @@ class Duration(_Duration):
return core_schema.no_info_wrap_validator_function(cls._validate, core_schema.timedelta_schema())
@classmethod
def _validate(cls, value: Any, handler: core_schema.ValidatorFunctionWrapHandler) -> 'Duration':
def _validate(cls, value: Any, handler: core_schema.ValidatorFunctionWrapHandler) -> Duration:
"""Validate the Duration object and return it.
Args:
@ -197,9 +199,25 @@ class Duration(_Duration):
Returns:
The validated value or raises a PydanticCustomError.
"""
# if we are passed an existing instance, pass it straight through.
if isinstance(value, (_Duration, timedelta)):
return Duration(seconds=value.total_seconds())
if isinstance(value, _Duration):
return Duration(
years=value.years,
months=value.months,
weeks=value.weeks,
days=value.remaining_days,
hours=value.hours,
minutes=value.minutes,
seconds=value.remaining_seconds,
microseconds=value.microseconds,
)
if isinstance(value, timedelta):
return Duration(
days=value.days,
seconds=value.seconds,
microseconds=value.microseconds,
)
try:
parsed = parse(value, exact=True)

View file

@ -9,7 +9,7 @@ from __future__ import annotations
from collections.abc import Sequence
from dataclasses import dataclass
from functools import partial
from typing import Any, ClassVar, Optional
from typing import Any, ClassVar
from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler
from pydantic_core import PydanticCustomError, core_schema
@ -90,7 +90,7 @@ class PhoneNumberValidator:
supported_regions (list[str]): The supported regions. If empty, all regions are supported (default).
Returns:
str: The formatted phone number.
The formatted phone number.
Example:
MyNumberType = Annotated[
@ -107,9 +107,9 @@ class PhoneNumberValidator:
us_number: USNumberType
"""
default_region: Optional[str] = None
default_region: str | None = None
number_format: str = 'RFC3966'
supported_regions: Optional[Sequence[str]] = None
supported_regions: Sequence[str] | None = None
def __post_init__(self) -> None:
if self.default_region and self.default_region not in phonenumbers.SUPPORTED_REGIONS:
@ -131,7 +131,7 @@ class PhoneNumberValidator:
def _parse(
region: str | None,
number_format: str,
supported_regions: Optional[Sequence[str]],
supported_regions: Sequence[str] | None,
phone_number: Any,
) -> str:
if not phone_number:

View file

@ -2,6 +2,8 @@
[`ABARoutingNumber`][pydantic_extra_types.routing_number.ABARoutingNumber] data type.
"""
from __future__ import annotations
from typing import Any, ClassVar
from pydantic import GetCoreSchemaHandler
@ -54,7 +56,7 @@ class ABARoutingNumber(str):
)
@classmethod
def _validate(cls, __input_value: str, _: core_schema.ValidationInfo) -> 'ABARoutingNumber':
def _validate(cls, __input_value: str, _: core_schema.ValidationInfo) -> ABARoutingNumber:
return cls(__input_value)
@classmethod

View file

@ -53,3 +53,7 @@ class SemanticVersion(semver.Version):
pattern=r'^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'
)
)
@classmethod
def validate_from_str(cls, value: str) -> 'SemanticVersion':
return cls.parse(value)

View file

@ -4,12 +4,13 @@ This class depends on the [semver](https://python-semver.readthedocs.io/en/lates
"""
import warnings
from typing import Annotated, Any, Callable
from typing import Any, Callable
from pydantic import GetJsonSchemaHandler
from pydantic.json_schema import JsonSchemaValue
from pydantic_core import core_schema
from semver import Version
from typing_extensions import Annotated
warnings.warn(
'Use from pydantic_extra_types.semver import SemanticVersion instead. Will be removed in 3.0.0.', DeprecationWarning

View file

@ -57,7 +57,7 @@ def get_timezones() -> set[str]:
class TimeZoneNameSettings(type):
def __new__(cls, name: str, bases: tuple[type, ...], dct: dict[str, Any], **kwargs: Any) -> type[TimeZoneName]:
dct['strict'] = kwargs.pop('strict', True)
return cast(type[TimeZoneName], super().__new__(cls, name, bases, dct))
return cast('type[TimeZoneName]', super().__new__(cls, name, bases, dct))
def __init__(cls, name: str, bases: tuple[type, ...], dct: dict[str, Any], **kwargs: Any) -> None:
super().__init__(name, bases, dct)

View file

@ -92,7 +92,7 @@ keep-runtime-typing = true
[tool.ruff]
line-length = 120
target-version = "py39"
target-version = 'py38'
[tool.ruff.lint]
extend-select = [

View file

@ -3,12 +3,7 @@ from typing import Union
import pycountry
import pytest
from pydantic import BaseModel
try:
from typing import Annotated
except ImportError:
# Python 3.8
from typing import Annotated
from typing_extensions import Annotated
import pydantic_extra_types
from pydantic_extra_types import epoch

View file

@ -92,8 +92,30 @@ def test_pendulum_date_existing_instance(instance):
[
pendulum.duration(days=42, hours=13, minutes=37),
pendulum.duration(days=-42, hours=13, minutes=37),
pendulum.duration(weeks=97),
pendulum.duration(days=463),
pendulum.duration(milliseconds=90122),
pendulum.duration(microseconds=90122),
pendulum.duration(
years=2,
months=3,
weeks=19,
days=1,
hours=25,
seconds=732,
milliseconds=123,
microseconds=1324,
),
timedelta(days=42, hours=13, minutes=37),
timedelta(days=-42, hours=13, minutes=37),
timedelta(
weeks=19,
days=1,
hours=25,
seconds=732,
milliseconds=123,
microseconds=1324,
),
],
)
def test_duration_timedelta__existing_instance(instance):
@ -227,7 +249,11 @@ def test_pendulum_dt_from_str_unix_timestamp_is_utc(dt):
@pytest.mark.parametrize(
'd',
[pendulum.now().date().isoformat(), pendulum.now().to_w3c_string(), pendulum.now().to_iso8601_string()],
[
pendulum.now().date().isoformat(),
pendulum.now().to_w3c_string(),
pendulum.now().to_iso8601_string(),
],
)
def test_pendulum_date_from_serialized(d):
"""Verifies that building an instance from serialized, well-formed strings decode properly."""
@ -308,7 +334,10 @@ def test_pendulum_dt_non_strict_malformed(dt):
DtModelNotStrict(dt=dt)
@pytest.mark.parametrize('invalid_value', [None, 'malformed', pendulum.today().to_iso8601_string()[:5], 'P10Y10M10D'])
@pytest.mark.parametrize(
'invalid_value',
[None, 'malformed', pendulum.today().to_iso8601_string()[:5], 'P10Y10M10D'],
)
def test_pendulum_date_malformed(invalid_value):
"""Verifies that the instance fails to validate if malformed date are passed."""
with pytest.raises(ValidationError):
@ -317,7 +346,14 @@ def test_pendulum_date_malformed(invalid_value):
@pytest.mark.parametrize(
'delta_t',
[None, 'malformed', pendulum.today().to_iso8601_string()[:5], 42, '12m', '2021-01-01T12:00:00'],
[
None,
'malformed',
pendulum.today().to_iso8601_string()[:5],
42,
'12m',
'2021-01-01T12:00:00',
],
)
def test_pendulum_duration_malformed(delta_t):
"""Verifies that the instance fails to validate if malformed durations are passed."""
@ -344,3 +380,9 @@ def test_date_type_adapter(input_type: type, value, is_instance: type):
assert type(validated) is input_type
assert isinstance(validated, input_type)
assert isinstance(validated, is_instance)
def test_pendulum_duration_months_are_preserved():
m = DurationModel(delta_t=pendulum.Duration(months=1))
assert m.delta_t.months == 1

View file

@ -1,16 +1,10 @@
from typing import Any, Optional, Union
try:
from typing import Annotated
except ImportError:
# Python 3.8
from typing import Annotated
import phonenumbers
import pytest
from phonenumbers import PhoneNumber
from pydantic import BaseModel, TypeAdapter, ValidationError
from typing_extensions import Annotated
from pydantic_extra_types.phone_numbers import PhoneNumberValidator

2
uv.lock generated
View file

@ -477,7 +477,7 @@ wheels = [
[[package]]
name = "pydantic-extra-types"
version = "2.10.0"
version = "2.10.1"
source = { editable = "." }
dependencies = [
{ name = "pydantic" },