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

fix: pydantic 2.10.x breaking change #1095

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

mdaffad
Copy link

@mdaffad mdaffad commented Dec 25, 2024

Changes:

  1. Breaking change on AnyUrl type
  2. Breaking change on pydantic_core.core_schema.no_info_plain_validator_function

Known affected test on pydantic latest release:

  1. tests/odm/test_encoder.py::test_should_encode_pydantic_v2_url_correctly
  2. tests/odm/test_encoder.py::test_should_be_able_to_save_retrieve_doc_with_url
  3. tests/odm/test_fields.py::test_revision_id_not_in_schema
  4. tests/odm/test_json_schema_generation.py::test_schema_export_of_model_with_decimal_field
  5. tests/odm/test_json_schema_generation.py::test_schema_export_of_model_with_pydanticobjectid
  6. tests/odm/test_json_schema_generation.py::test_schema_export_of_model_with_link
  7. tests/odm/test_json_schema_generation.py
  8. tests/odm/test_json_schema_generation.py::test_schema_export_of_model_with_list_link
  9. tests/odm/test_json_schema_generation.py::test_schema_export_of_model_with_backlink
  10. tests/odm/test_json_schema_generation.py::test_schema_export_of_model_with_list_backlink

Related issues:

  1. Closes [BUG] Pydantic URLs broken in pydantic 2.10.3 #1084

return_schema=str_schema(),
when_used="json",
),
)
return json_or_python_schema(
Copy link
Contributor

@Viicos Viicos Dec 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, Pydantic maintainer here.

I don't think there's a need to make a distinction between JSON and Python here. pydantic_js_input_core_schema can be specified for both, it's not an issue:

return no_info_plain_validator_function(
    cls._validate,
    json_schema_input_schema=...,
    serialization=...,
)

As a side note, I know our logic to create custom types through __get_pydantic_core_schema__ isn't ideal (confusing pattern, need to create core schemas manually, etc). Ideally, PydanticObjectId could be defined as an annotated type:

PydanticObjectId = Annotated[
    ObjectId,
    PlainValidator(_validate, json_schema_input_type=Annotated[str, Field(pattern="^[0-9a-f]{24}$", min_length=24, max_length=24)]),
    PlainSerializer(lambda v: str(v), return_type=str),
]

This way, you go through the "user facing" validators API, where json_schema_input_type is guaranteed to not change (unlike how this attribute was stored on the core schema — we had to change it in 2.10 to avoid core schema simplification errors).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion. I updated on the plain validator function. But for the

PydanticObjectId = Annotated[
    ObjectId,
    PlainValidator(_validate, json_schema_input_type=Annotated[str, Field(pattern="^[0-9a-f]{24}$", min_length=24, max_length=24)]),
    PlainSerializer(lambda v: str(v), return_type=str),
]

I have noticed there are some breaking changes that would require significant modifications to the codebase since it changes the checker for PydanticObjectId as typing.Annotated and not a normal class. One of the example is:

def extract_id_class(annotation) -> Type[Any]:
    if get_origin(annotation) is not None:
        try:
            annotation = next(
                arg for arg in get_args(annotation) if arg is not type(None)
            )
        except StopIteration:
            annotation = None
    if inspect.isclass(annotation):
        return annotation
    raise ValueError("Unknown annotation: {}".format(annotation))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes indeed this will most likely break things (and the Annotated form also has some drawbacks). And it seems that beanie expects PydanticObjectId to be instantiable, which is not going to be the case with Annotated :/

@@ -1,9 +1,22 @@
from typing import Any, Type

import pydantic
from packaging.specifiers import SpecifierSet
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll need to switch back to the pydantic.VERSION manual parsing as packacing is a 3rd party dependency, and currently not included in the requirements.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree here, lets not introduce packaging as a dependency.

Copy link
Contributor

@staticxterm staticxterm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thank you for your contribution.
Looks good in general, no further major comments apart from what @Viicos already commented.

Comment on lines 2 to 4
on:
pull_request:
push:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not so sure about this seemingly unrelated change.
Won't this run multiple jobs for a "PR open" and a "branch push"?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi thanks to check this PR.
I used it in for CI tests on current branch before make the PR. That part has been removed now.

@@ -1,9 +1,22 @@
from typing import Any, Type

import pydantic
from packaging.specifiers import SpecifierSet
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree here, lets not introduce packaging as a dependency.

@mdaffad mdaffad requested a review from staticxterm December 28, 2024 12:03
Copy link
Contributor

@staticxterm staticxterm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No other comments apart from the version check one, looks good.

IS_PYDANTIC_V2 = int(pydantic.VERSION.split(".")[0]) >= 2
IS_PYDANTIC_V2_10 = (
IS_PYDANTIC_V2 and int(pydantic.VERSION.split(".")[1]) >= 10
) or int(pydantic.VERSION.split(".")[0]) > 2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove this "or" part. Any other major Pydantic version would not necessarily be compatible with Beanie and I think we should not bet on this.
Lets make this check do what it says in the name, to explicitly check for Pydantic v2.10.x (or above, constrained to v2).

@mdaffad mdaffad requested a review from staticxterm December 28, 2024 14:15
staticxterm
staticxterm previously approved these changes Dec 28, 2024
@staticxterm staticxterm requested a review from a team December 28, 2024 14:22
@staticxterm
Copy link
Contributor

Sorry, @mdaffad, one other thing also popped to mind. Lets also include latest Pydantic v2.10.4 in our CI testing as well?
Please add it to .github/workflows/github-actions-tests.yml

@mdaffad
Copy link
Author

mdaffad commented Dec 28, 2024

It seems version 2.10.4 from matrix dropped after this run: https://github.com/BeanieODM/beanie/actions/runs/12488481332/workflow. I will re-add it.

Copy link

@Riverfount Riverfount left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@adeelsohailahmed adeelsohailahmed requested review from Viicos and a team and removed request for Viicos December 28, 2024 18:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BUG] Pydantic URLs broken in pydantic 2.10.3
4 participants