Skip to content

Commit

Permalink
Fix issue where generic models couldn't be parametrized with BaseModel (
Browse files Browse the repository at this point in the history
  • Loading branch information
dmontagu committed Jul 28, 2023
1 parent 2a4a7e5 commit bb43605
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 11 deletions.
6 changes: 3 additions & 3 deletions pydantic/_internal/_generics.py
Expand Up @@ -15,7 +15,7 @@
from ._core_utils import get_type_ref
from ._forward_ref import PydanticRecursiveRef
from ._typing_extra import TypeVarType, typing_base
from ._utils import all_identical, is_basemodel
from ._utils import all_identical, is_model_class

if sys.version_info >= (3, 10):
from typing import _UnionGenericAlias # type: ignore[attr-defined]
Expand Down Expand Up @@ -197,7 +197,7 @@ def iter_contained_typevars(v: Any) -> Iterator[TypeVarType]:
"""
if isinstance(v, TypeVar):
yield v
elif is_basemodel(v):
elif is_model_class(v):
yield from v.__pydantic_generic_metadata__['parameters']
elif isinstance(v, (DictValues, list)):
for var in v:
Expand Down Expand Up @@ -316,7 +316,7 @@ def replace_types(type_: Any, type_map: Mapping[Any, Any] | None) -> Any:
# We handle pydantic generic models separately as they don't have the same
# semantics as "typing" classes or generic aliases

if not origin_type and is_basemodel(type_):
if not origin_type and is_model_class(type_):
parameters = type_.__pydantic_generic_metadata__['parameters']
if not parameters:
return type_
Expand Down
12 changes: 4 additions & 8 deletions pydantic/_internal/_utils.py
Expand Up @@ -79,17 +79,13 @@ def lenient_issubclass(cls: Any, class_or_tuple: Any) -> bool: # pragma: no cov
raise # pragma: no cover


def is_basemodel(cls: Any) -> TypeGuard[type[BaseModel]]:
"""We can remove this function and go back to using lenient_issubclass, but this is nice because it
ensures that we get proper type-checking, which lenient_issubclass doesn't provide.
Would be nice if there was a lenient_issubclass-equivalent in typing_extensions, or otherwise
a way to define such a function that would support proper type-checking; maybe we should bring it up
at the typing summit..
def is_model_class(cls: Any) -> TypeGuard[type[BaseModel]]:
"""Returns true if cls is a _proper_ subclass of BaseModel, and provides proper type-checking,
unlike raw calls to lenient_issubclass.
"""
from ..main import BaseModel

return lenient_issubclass(cls, BaseModel)
return lenient_issubclass(cls, BaseModel) and cls is not BaseModel


def is_valid_identifier(identifier: str) -> bool:
Expand Down
10 changes: 10 additions & 0 deletions tests/test_generics.py
Expand Up @@ -2551,3 +2551,13 @@ class ParamSpecGenericModel(BaseModel, Generic[P]):
my_generic: MyGenericParamSpecClass[P]

model_config = dict(arbitrary_types_allowed=True)


def test_parametrize_with_basemodel():
T = TypeVar('T')

class SimpleGenericModel(BaseModel, Generic[T]):
pass

class Concrete(SimpleGenericModel[BaseModel]):
pass

0 comments on commit bb43605

Please sign in to comment.