summaryrefslogtreecommitdiffstats
path: root/pydantic_extra_types
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 02:48:08 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 02:48:08 +0000
commite6b0b20305ffdd91cc0330cf73233f0172891ebc (patch)
tree92b41ba8113b4fe462474506be44c18bb64b8c2e /pydantic_extra_types
parentReleasing debian version 2.6.0-2. (diff)
downloadpydantic-extra-types-e6b0b20305ffdd91cc0330cf73233f0172891ebc.tar.xz
pydantic-extra-types-e6b0b20305ffdd91cc0330cf73233f0172891ebc.zip
Merging upstream version 2.7.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'pydantic_extra_types')
-rw-r--r--pydantic_extra_types/__init__.py2
-rw-r--r--pydantic_extra_types/color.py1
-rw-r--r--pydantic_extra_types/coordinate.py1
-rw-r--r--pydantic_extra_types/country.py1
-rw-r--r--pydantic_extra_types/currency_code.py1
-rw-r--r--pydantic_extra_types/language_code.py212
-rw-r--r--pydantic_extra_types/pendulum_dt.py118
-rw-r--r--pydantic_extra_types/phone_numbers.py1
-rw-r--r--pydantic_extra_types/routing_number.py1
-rw-r--r--pydantic_extra_types/ulid.py1
10 files changed, 337 insertions, 2 deletions
diff --git a/pydantic_extra_types/__init__.py b/pydantic_extra_types/__init__.py
index f0e5e1e..766ce2d 100644
--- a/pydantic_extra_types/__init__.py
+++ b/pydantic_extra_types/__init__.py
@@ -1 +1 @@
-__version__ = '2.6.0'
+__version__ = '2.7.0'
diff --git a/pydantic_extra_types/color.py b/pydantic_extra_types/color.py
index 34aa441..8b9eabe 100644
--- a/pydantic_extra_types/color.py
+++ b/pydantic_extra_types/color.py
@@ -7,6 +7,7 @@ A few colors have multiple names referring to the sames colors, eg. `grey` and `
In these cases the _last_ color when sorted alphabetically takes preferences,
eg. `Color((0, 255, 255)).as_named() == 'cyan'` because "cyan" comes after "aqua".
"""
+
from __future__ import annotations
import math
diff --git a/pydantic_extra_types/coordinate.py b/pydantic_extra_types/coordinate.py
index df470d5..10eaa05 100644
--- a/pydantic_extra_types/coordinate.py
+++ b/pydantic_extra_types/coordinate.py
@@ -3,6 +3,7 @@ The `pydantic_extra_types.coordinate` module provides the [`Latitude`][pydantic_
[`Longitude`][pydantic_extra_types.coordinate.Longitude], and
[`Coordinate`][pydantic_extra_types.coordinate.Coordinate] data types.
"""
+
from dataclasses import dataclass
from typing import Any, ClassVar, Tuple, Type
diff --git a/pydantic_extra_types/country.py b/pydantic_extra_types/country.py
index a6d26e2..7af99c7 100644
--- a/pydantic_extra_types/country.py
+++ b/pydantic_extra_types/country.py
@@ -1,6 +1,7 @@
"""
Country definitions that are based on the [ISO 3166](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes).
"""
+
from __future__ import annotations
from dataclasses import dataclass
diff --git a/pydantic_extra_types/currency_code.py b/pydantic_extra_types/currency_code.py
index c19d9bf..fbc0cbd 100644
--- a/pydantic_extra_types/currency_code.py
+++ b/pydantic_extra_types/currency_code.py
@@ -1,6 +1,7 @@
"""
Currency definitions that are based on the [ISO4217](https://en.wikipedia.org/wiki/ISO_4217).
"""
+
from __future__ import annotations
from typing import Any
diff --git a/pydantic_extra_types/language_code.py b/pydantic_extra_types/language_code.py
index 117e877..8c15385 100644
--- a/pydantic_extra_types/language_code.py
+++ b/pydantic_extra_types/language_code.py
@@ -1,9 +1,12 @@
"""
Language definitions that are based on the [ISO 639-3](https://en.wikipedia.org/wiki/ISO_639-3) & [ISO 639-5](https://en.wikipedia.org/wiki/ISO_639-5).
"""
+
from __future__ import annotations
-from typing import Any
+from dataclasses import dataclass
+from functools import lru_cache
+from typing import Any, Union
from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler
from pydantic_core import PydanticCustomError, core_schema
@@ -17,6 +20,213 @@ except ModuleNotFoundError: # pragma: no cover
)
+@dataclass
+class LanguageInfo:
+ """
+ LanguageInfo is a dataclass that contains the language information.
+
+ Args:
+ alpha2: The language code in the [ISO 639-1 alpha-2](https://en.wikipedia.org/wiki/ISO_639-1) format.
+ alpha3: The language code in the [ISO 639-3 alpha-3](https://en.wikipedia.org/wiki/ISO_639-3) format.
+ name: The language name.
+ """
+
+ alpha2: Union[str, None]
+ alpha3: str
+ name: str
+
+
+@lru_cache
+def _languages() -> list[LanguageInfo]:
+ """
+ Return a list of LanguageInfo objects containing the language information.
+
+ Returns:
+ A list of LanguageInfo objects containing the language information.
+ """
+ return [
+ LanguageInfo(
+ alpha2=getattr(language, 'alpha_2', None),
+ alpha3=language.alpha_3,
+ name=language.name,
+ )
+ for language in pycountry.languages
+ ]
+
+
+@lru_cache
+def _index_by_alpha2() -> dict[str, LanguageInfo]:
+ """
+ Return a dictionary with the language code in the [ISO 639-1 alpha-2](https://en.wikipedia.org/wiki/ISO_639-1) format as the key and the LanguageInfo object as the value.
+ """
+ return {language.alpha2: language for language in _languages() if language.alpha2 is not None}
+
+
+@lru_cache
+def _index_by_alpha3() -> dict[str, LanguageInfo]:
+ """
+ Return a dictionary with the language code in the [ISO 639-3 alpha-3](https://en.wikipedia.org/wiki/ISO_639-3) format as the key and the LanguageInfo object as the value.
+ """
+ return {language.alpha3: language for language in _languages()}
+
+
+@lru_cache
+def _index_by_name() -> dict[str, LanguageInfo]:
+ """
+ Return a dictionary with the language name as the key and the LanguageInfo object as the value.
+ """
+ return {language.name: language for language in _languages()}
+
+
+class LanguageAlpha2(str):
+ """LanguageAlpha2 parses languages codes in the [ISO 639-1 alpha-2](https://en.wikipedia.org/wiki/ISO_639-1)
+ format.
+
+ ```py
+ from pydantic import BaseModel
+
+ from pydantic_extra_types.language_code import LanguageAlpha2
+
+ class Movie(BaseModel):
+ audio_lang: LanguageAlpha2
+ subtitles_lang: LanguageAlpha2
+
+ movie = Movie(audio_lang='de', subtitles_lang='fr')
+ print(movie)
+ #> audio_lang='de' subtitles_lang='fr'
+ ```
+ """
+
+ @classmethod
+ def _validate(cls, __input_value: str, _: core_schema.ValidationInfo) -> LanguageAlpha2:
+ """
+ Validate a language code in the ISO 639-1 alpha-2 format from the provided str value.
+
+ Args:
+ __input_value: The str value to be validated.
+ _: The Pydantic ValidationInfo.
+
+ Returns:
+ The validated language code in the ISO 639-1 alpha-2 format.
+ """
+ if __input_value not in _index_by_alpha2():
+ raise PydanticCustomError('language_alpha2', 'Invalid language alpha2 code')
+ return cls(__input_value)
+
+ @classmethod
+ def __get_pydantic_core_schema__(
+ cls, source: type[Any], handler: GetCoreSchemaHandler
+ ) -> core_schema.AfterValidatorFunctionSchema:
+ """
+ Return a Pydantic CoreSchema with the language code in the ISO 639-1 alpha-2 format validation.
+
+ Args:
+ source: The source type.
+ handler: The handler to get the CoreSchema.
+
+ Returns:
+ A Pydantic CoreSchema with the language code in the ISO 639-1 alpha-2 format validation.
+ """
+ return core_schema.with_info_after_validator_function(
+ cls._validate,
+ core_schema.str_schema(to_lower=True),
+ )
+
+ @classmethod
+ def __get_pydantic_json_schema__(
+ cls, schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
+ ) -> dict[str, Any]:
+ """
+ Return a Pydantic JSON Schema with the language code in the ISO 639-1 alpha-2 format validation.
+
+ Args:
+ schema: The Pydantic CoreSchema.
+ handler: The handler to get the JSON Schema.
+
+ Returns:
+ A Pydantic JSON Schema with the language code in the ISO 639-1 alpha-2 format validation.
+ """
+ json_schema = handler(schema)
+ json_schema.update({'pattern': r'^\w{2}$'})
+ return json_schema
+
+ @property
+ def alpha3(self) -> str:
+ """The language code in the [ISO 639-3 alpha-3](https://en.wikipedia.org/wiki/ISO_639-3) format."""
+ return _index_by_alpha2()[self].alpha3
+
+ @property
+ def name(self) -> str:
+ """The language name."""
+ return _index_by_alpha2()[self].name
+
+
+class LanguageName(str):
+ """LanguageName parses languages names listed in the [ISO 639-3 standard](https://en.wikipedia.org/wiki/ISO_639-3)
+ format.
+
+ ```py
+ from pydantic import BaseModel
+
+ from pydantic_extra_types.language_code import LanguageName
+
+ class Movie(BaseModel):
+ audio_lang: LanguageName
+ subtitles_lang: LanguageName
+
+ movie = Movie(audio_lang='Dutch', subtitles_lang='Mandarin Chinese')
+ print(movie)
+ #> audio_lang='Dutch' subtitles_lang='Mandarin Chinese'
+ ```
+ """
+
+ @classmethod
+ def _validate(cls, __input_value: str, _: core_schema.ValidationInfo) -> LanguageName:
+ """
+ Validate a language name from the provided str value.
+
+ Args:
+ __input_value: The str value to be validated.
+ _: The Pydantic ValidationInfo.
+
+ Returns:
+ The validated language name.
+ """
+ if __input_value not in _index_by_name():
+ raise PydanticCustomError('language_name', 'Invalid language name')
+ return cls(__input_value)
+
+ @classmethod
+ def __get_pydantic_core_schema__(
+ cls, source: type[Any], handler: GetCoreSchemaHandler
+ ) -> core_schema.AfterValidatorFunctionSchema:
+ """
+ Return a Pydantic CoreSchema with the language name validation.
+
+ Args:
+ source: The source type.
+ handler: The handler to get the CoreSchema.
+
+ Returns:
+ A Pydantic CoreSchema with the language name validation.
+ """
+ return core_schema.with_info_after_validator_function(
+ cls._validate,
+ core_schema.str_schema(),
+ serialization=core_schema.to_string_ser_schema(),
+ )
+
+ @property
+ def alpha2(self) -> Union[str, None]:
+ """The language code in the [ISO 639-1 alpha-2](https://en.wikipedia.org/wiki/ISO_639-1) format. Does not exist for all languages."""
+ return _index_by_name()[self].alpha2
+
+ @property
+ def alpha3(self) -> str:
+ """The language code in the [ISO 639-3 alpha-3](https://en.wikipedia.org/wiki/ISO_639-3) format."""
+ return _index_by_name()[self].alpha3
+
+
class ISO639_3(str):
"""ISO639_3 parses Language in the [ISO 639-3 alpha-3](https://en.wikipedia.org/wiki/ISO_639-3_alpha-3)
format.
diff --git a/pydantic_extra_types/pendulum_dt.py b/pydantic_extra_types/pendulum_dt.py
index f507779..f3a304f 100644
--- a/pydantic_extra_types/pendulum_dt.py
+++ b/pydantic_extra_types/pendulum_dt.py
@@ -4,7 +4,9 @@ CoreSchema implementation. This allows Pydantic to validate the DateTime object.
"""
try:
+ from pendulum import Date as _Date
from pendulum import DateTime as _DateTime
+ from pendulum import Duration as _Duration
from pendulum import parse
except ModuleNotFoundError: # pragma: no cover
raise RuntimeError(
@@ -72,3 +74,119 @@ class DateTime(_DateTime):
except Exception as exc:
raise PydanticCustomError('value_error', 'value is not a valid timestamp') from exc
return handler(data)
+
+
+class Date(_Date):
+ """
+ A `pendulum.Date` object. At runtime, this type decomposes into pendulum.Date automatically.
+ This type exists because Pydantic throws a fit on unknown types.
+
+ ```python
+ from pydantic import BaseModel
+ from pydantic_extra_types.pendulum_dt import Date
+
+ class test_model(BaseModel):
+ dt: Date
+
+ print(test_model(dt='2021-01-01'))
+
+ #> test_model(dt=Date(2021, 1, 1))
+ ```
+ """
+
+ __slots__: List[str] = []
+
+ @classmethod
+ def __get_pydantic_core_schema__(cls, source: Type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+ """
+ Return a Pydantic CoreSchema with the Date validation
+
+ Args:
+ source: The source type to be converted.
+ handler: The handler to get the CoreSchema.
+
+ Returns:
+ A Pydantic CoreSchema with the Date validation.
+ """
+ return core_schema.no_info_wrap_validator_function(cls._validate, core_schema.date_schema())
+
+ @classmethod
+ def _validate(cls, value: Any, handler: core_schema.ValidatorFunctionWrapHandler) -> Any:
+ """
+ Validate the date object and return it.
+
+ Args:
+ value: The value to validate.
+ handler: The handler to get the CoreSchema.
+
+ Returns:
+ The validated value or raises a PydanticCustomError.
+ """
+ # if we are passed an existing instance, pass it straight through.
+ if isinstance(value, _Date):
+ return handler(value)
+
+ # otherwise, parse it.
+ try:
+ data = parse(value)
+ except Exception as exc:
+ raise PydanticCustomError('value_error', 'value is not a valid date') from exc
+ return handler(data)
+
+
+class Duration(_Duration):
+ """
+ A `pendulum.Duration` object. At runtime, this type decomposes into pendulum.Duration automatically.
+ This type exists because Pydantic throws a fit on unknown types.
+
+ ```python
+ from pydantic import BaseModel
+ from pydantic_extra_types.pendulum_dt import Duration
+
+ class test_model(BaseModel):
+ delta_t: Duration
+
+ print(test_model(delta_t='P1DT25H'))
+
+ #> test_model(delta_t=Duration(days=2, hours=1))
+ ```
+ """
+
+ __slots__: List[str] = []
+
+ @classmethod
+ def __get_pydantic_core_schema__(cls, source: Type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+ """
+ Return a Pydantic CoreSchema with the Duration validation
+
+ Args:
+ source: The source type to be converted.
+ handler: The handler to get the CoreSchema.
+
+ Returns:
+ A Pydantic CoreSchema with the Duration validation.
+ """
+ return core_schema.no_info_wrap_validator_function(cls._validate, core_schema.timedelta_schema())
+
+ @classmethod
+ def _validate(cls, value: Any, handler: core_schema.ValidatorFunctionWrapHandler) -> Any:
+ """
+ Validate the Duration object and return it.
+
+ Args:
+ value: The value to validate.
+ handler: The handler to get the CoreSchema.
+
+ Returns:
+ The validated value or raises a PydanticCustomError.
+ """
+ # if we are passed an existing instance, pass it straight through.
+ if isinstance(value, _Duration):
+ return handler(value)
+
+ # otherwise, parse it.
+ try:
+ data = parse(value)
+ except Exception as exc:
+ raise PydanticCustomError('value_error', 'value is not a valid duration') from exc
+ return handler(data)
diff --git a/pydantic_extra_types/phone_numbers.py b/pydantic_extra_types/phone_numbers.py
index 7acaa89..cf03417 100644
--- a/pydantic_extra_types/phone_numbers.py
+++ b/pydantic_extra_types/phone_numbers.py
@@ -4,6 +4,7 @@ The `pydantic_extra_types.phone_numbers` module provides the
This class depends on the [phonenumbers] package, which is a Python port of Google's [libphonenumber].
"""
+
from __future__ import annotations
from typing import Any, Callable, ClassVar, Generator
diff --git a/pydantic_extra_types/routing_number.py b/pydantic_extra_types/routing_number.py
index 22ea6e8..b4d53a8 100644
--- a/pydantic_extra_types/routing_number.py
+++ b/pydantic_extra_types/routing_number.py
@@ -2,6 +2,7 @@
The `pydantic_extra_types.routing_number` module provides the
[`ABARoutingNumber`][pydantic_extra_types.routing_number.ABARoutingNumber] data type.
"""
+
from typing import Any, ClassVar, Type
from pydantic import GetCoreSchemaHandler
diff --git a/pydantic_extra_types/ulid.py b/pydantic_extra_types/ulid.py
index d2bf650..5891f9f 100644
--- a/pydantic_extra_types/ulid.py
+++ b/pydantic_extra_types/ulid.py
@@ -3,6 +3,7 @@ The `pydantic_extra_types.ULID` module provides the [`ULID`] data type.
This class depends on the [python-ulid] package, which is a validate by the [ULID-spec](https://github.com/ulid/spec#implementations-in-other-languages).
"""
+
from __future__ import annotations
from dataclasses import dataclass