1
0
Fork 0

Merging upstream version 2.7.0.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-05 14:03:42 +01:00
parent 9120759ada
commit cf6ffde11c
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
18 changed files with 588 additions and 84 deletions

View file

@ -1,82 +1,97 @@
# CHANGELOG
## v2.6.0
## Latest Changes
* Allow python-ulid 2.x on Python 3.9 and later by @musicinmybrain in <https://github.com/pydantic/pydantic-extra-types/pull/131>
* Do not pin the ”major” version of pycountry by @musicinmybrain in <https://github.com/pydantic/pydantic-extra-types/pull/132>
* 🤖 Create dependabot.yml for updating GitHub action by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/134>
* :memo: Refactor Documentation for ISBN and MAC address modules by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/124>
* Add language code definitions and test by @07pepa in <https://github.com/pydantic/pydantic-extra-types/pull/141>
* :memo: Create a `changelog` to match release notes by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/142>
* Add currency code ISO 4217 and its subset that includes only currencies by @07pepa in <https://github.com/pydantic/pydantic-extra-types/pull/143>
* 🔨 Update code formatting and linting configurations by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/144>
* 👷 Add Python checking for dependencies by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/145>
* 🐛 fix single quote issue by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/148>
## 2.7.0
## v2.5.0
* 🔥 Remove latest-changes workflow. PR [#165](https://github.com/pydantic/pydantic-extra-types/pull/165) by [yezz123](https://github.com/yezz123)
* 🔨 Add latest-changes workflow to generate Changes. PR [#164](https://github.com/pydantic/pydantic-extra-types/pull/164) by [yezz123](https://github.com/yezz123)
* Added LanguageAlpha2 and LanguageName types. PR [#153](https://github.com/pydantic/pydantic-extra-types/pull/153) by [odelmarcelle](https://github.com/odelmarcelle)
* Added support for pendulum Dates. PR [#154](https://github.com/pydantic/pydantic-extra-types/pull/154) by [Woody1193](https://github.com/Woody1193)
* Add support for pendulum Duration. PR [#162](https://github.com/pydantic/pydantic-extra-types/pull/162) by [tempookian](https://github.com/tempookian)
* Add Pendulum DT support by @theunkn0wn1 in <https://github.com/pydantic/pydantic-extra-types/pull/110>
### Dependencies
## v2.4.1
* ⬆ Bump the python-packages group with 1 update. PR [#150](https://github.com/pydantic/pydantic-extra-types/pull/150) by [dependabot](https://github.com/dependabot)
* ⬆ Bump the python-packages group with 6 updates. PR [#160](https://github.com/pydantic/pydantic-extra-types/pull/160) by [dependabot](https://github.com/dependabot)
* Fix refs blocking docs build by @sydney-runkle in <https://github.com/pydantic/pydantic-extra-types/pull/125>
## 2.6.0
## v2.4.0
* Allow python-ulid 2.x on Python 3.9 and later. PR [#131](https://github.com/pydantic/pydantic-extra-types/pull/131) by [@musicinmybrain](https://github.com/musicinmybrain)
* Do not pin the ”major” version of pycountry. PR [#132](https://github.com/pydantic/pydantic-extra-types/pull/132) by [@musicinmybrain](https://github.com/musicinmybrain)
* 🤖 Create dependabot.yml for updating GitHub action. PR [#134](https://github.com/pydantic/pydantic-extra-types/pull/134) by [@yezz123](https://github.com/yezz123)
* Refactor Documentation for ISBN and MAC address modules. PR [#124](https://github.com/pydantic/pydantic-extra-types/pull/124) by [@yezz123](https://github.com/yezz123)
* Add language code definitions and test. PR [#141](https://github.com/pydantic/pydantic-extra-types/pull/141) by [@07pepa](https://github.com/07pepa)
* Create a `changelog` to match release notes. PR [#142](https://github.com/pydantic/pydantic-extra-types/pull/142) by [@yezz123](https://github.com/yezz123)
* Add currency code ISO 4217 and its subset that includes only currencies. PR [#143](https://github.com/pydantic/pydantic-extra-types/pull/143) by [@07pepa](https://github.com/07pepa)
* 🔨 Update code formatting and linting configurations. PR [#144](https://github.com/pydantic/pydantic-extra-types/pull/144) by [@yezz123](https://github.com/yezz123)
* 👷 Add Python checking for dependencies. PR [#145](https://github.com/pydantic/pydantic-extra-types/pull/145) by [@yezz123](https://github.com/yezz123)
* 🐛 Fix single quote issue. PR [#148](https://github.com/pydantic/pydantic-extra-types/pull/148) by [@yezz123](https://github.com/yezz123)
* Add: New type ISBN by @lucasmucidas in <https://github.com/pydantic/pydantic-extra-types/pull/116>
* fix validate_digits actually allowing non digit characters by @romaincaillon in <https://github.com/pydantic/pydantic-extra-types/pull/120>
* ♻️ refactor the `validate_brand` method & add new types by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/56>
* ✅ Drop python 3.7 & support 3.12 by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/122>
## 2.5.0
## v2.3.0
* Add Pendulum DT support. PR [#110](https://github.com/pydantic/pydantic-extra-types/pull/110) by [@theunkn0wn1](https://github.com/theunkn0wn1)
* Upgrade pydantic version to >=2.5.2 by @hramezani in <https://github.com/pydantic/pydantic-extra-types/pull/113>
## 2.4.1
## v.2.2.0
* Fix refs blocking docs build. PR [#125](https://github.com/pydantic/pydantic-extra-types/pull/125) by [@sydney-runkle](https://github.com/sydney-runkle)
* Add `long` and `short` format to `as_hex` by @DJRHails in <https://github.com/pydantic/pydantic-extra-types/pull/93>
* Refactor documentation by @Kludex in <https://github.com/pydantic/pydantic-extra-types/pull/98>
* ✨ add `ULID` type by @JeanArhancet in <https://github.com/pydantic/pydantic-extra-types/pull/73>
* Added `__get_pydantic_json_schema__` method with `format='tel'` by @hasansezertasan in <https://github.com/pydantic/pydantic-extra-types/pull/106>
## 2.4.0
## v2.1.0
* Add: New type ISBN. PR [#116](https://github.com/pydantic/pydantic-extra-types/pull/116) by [lucasmucidas](https://github.com/lucasmucidas)
* Fix validate_digits actually allowing non-digit characters. PR [#120](https://github.com/pydantic/pydantic-extra-types/pull/120) by [romaincaillon](https://github.com/romaincaillon)
* Refactor the `validate_brand` method & add new types. PR [#56](https://github.com/pydantic/pydantic-extra-types/pull/56) by [yezz123](https://github.com/yezz123)
* Drop Python 3.7 & support 3.12. PR [#122](https://github.com/pydantic/pydantic-extra-types/pull/122) by [yezz123](https://github.com/yezz123)
* ✨ add `MacAddress` type by @JeanArhancet in <https://github.com/pydantic/pydantic-extra-types/pull/71>
* :memo: fix usage of `MAC address` by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/72>
* Add docstrings for payment cards by @tpdorsey in <https://github.com/pydantic/pydantic-extra-types/pull/77>
* Fix mac adddress validation by @JeanArhancet in <https://github.com/pydantic/pydantic-extra-types/pull/79>
* Remove work in progress part from README.md by @hramezani in <https://github.com/pydantic/pydantic-extra-types/pull/81>
* Add `Latitude`, `Longitude` and `Coordinate` by @JeanArhancet in <https://github.com/pydantic/pydantic-extra-types/pull/76>
* Refactor: use stdlib and remove useless code by @eumiro in <https://github.com/pydantic/pydantic-extra-types/pull/86>
* Make Latitude and Longitude evaluated by @Kludex in <https://github.com/pydantic/pydantic-extra-types/pull/90>
## 2.3.0
## v2.0.0
* Upgrade pydantic version to >=2.5.2. PR [#113](https://github.com/pydantic/pydantic-extra-types/pull/113) by [hramezani](https://github.com/hramezani)
* Migrate `Color` & `Payment Card` by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/2>
* add `pydantic` to classifiers by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/13>
* remove dependencies caching by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/16>
* :bug: deprecate `__modify_schema__` method by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/20>
* Fix Color JSON schema generation by @dmontagu in <https://github.com/pydantic/pydantic-extra-types/pull/21>
* fix issues of `pydantic_core.core_schema` has no attribute `xxx` by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/23>
* Fix Failed tests for `color` type by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/26>
* Created Country type by @HomiGrotas in <https://github.com/pydantic/pydantic-extra-types/pull/14>
* Add phone number types by @JamesHutchison in <https://github.com/pydantic/pydantic-extra-types/pull/25>
* make `phonenumbers` a requirement by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/29>
* chore(feat): Add ABARouting number type by @RevinderDev in <https://github.com/pydantic/pydantic-extra-types/pull/30>
* add missing countries by @EssaAlshammri in <https://github.com/pydantic/pydantic-extra-types/pull/32>
* chore: resolve `pydantic-core` dependency conflict by @hirotasoshu in <https://github.com/pydantic/pydantic-extra-types/pull/45>
* Add `MIR` card brand by @hirotasoshu in <https://github.com/pydantic/pydantic-extra-types/pull/46>
* fix dependencies version by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/48>
* 📝 Add documentation for `Color` and `PaymentCardNumber` by @Kludex in <https://github.com/pydantic/pydantic-extra-types/pull/50>
* Add hooky by @Kludex in <https://github.com/pydantic/pydantic-extra-types/pull/51>
* ♻️ Simplify project structure by @Kludex in <https://github.com/pydantic/pydantic-extra-types/pull/52>
* 👷 Add coverage check on the pipeline by @Kludex in <https://github.com/pydantic/pydantic-extra-types/pull/53>
* ♻️ refactor country type using `pycountry` by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/54>
* ✅ Add 100% coverage by @Kludex in <https://github.com/pydantic/pydantic-extra-types/pull/57>
* Add support for transparent Color by @CollinHeist in <https://github.com/pydantic/pydantic-extra-types/pull/59>
* 📝 Add documentation for `PhoneNumber` and `ABARoutingNumber` by @Kludex in <https://github.com/pydantic/pydantic-extra-types/pull/60>
* 📝 Refactor README by @Kludex in <https://github.com/pydantic/pydantic-extra-types/pull/61>
* 🚚 Rename `routing_number.md` to `routing_numbers.md` by @Kludex in <https://github.com/pydantic/pydantic-extra-types/pull/62>
* :memo: fix code in `payment` documentation by @yezz123 in <https://github.com/pydantic/pydantic-extra-types/pull/63>
* uprev pydantic to b3 by @samuelcolvin in <https://github.com/pydantic/pydantic-extra-types/pull/69>
* Prepare for release 2.0.0 by @hramezani in <https://github.com/pydantic/pydantic-extra-types/pull/70>
## 2.2.0
* Add `long` and `short` format to `as_hex`. PR [#93](https://github.com/pydantic/pydantic-extra-types/pull/93) by [DJRHails](https://github.com/DJRHails)
* Refactor documentation. PR [#98](https://github.com/pydantic/pydantic-extra-types/pull/98) by [Kludex](https://github.com/Kludex)
* Add `ULID` type. PR [#73](https://github.com/pydantic/pydantic-extra-types/pull/73) by [JeanArhancet](https://github.com/JeanArhancet)
* Add `__get_pydantic_json_schema__` method with `format='tel'`. PR [#106](https://github.com/pydantic/pydantic-extra-types/pull/106) by [hasansezertasan](https://github.com/hasansezertasan)
## 2.1.0
* Add `MacAddress` type. PR [#71](https://github.com/pydantic/pydantic-extra-types/pull/71) by [JeanArhancet](https://github.com/JeanArhancet)
* Fix usage of `MAC address`. PR [#72](https://github.com/pydantic/pydantic-extra-types/pull/72) by [yezz123](https://github.com/yezz123)
* Add docstrings for payment cards. PR [#77](https://github.com/pydantic/pydantic-extra-types/pull/77) by [tpdorsey](https://github.com/tpdorsey)
* Fix MAC address validation. PR [#79](https://github.com/pydantic/pydantic-extra-types/pull/79) by [JeanArhancet](https://github.com/JeanArhancet)
* Remove work in progress part from README.md. PR [#81](https://github.com/pydantic/pydantic-extra-types/pull/81) by [hramezani](https://github.com/hramezani)
* Add `Latitude`, `Longitude`, and `Coordinate`. PR [#76](https://github.com/pydantic/pydantic-extra-types/pull/76) by [JeanArhancet](https://github.com/JeanArhancet)
* Refactor: use stdlib and remove useless code. PR [#86](https://github.com/pydantic/pydantic-extra-types/pull/86) by [eumiro](https://github.com/eumiro)
* Make Latitude and Longitude evaluated. PR [#90](https://github.com/pydantic/pydantic-extra-types/pull/90) by [Kludex](https://github.com/Kludex)
## 2.0.0
* Migrate `Color` & `Payment Card`. PR [#2](https://github.com/pydantic/pydantic-extra-types/pull/2) by [yezz123](https://github.com/yezz123)
* Add `pydantic` to classifiers. PR [#13](https://github.com/pydantic/pydantic-extra-types/pull/13) by [yezz123](https://github.com/yezz123)
* Remove dependencies caching. PR [#16](https://github.com/pydantic/pydantic-extra-types/pull/16) by [yezz123](https://github.com/yezz123)
* Deprecate `__modify_schema__` method. PR [#20](https://github.com/pydantic/pydantic-extra-types/pull/20) by [yezz123](https://github.com/yezz123)
* Fix Color JSON schema generation. PR [#21](https://github.com/pydantic/pydantic-extra-types/pull/21) by [dmontagu](https://github.com/dmontagu)
* Fix issues of `pydantic_core.core_schema` has no attribute `xxx`. PR [#23](https://github.com/pydantic/pydantic-extra-types/pull/23) by [yezz123](https://github.com/yezz123)
* Fix Failed tests for `color` type. PR [#26](https://github.com/pydantic/pydantic-extra-types/pull/26) by [yezz123](https://github.com/yezz123)
* Created Country type. PR [#14](https://github.com/pydantic/pydantic-extra-types/pull/14) by [HomiGrotas](https://github.com/HomiGrotas)
* Add phone number types. PR [#25](https://github.com/pydantic/pydantic-extra-types/pull/25) by [JamesHutchison](https://github.com/JamesHutchison)
* Make `phonenumbers` a requirement. PR [#29](https://github.com/pydantic/pydantic-extra-types/pull/29) by [yezz123](https://github.com/yezz123)
* Add ABARouting number type. PR [#30](https://github.com/pydantic/pydantic-extra-types/pull/30) by [RevinderDev](https://github.com/RevinderDev)
* Add missing countries. PR [#32](https://github.com/pydantic/pydantic-extra-types/pull/32) by [EssaAlshammri](https://github.com/EssaAlshammri)
* Resolve `pydantic-core` dependency conflict. PR [#45](https://github.com/pydantic/pydantic-extra-types/pull/45) by [hirotasoshu](https://github.com/hirotasoshu)
* Add `MIR` card brand. PR [#46](https://github.com/pydantic/pydantic-extra-types/pull/46) by [hirotasoshu](https://github.com/hirotasoshu)
* Fix dependencies version. PR [#48](https://github.com/pydantic/pydantic-extra-types/pull/48) by [yezz123](https://github.com/yezz123)
* Add documentation for `Color` and `PaymentCardNumber`. PR [#50](https://github.com/pydantic/pydantic-extra-types/pull/50) by [Kludex](https://github.com/Kludex)
* Add hooky. PR [#51](https://github.com/pydantic/pydantic-extra-types/pull/51) by [Kludex](https://github.com/Kludex)
* Simplify project structure. PR [#52](https://github.com/pydantic/pydantic-extra-types/pull/52) by [Kludex](https://github.com/Kludex)
* Add coverage check on the pipeline. PR [#53](https://github.com/pydantic/pydantic-extra-types/pull/53) by [Kludex](https://github.com/Kludex)
* Refactor country type using `pycountry`. PR [#54](https://github.com/pydantic/pydantic-extra-types/pull/54) by [yezz123](https://github.com/yezz123)
* Add 100% coverage. PR [#57](https://github.com/pydantic/pydantic-extra-types/pull/57) by [Kludex](https://github.com/Kludex)
* Add support for transparent Color. PR [#59](https://github.com/pydantic/pydantic-extra-types/pull/59) by [CollinHeist](https://github.com/CollinHeist)
* Add documentation for `PhoneNumber` and `ABARoutingNumber`. PR [#60](https://github.com/pydantic/pydantic-extra-types/pull/60) by [Kludex](https://github.com/Kludex)
* Refactor README. PR [#61](https://github.com/pydantic/pydantic-extra-types/pull/61) by [Kludex](https://github.com/Kludex)
* Rename `routing_number.md` to `routing_numbers.md`. PR [#62](https://github.com/pydantic/pydantic-extra-types/pull/62) by [Kludex](https://github.com/Kludex)
* Fix code in `payment` documentation. PR [#63](https://github.com/pydantic/pydantic-extra-types/pull/63) by [yezz123](https://github.com/yezz123)
* Uprev pydantic to b3. PR [#69](https://github.com/pydantic/pydantic-extra-types/pull/69) by [samuelcolvin](https://github.com/samuelcolvin)
* Prepare for release 2.0.0. PR [#70](https://github.com/pydantic/pydantic-extra-types/pull/70) by [hramezani](https://github.com/hramezani)

View file

@ -18,12 +18,12 @@ refresh-lockfiles:
.PHONY: format
format:
ruff --fix $(sources)
ruff check --fix $(sources)
ruff format $(sources)
.PHONY: lint
lint:
ruff $(sources)
ruff check $(sources)
ruff format --check $(sources)
.PHONY: mypy

View file

@ -1 +1 @@
__version__ = '2.6.0'
__version__ = '2.7.0'

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -14,7 +14,7 @@ filelock==3.13.1
# via virtualenv
identify==2.5.35
# via pre-commit
mypy==1.8.0
mypy==1.9.0
# via -r requirements/linting.in
mypy-extensions==1.0.0
# via mypy
@ -22,11 +22,11 @@ nodeenv==1.8.0
# via pre-commit
platformdirs==4.2.0
# via virtualenv
pre-commit==3.6.2
pre-commit==3.7.0
# via -r requirements/linting.in
pyyaml==6.0.1
# via pre-commit
ruff==0.2.2
ruff==0.3.5
# via -r requirements/linting.in
typing-extensions==4.10.0
# via mypy

View file

@ -10,7 +10,7 @@ charset-normalizer==3.3.2
# via requests
codecov==2.1.13
# via -r requirements/testing.in
coverage[toml]==7.4.3
coverage[toml]==7.4.4
# via
# -r requirements/testing.in
# codecov
@ -31,12 +31,12 @@ pluggy==1.4.0
# via pytest
pygments==2.17.2
# via rich
pytest==8.0.2
pytest==8.1.1
# via
# -r requirements/testing.in
# pytest-cov
# pytest-pretty
pytest-cov==4.1.0
pytest-cov==5.0.0
# via -r requirements/testing.in
pytest-pretty==1.2.0
# via -r requirements/testing.in

View file

@ -13,7 +13,7 @@ from pydantic_extra_types.country import (
)
from pydantic_extra_types.currency_code import ISO4217, Currency
from pydantic_extra_types.isbn import ISBN
from pydantic_extra_types.language_code import ISO639_3, ISO639_5
from pydantic_extra_types.language_code import ISO639_3, ISO639_5, LanguageAlpha2, LanguageName
from pydantic_extra_types.mac_address import MacAddress
from pydantic_extra_types.payment import PaymentCardNumber
from pydantic_extra_types.pendulum_dt import DateTime
@ -219,6 +219,24 @@ everyday_currencies.sort()
'type': 'object',
},
),
(
LanguageAlpha2,
{
'properties': {'x': {'pattern': '^\\w{2}$', 'title': 'X', 'type': 'string'}},
'required': ['x'],
'title': 'Model',
'type': 'object',
},
),
(
LanguageName,
{
'properties': {'x': {'title': 'X', 'type': 'string'}},
'required': ['x'],
'title': 'Model',
'type': 'object',
},
),
(
ISO639_3,
{

View file

@ -1,10 +1,37 @@
import re
from string import printable
import pycountry
import pytest
from pydantic import BaseModel, ValidationError
from pydantic_extra_types import language_code
from pydantic_extra_types.language_code import (
LanguageAlpha2,
LanguageInfo,
LanguageName,
_index_by_alpha2,
_index_by_alpha3,
_index_by_name,
)
PARAMS_AMOUNT = 20
@pytest.fixture(scope='module', name='MovieAlpha2')
def movie_alpha2_fixture():
class Movie(BaseModel):
audio_lang: LanguageAlpha2
return Movie
@pytest.fixture(scope='module', name='MovieName')
def movie_name_fixture():
class Movie(BaseModel):
audio_lang: LanguageName
return Movie
class ISO3CheckingModel(BaseModel):
@ -15,6 +42,34 @@ class ISO5CheckingModel(BaseModel):
lang: language_code.ISO639_5
@pytest.mark.parametrize('alpha2, language_data', list(_index_by_alpha2().items()))
def test_valid_alpha2(alpha2: str, language_data: LanguageInfo, MovieAlpha2):
the_godfather = MovieAlpha2(audio_lang=alpha2)
assert the_godfather.audio_lang == language_data.alpha2
assert the_godfather.audio_lang.alpha3 == language_data.alpha3
assert the_godfather.audio_lang.name == language_data.name
@pytest.mark.parametrize('alpha2', list(printable) + list(_index_by_alpha3().keys())[:PARAMS_AMOUNT])
def test_invalid_alpha2(alpha2: str, MovieAlpha2):
with pytest.raises(ValidationError, match='Invalid language alpha2 code'):
MovieAlpha2(audio_lang=alpha2)
@pytest.mark.parametrize('name, language_data', list(_index_by_name().items())[:PARAMS_AMOUNT])
def test_valid_name(name: str, language_data: LanguageInfo, MovieName):
the_godfather = MovieName(audio_lang=name)
assert the_godfather.audio_lang == language_data.name
assert the_godfather.audio_lang.alpha2 == language_data.alpha2
assert the_godfather.audio_lang.alpha3 == language_data.alpha3
@pytest.mark.parametrize('name', set(printable) - {'E', 'U'}) # E and U are valid language codes
def test_invalid_name(name: str, MovieName):
with pytest.raises(ValidationError, match='Invalid language name'):
MovieName(audio_lang=name)
@pytest.mark.parametrize('lang', map(lambda lang: lang.alpha_3, pycountry.languages))
def test_iso_ISO639_3_code_ok(lang: str):
model = ISO3CheckingModel(lang=lang)

View file

@ -2,38 +2,119 @@ import pendulum
import pytest
from pydantic import BaseModel, ValidationError
from pydantic_extra_types.pendulum_dt import DateTime
from pydantic_extra_types.pendulum_dt import Date, DateTime, Duration
class Model(BaseModel):
class DtModel(BaseModel):
dt: DateTime
class DateModel(BaseModel):
d: Date
class DurationModel(BaseModel):
delta_t: Duration
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)
model = DtModel(dt=now)
assert model.dt == now
def test_pendulum_date_existing_instance():
"""
Verifies that constructing a model with an existing pendulum date doesn't throw.
"""
today = pendulum.today().date()
model = DateModel(d=today)
assert model.d == today
def test_pendulum_duration_existing_instance():
"""
Verifies that constructing a model with an existing pendulum duration doesn't throw.
"""
delta_t = pendulum.duration(days=42, hours=13, minutes=37)
model = DurationModel(delta_t=delta_t)
assert model.delta_t.total_seconds() == delta_t.total_seconds()
@pytest.mark.parametrize(
'dt', [pendulum.now().to_iso8601_string(), pendulum.now().to_w3c_string(), pendulum.now().to_iso8601_string()]
'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)
model = DtModel(dt=dt)
assert model.dt == dt_actual
def test_pendulum_date_from_serialized():
"""
Verifies that building an instance from serialized, well-formed strings decode properly.
"""
date_actual = pendulum.parse('2024-03-18').date()
model = DateModel(d='2024-03-18')
assert model.d == date_actual
@pytest.mark.parametrize(
'delta_t_str',
[
'P3.14D',
'PT404H',
'P1DT25H',
'P2W',
'P10Y10M10D',
],
)
def test_pendulum_duration_from_serialized(delta_t_str):
"""
Verifies that building an instance from serialized, well-formed strings decode properly.
"""
true_delta_t = pendulum.parse(delta_t_str)
model = DurationModel(delta_t=delta_t_str)
assert model.delta_t == true_delta_t
@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)
DtModel(dt=dt)
@pytest.mark.parametrize('date', [None, 'malformed', pendulum.today().to_iso8601_string()[:5], 42])
def test_pendulum_date_malformed(date):
"""
Verifies that the instance fails to validate if malformed date are passed.
"""
with pytest.raises(ValidationError):
DateModel(d=date)
@pytest.mark.parametrize(
'delta_t',
[None, 'malformed', pendulum.today().to_iso8601_string()[:5], 42, '12m'],
)
def test_pendulum_duration_malformed(delta_t):
"""
Verifies that the instance fails to validate if malformed durations are passed.
"""
with pytest.raises(ValidationError):
DurationModel(delta_t=delta_t)

View file

@ -31,8 +31,8 @@ class Something(BaseModel):
(_ULID.from_str('01BTGNYV6HRNK8K8VKZASZCFPE'), '01BTGNYV6HRNK8K8VKZASZCFPE', True),
(_ULID.from_str('01BTGNYV6HRNK8K8VKZASZCFPF'), '01BTGNYV6HRNK8K8VKZASZCFPF', True),
# Invalid _ULID for bytes format
(b'\x01\xBA\x1E\xB2\x8A\x9F\xFAy\x10\xD5\xA5k\xC8', None, False), # Invalid ULID (short length)
(b'\x01\xBA\x1E\xB2\x8A\x9F\xFAy\x10\xD5\xA5k\xC8\xB6\x00', None, False), # Invalid ULID (long length)
(b'\x01\xba\x1e\xb2\x8a\x9f\xfay\x10\xd5\xa5k\xc8', None, False), # Invalid ULID (short length)
(b'\x01\xba\x1e\xb2\x8a\x9f\xfay\x10\xd5\xa5k\xc8\xb6\x00', None, False), # Invalid ULID (long length)
# Valid ULID for int format
(109667145845879622871206540411193812282, '2JG4FVY7N8XS4GFVHPXGJZ8S9T', True),
(109667145845879622871206540411193812283, '2JG4FVY7N8XS4GFVHPXGJZ8S9V', True),