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

Creating a TypeAdapter for a nested TypedDict removes model_validator for other TypeAdapters #9318

Open
1 task done
cc-a opened this issue Apr 25, 2024 · 1 comment
Open
1 task done
Assignees
Labels
bug V2 Bug related to Pydantic V2

Comments

@cc-a
Copy link

cc-a commented Apr 25, 2024

Initial Checks

  • I confirm that I'm using Pydantic V2

Description

Please see the example below where validation should fail but does not. Commenting out the creation of InnerTypeAdapter gives a validation error as expected.

Creating a TypeAdapter for the Inner class seems to prevent it's model validator from being run when using a TypeAdapter for the Outer class. Performing validation with InnerTypeAdapter works as expected.

Example Code

from pydantic import TypeAdapter, model_validator, ValidationError

from typing_extensions import TypedDict


class Inner(TypedDict):
    a: int

    @model_validator(mode="after")
    def check(self):
        raise ValueError("expected error")
        return self

InnerTypeAdapter = TypeAdapter(Inner)

class Outer(TypedDict):
    inner: Inner

OuterTypeAdapter = TypeAdapter(Outer)

OuterTypeAdapter.validate_python(dict(inner=dict(a=0)))

Python, Pydantic & OS Version

pydantic version: 2.7.1
        pydantic-core version: 2.18.2
          pydantic-core build: profile=release pgo=true
                 install path: /home/ccaveayl/pydantic_bug/.venv/lib/python3.11/site-packages/pydantic
               python version: 3.11.8 (main, Feb 12 2024, 14:50:05) [GCC 13.2.1 20230801]
                     platform: Linux-6.8.7-arch1-1-x86_64-with-glibc2.39
             related packages: typing_extensions-4.11.0
                       commit: unknown
@cc-a cc-a added bug V2 Bug related to Pydantic V2 pending Awaiting a response / confirmation labels Apr 25, 2024
@sydney-runkle
Copy link
Member

Hmph, so this works as expected if you use BaseModel instead of TypedDict.

Here's a repro with some schema information - the schema created with the inner type adapter used is missing the after validator function (which is a bug, as you've noticed behaviorally), but I'm not sure why.

from pydantic import BaseModel, TypeAdapter, model_validator, ValidationError

from typing_extensions import TypedDict


class Inner(TypedDict):
    a: int

    @model_validator(mode="after")
    def check(cls, v):
        raise ValueError("expected error")
        return self

InnerTypeAdapter = TypeAdapter(Inner)
# InnerTypeAdapter.validate_python(dict(a=0))
"""
pydantic_core._pydantic_core.ValidationError: 1 validation error for function-after[check(), typed-dict]
  Value error, expected error [type=value_error, input_value={'a': 0}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.7/v/value_error
"""

class Outer(TypedDict):
    inner: Inner

OuterTypeAdapter = TypeAdapter(Outer)

from pydantic._internal._core_utils import pretty_print_core_schema
pretty_print_core_schema(OuterTypeAdapter.core_schema)
"""
With TypedDict, with inner adapter
{
    'type': 'typed-dict',
    'fields': {
        'inner': {
            'type': 'typed-dict-field',
            'schema': {
                'type': 'typed-dict',
                'fields': {
                    'a': {
                        'type': 'typed-dict-field',
                        'schema': {'type': 'int'},
                        'required': True,
                        'metadata': {
                            'pydantic_js_functions': [],
                            'pydantic_js_annotation_functions': [<function get_json_schema_update_func.<locals>.json_schema_update_func at 0x1053f8e50>]
                        }
                    }
                },
                'computed_fields': [],
                'ref': '__main__.Inner:4955795504',
                'config': {'title': 'Inner'}
            },
            'required': True,
            'metadata': {
                'pydantic_js_functions': [],
                'pydantic_js_annotation_functions': [<function get_json_schema_update_func.<locals>.json_schema_update_func at 0x1053f8d30>]
            }
        }
    },
    'computed_fields': [],
    'ref': '__main__.Outer:5369543232',
    'config': {'title': 'Outer'}
}
"""

"""
With TypedDict, no inner adapter
{
    'type': 'typed-dict',
    'fields': {
        'inner': {
            'type': 'typed-dict-field',
            'schema': {
                'function': {'type': 'no-info', 'function': <bound method Inner.check of <class '__main__.Inner'>>},
                'schema': {
                    'type': 'typed-dict',
                    'fields': {
                        'a': {
                            'type': 'typed-dict-field',
                            'schema': {'type': 'int'},
                            'required': True,
                            'metadata': {
                                'pydantic_js_functions': [],
                                'pydantic_js_annotation_functions': [
                                    <function get_json_schema_update_func.<locals>.json_schema_update_func at 0x105a7ab00>
                                ]
                            }
                        }
                    },
                    'computed_fields': [],
                    'config': {'title': 'Inner'}
                },
                'ref': '__main__.Inner:5258552160',
                'type': 'function-after'
            },
            'required': True,
            'metadata': {
                'pydantic_js_functions': [],
                'pydantic_js_annotation_functions': [<function get_json_schema_update_func.<locals>.json_schema_update_func at 0x105a78f70>]
            }
        }
    },
    'computed_fields': [],
    'ref': '__main__.Outer:5258589040',
    'config': {'title': 'Outer'}
}
"""

Definitely something we need to fix, likely on the pydantic side of things!

@sydney-runkle sydney-runkle removed the pending Awaiting a response / confirmation label Apr 28, 2024
@sydney-runkle sydney-runkle self-assigned this Apr 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug V2 Bug related to Pydantic V2
Projects
None yet
Development

No branches or pull requests

2 participants