Skip to content

Adds a pydantic V2 compatibility layer #218

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

Merged
merged 2 commits into from
Aug 28, 2023
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
28 changes: 17 additions & 11 deletions cloudevents/pydantic/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@
from cloudevents.exceptions import PydanticFeatureNotInstalled

try:
import pydantic
from pydantic import VERSION as PYDANTIC_VERSION

pydantic_major_version = PYDANTIC_VERSION.split(".")[0]
if pydantic_major_version == "2":
from pydantic.v1 import BaseModel, Field
else:
from pydantic import BaseModel, Field # type: ignore
except ImportError: # pragma: no cover # hard to test
raise PydanticFeatureNotInstalled(
"CloudEvents pydantic feature is not installed. "
Expand Down Expand Up @@ -84,7 +90,7 @@ def _ce_json_loads( # type: ignore[no-untyped-def]
return conversion.to_dict(http.from_json(data))


class CloudEvent(abstract.CloudEvent, pydantic.BaseModel): # type: ignore
class CloudEvent(abstract.CloudEvent, BaseModel): # type: ignore
"""
A Python-friendly CloudEvent representation backed by Pydantic-modeled fields.

Expand All @@ -97,7 +103,7 @@ def create(
) -> "CloudEvent":
return cls(attributes, data)

data: typing.Optional[typing.Any] = pydantic.Field(
data: typing.Optional[typing.Any] = Field(
title="Event Data",
description=(
"CloudEvents MAY include domain-specific information about the occurrence."
Expand All @@ -107,7 +113,7 @@ def create(
" when those respective attributes are present."
),
)
source: str = pydantic.Field(
source: str = Field(
title="Event Source",
description=(
"Identifies the context in which an event happened. Often this will include"
Expand All @@ -132,7 +138,7 @@ def create(
example="https://github.com/cloudevents",
)

id: str = pydantic.Field(
id: str = Field(
default_factory=attribute.default_id_selection_algorithm,
title="Event ID",
description=(
Expand All @@ -144,7 +150,7 @@ def create(
),
example="A234-1234-1234",
)
type: str = pydantic.Field(
type: str = Field(
title="Event Type",
description=(
"This attribute contains a value describing the type of event related to"
Expand All @@ -154,7 +160,7 @@ def create(
),
example="com.github.pull_request.opened",
)
specversion: attribute.SpecVersion = pydantic.Field(
specversion: attribute.SpecVersion = Field(
default=attribute.DEFAULT_SPECVERSION,
title="Specification Version",
description=(
Expand All @@ -168,7 +174,7 @@ def create(
),
example=attribute.DEFAULT_SPECVERSION,
)
time: typing.Optional[datetime.datetime] = pydantic.Field(
time: typing.Optional[datetime.datetime] = Field(
default_factory=attribute.default_time_selection_algorithm,
title="Occurrence Time",
description=(
Expand All @@ -182,7 +188,7 @@ def create(
example="2018-04-05T17:31:00Z",
)

subject: typing.Optional[str] = pydantic.Field(
subject: typing.Optional[str] = Field(
title="Event Subject",
description=(
"This describes the subject of the event in the context of the event"
Expand All @@ -202,7 +208,7 @@ def create(
),
example="123",
)
datacontenttype: typing.Optional[str] = pydantic.Field(
datacontenttype: typing.Optional[str] = Field(
title="Event Data Content Type",
description=(
"Content type of data value. This attribute enables data to carry any type"
Expand All @@ -211,7 +217,7 @@ def create(
),
example="text/xml",
)
dataschema: typing.Optional[str] = pydantic.Field(
dataschema: typing.Optional[str] = Field(
title="Event Data Schema",
description=(
"Identifies the schema that data adheres to. "
Expand Down
8 changes: 7 additions & 1 deletion cloudevents/tests/test_pydantic_cloudevent.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,19 @@
from json import loads

import pytest
from pydantic import ValidationError
from pydantic import VERSION as PYDANTIC_VERSION

from cloudevents.conversion import _json_or_string
from cloudevents.exceptions import IncompatibleArgumentsError
from cloudevents.pydantic import CloudEvent
from cloudevents.sdk.event.attribute import SpecVersion

pydantic_major_version = PYDANTIC_VERSION.split(".")[0]
if pydantic_major_version == "2":
from pydantic.v1 import ValidationError
else:
from pydantic import ValidationError

_DUMMY_SOURCE = "dummy:source"
_DUMMY_TYPE = "tests.cloudevents.override"
_DUMMY_TIME = "2022-07-16T11:20:34.284130+00:00"
Expand Down
2 changes: 1 addition & 1 deletion requirements/test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ aiohttp
Pillow
requests
flask
pydantic>=1.0.0,<2.0
pydantic>=1.0.0,<3.0
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,6 @@ def get_version(rel_path):
include_package_data=True,
version=pypi_config["version_target"],
install_requires=["deprecation>=2.0,<3.0"],
extras_require={"pydantic": "pydantic>=1.0.0,<2.0"},
extras_require={"pydantic": "pydantic>=1.0.0,<3.0"},
zip_safe=True,
)