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 issue where generic models couldn't be parametrized with BaseModel #6933

Merged
merged 1 commit into from Jul 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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