Skip to content

Commit

Permalink
Resolve postponed NamedTuple annotations (pydantic#2760)
Browse files Browse the repository at this point in the history
Thanks to @PrettyWood for pointing me to the right place to fix this!

Since I was told that both NamedTuple and TypedDict use the same
__pydantic_model__ machinery that dataclasses do, I checked and found
that TypedDict had the same bug, and fixed that too.

Tests for both issues are included, which fail without the associated
fixes being applied.
  • Loading branch information
jameysharp committed May 8, 2021
1 parent 5921d5e commit c6b4542
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 3 deletions.
11 changes: 9 additions & 2 deletions pydantic/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,10 @@ def pattern_validator(v: Any) -> Pattern[str]:
def make_namedtuple_validator(namedtuple_cls: Type[NamedTupleT]) -> Callable[[Tuple[Any, ...]], NamedTupleT]:
from .annotated_types import create_model_from_namedtuple

NamedTupleModel = create_model_from_namedtuple(namedtuple_cls)
NamedTupleModel = create_model_from_namedtuple(
namedtuple_cls,
__module__=namedtuple_cls.__module__,
)
namedtuple_cls.__pydantic_model__ = NamedTupleModel # type: ignore[attr-defined]

def namedtuple_validator(values: Tuple[Any, ...]) -> NamedTupleT:
Expand All @@ -579,7 +582,11 @@ def make_typeddict_validator(
) -> Callable[[Any], Dict[str, Any]]:
from .annotated_types import create_model_from_typeddict

TypedDictModel = create_model_from_typeddict(typeddict_cls, __config__=config)
TypedDictModel = create_model_from_typeddict(
typeddict_cls,
__config__=config,
__module__=typeddict_cls.__module__,
)
typeddict_cls.__pydantic_model__ = TypedDictModel # type: ignore[attr-defined]

def typeddict_validator(values: 'TypedDict') -> Dict[str, Any]:
Expand Down
30 changes: 29 additions & 1 deletion tests/test_annotated_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import pytest
from typing_extensions import TypedDict

from pydantic import BaseModel, ValidationError
from pydantic import BaseModel, PositiveInt, ValidationError

if sys.version_info < (3, 9):
try:
Expand Down Expand Up @@ -123,6 +123,23 @@ class Model(BaseModel):
]


def test_namedtuple_postponed_annotation():
"""
https://github.com/samuelcolvin/pydantic/issues/2760
"""

class Tup(NamedTuple):
v: 'PositiveInt'

class Model(BaseModel):
t: Tup

# The effect of issue #2760 is that this call raises a `ConfigError` even though the type declared on `Tup.v`
# references a binding in this module's global scope.
with pytest.raises(ValidationError):
Model.parse_obj({'t': [-1]})


def test_typeddict():
class TD(TypedDict):
a: int
Expand Down Expand Up @@ -253,3 +270,14 @@ class Model(BaseModel):
},
},
}


def test_typeddict_postponed_annotation():
class DataTD(TypedDict):
v: 'PositiveInt'

class Model(BaseModel):
t: DataTD

with pytest.raises(ValidationError):
Model.parse_obj({'t': {'v': -1}})

0 comments on commit c6b4542

Please sign in to comment.