Skip to content

Commit

Permalink
Improve Pydantic support
Browse files Browse the repository at this point in the history
  • Loading branch information
mdomke committed May 7, 2024
1 parent f4bf290 commit c96efc5
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 7 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ Changelog

Versions follow `CalVer <http://www.calver.org/>`_ with the scheme ``YY.0M.Micro``.

`2024.05.0`_ - 2024/05/07
-------------------------
Fixed
~~~~~
* Loading JSON data into a Pydantic model with an ``IBAN``-field (``Model.model_validate_json()``)
was previously broken and has been fixed now.

Added
~~~~~
* JSON schema generation for Pydantic models.

Changed
~~~~~~~
* Updated bank registries.


`2024.04.0`_ - 2024/04/18
-------------------------
Added
Expand Down Expand Up @@ -564,6 +580,7 @@ Added
* Added :attr:`.BIC.country` and :attr:`.IBAN.country`.


.. _2024.05.0: https://github.com/mdomke/schwifty/compare/2024.04.0...2024.05.0
.. _2024.04.0: https://github.com/mdomke/schwifty/compare/2023.01.1...2024.04.0
.. _2024.01.1: https://github.com/mdomke/schwifty/compare/2023.11.2...2024.01.1
.. _2023.11.2: https://github.com/mdomke/schwifty/compare/2023.11.1...2023.11.2
Expand Down
43 changes: 37 additions & 6 deletions schwifty/iban.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@

if TYPE_CHECKING:
from pydantic import GetCoreSchemaHandler
from pydantic import GetJsonSchemaHandler
from pydantic import ValidatorFunctionWrapHandler
from pydantic.json_schema import JsonSchemaValue
from pydantic_core import CoreSchema


Expand Down Expand Up @@ -359,16 +362,44 @@ def bank_short_name(self) -> str | None:
return self.bban.bank_short_name

@classmethod
def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> CoreSchema:
def __get_pydantic_core_schema__(
cls, source: type[Any], handler: GetCoreSchemaHandler
) -> CoreSchema:
from pydantic_core import core_schema

return core_schema.union_schema(
[
core_schema.is_instance_schema(IBAN),
core_schema.no_info_plain_validator_function(IBAN),
]
return core_schema.no_info_wrap_validator_function(
cls._pydantic_validate,
core_schema.union_schema(
[
core_schema.is_instance_schema(IBAN),
core_schema.no_info_plain_validator_function(IBAN),
core_schema.str_schema(max_length=34),
]
),
serialization=core_schema.to_string_ser_schema(
when_used="json-unless-none",
),
)

@classmethod
def __get_pydantic_json_schema__(
cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler
) -> JsonSchemaValue:
json_schema = handler(core_schema)
json_schema = handler.resolve_ref_schema(json_schema)
json_schema["title"] = "IBAN"
return json_schema

@classmethod
def _pydantic_validate(cls, value: Any, handler: ValidatorFunctionWrapHandler) -> Any:
from pydantic_core import PydanticCustomError

try:
iban = cls(value)
except exceptions.SchwiftyException as err:
raise PydanticCustomError("iban_format", str(err)) from err
return handler(iban)


def add_bban_regex(country: str, spec: dict) -> dict:
bban_spec = spec["bban_spec"]
Expand Down
17 changes: 16 additions & 1 deletion tests/test_iban.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,21 @@ class Model(BaseModel):

model = Model(iban="GL89 6471 0001 0002 06") # type: ignore[arg-type]
assert isinstance(model.iban, IBAN)
assert model.model_dump() == {"iban": model.iban}

with pytest.raises(ValidationError):
with pytest.raises(ValidationError) as err:
Model(iban="GB00 HLFX 1101 6111 4553 65") # type: ignore[arg-type]
assert len(err.value.errors()) == 1
error = err.value.errors()[0]
assert error["type"] == "iban_format"
assert error["msg"] == "Invalid checksum digits"
assert error["input"] == "GB00 HLFX 1101 6111 4553 65"

json_schema = model.model_json_schema()
assert json_schema["properties"]["iban"] == {"maxLength": 34, "title": "IBAN", "type": "string"}

dumped = model.model_dump_json()
assert dumped == '{"iban":"GL8964710001000206"}'

loaded = Model.model_validate_json(dumped)
assert loaded == model

0 comments on commit c96efc5

Please sign in to comment.