Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to Pydantic 2.x #26

Merged
merged 2 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 1.9.1
current_version = 2.0.0
commit = False
tag = False

Expand Down
231 changes: 180 additions & 51 deletions poetry.lock

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "libpvarki"
version = "1.9.1"
version = "2.0.0"
description = "Common helpers like standard logging init"
authors = ["Eero af Heurlin <eero.afheurlin@iki.fi>"]
homepage = "https://github.com/pvarki/python-libpvarki/"
Expand Down Expand Up @@ -59,8 +59,7 @@ branch = true
python = "^3.8"
ecs-logging = "^2.0"
fastapi = ">0.89,<1.0" # caret behaviour on 0.x is to lock to 0.x.*
# FIXME: Migrate to v2, see https://docs.pydantic.dev/2.3/migration/#basesettings-has-moved-to-pydantic-settings
pydantic= ">=1.10,<2.0"
pydantic= ">=2.0,<3.0"
cryptography = ">=41.0"
libadvian = "^1.4"
aiohttp = ">=3.10.2,<4.0"
Expand Down
2 changes: 1 addition & 1 deletion src/libpvarki/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
""" Common helpers like standard logging init """
__version__ = "1.9.1" # NOTE Use `bump2version --config-file patch` to bump versions correctly
__version__ = "2.0.0" # NOTE Use `bump2version --config-file patch` to bump versions correctly
16 changes: 7 additions & 9 deletions src/libpvarki/schemas/generic.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
"""Generic schemas"""
from typing import Optional

from pydantic import Field, Extra
from pydantic.main import BaseModel # pylint: disable=E0611 # false positive
from pydantic import Field, BaseModel, ConfigDict


class OperationResultResponse(BaseModel): # pylint: disable=too-few-public-methods
Expand All @@ -16,14 +15,13 @@ class OperationResultResponse(BaseModel): # pylint: disable=too-few-public-meth
json_schema_extra={"nullable": True},
)

class Config: # pylint: disable=too-few-public-methods
"""Example values for schema"""

extra = Extra.forbid
schema_extra = {
model_config = ConfigDict(
extra="forbid",
json_schema_extra={
"examples": [
{"success": True},
{"success": False, "error": "Things went wrong"},
{"success": True, "extra": "Tell the user they're awesome"},
]
}
],
},
)
55 changes: 25 additions & 30 deletions src/libpvarki/schemas/product.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
"""Pydantic schemas for product integration APIs"""
from typing import Optional

from pydantic import Field, Extra
from pydantic.main import BaseModel # pylint: disable=E0611 # false positive
from pydantic import Field, BaseModel, ConfigDict

# pylint: disable=too-few-public-methods
from .generic import OperationResultResponse # pylint: disable=W0611 # For backwards compatibility
Expand All @@ -15,19 +14,18 @@ class UserCRUDRequest(BaseModel):
callsign: str = Field(description="Callsign of the user")
x509cert: str = Field(description="Certificate encoded with CFSSL conventions (newlines escaped)")

class Config: # pylint: disable=too-few-public-methods
"""Example values for schema"""

extra = Extra.forbid
schema_extra = {
model_config = ConfigDict(
extra="forbid",
json_schema_extra={
"examples": [
{
"uuid": "3ede23ae-eff2-4aa8-b7ef-7fac68c39988",
"callsign": "ROTTA01a",
"x509cert": "-----BEGIN CERTIFICATE-----\\nMIIEwjCC...\\n-----END CERTIFICATE-----\\n",
},
]
}
],
},
)


# FIXME: The spec for these will be changed
Expand All @@ -41,19 +39,18 @@ class UserInstructionFragment(BaseModel):
json_schema_extra={"nullable": True},
)

class Config: # pylint: disable=too-few-public-methods
"""Example values for schema"""

extra = Extra.forbid
schema_extra = {
model_config = ConfigDict(
extra="forbid",
json_schema_extra={
"examples": [
{"html": "<p>Hello World!</p>"},
{
"html": """<p class="hello">Hello World!</p>""",
"inject_css": "http://example.com/mystyle.css",
},
]
}
],
},
)


class ReadyRequest(BaseModel): # pylint: disable=too-few-public-methods
Expand All @@ -63,19 +60,18 @@ class ReadyRequest(BaseModel): # pylint: disable=too-few-public-methods
apiurl: str = Field(description="Product API URL")
url: str = Field(description="Product UI URL")

class Config: # pylint: disable=too-few-public-methods
"""Example values for schema"""

extra = Extra.forbid
schema_extra = {
model_config = ConfigDict(
extra="forbid",
json_schema_extra={
"examples": [
{
"product": "tak",
"apiurl": "https://tak.sleepy-sloth.pvarki.fi:4625/",
"url": "https://tak.sleepy-sloth.pvarki.fi:8443/",
},
]
}
],
},
)


class ProductHealthCheckResponse(BaseModel):
Expand All @@ -86,13 +82,12 @@ class ProductHealthCheckResponse(BaseModel):
healthy: bool = Field(description="Everything is good, ready to serve")
extra: Optional[str] = Field(description="Extra information", default=None, json_schema_extra={"nullable": True})

class Config: # pylint: disable=too-few-public-methods
"""Example values for schema"""

extra = Extra.forbid
schema_extra = {
model_config = ConfigDict(
extra="forbid",
json_schema_extra={
"examples": [
{"healthy": True},
{"healthy": False, "extra": "Things went wrong"},
]
}
],
},
)
5 changes: 4 additions & 1 deletion tests/schemas/test_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
def test_result() -> None:
"""Test constructing OperationResultResponse"""
res = OperationResultResponse(success=True)
assert res.dict()["success"]
assert res.model_dump()["success"]


def test_result_extra() -> None:
"""Test that extra field forbid works"""
with pytest.raises(ValidationError):
OperationResultResponse(success=True, nosuchfield="Trololooo") # type: ignore[call-arg]

with pytest.raises(ValidationError):
OperationResultResponse.model_validate({"success": True, "nosuchfield": "Trololooo"})
6 changes: 3 additions & 3 deletions tests/schemas/test_product.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
def test_fragment() -> None:
"""Test constructing UserInstructionFragment"""
res = UserInstructionFragment(html="<p>Hello world!</p>")
assert res.dict()["html"]
assert res.model_dump()["html"]


def test_crud() -> None:
"""Test constructing UserCRUDRequest"""
res = UserCRUDRequest(callsign="KISSA23a", uuid="not really an uuid", x509cert="not really a cert")
assert res.dict()["callsign"] == "KISSA23a"
assert res.model_dump()["callsign"] == "KISSA23a"


def test_health() -> None:
"""Test constructing ProductHealthCheckResponse"""
res = ProductHealthCheckResponse(healthy=True)
assert res.dict()["healthy"] is True
assert res.model_dump()["healthy"] is True
2 changes: 1 addition & 1 deletion tests/test_libpvarki.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

def test_version() -> None:
"""Make sure version matches expected"""
assert __version__ == "1.9.1"
assert __version__ == "2.0.0"
Loading