Skip to content

Commit

Permalink
Include an error message hint for inherited ordering (#7124)
Browse files Browse the repository at this point in the history
Co-authored-by: David Montague <35119617+dmontagu@users.noreply.github.com>
  • Loading branch information
yohanvalencia and dmontagu committed Aug 16, 2023
1 parent 7387c8a commit 8ed6ea4
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 5 deletions.
15 changes: 10 additions & 5 deletions pydantic/_internal/_model_construction.py
Expand Up @@ -138,15 +138,20 @@ def wrapped_model_post_init(self: BaseModel, __context: Any) -> None:
if parameters and parent_parameters and not all(x in parameters for x in parent_parameters):
combined_parameters = parent_parameters + tuple(x for x in parameters if x not in parent_parameters)
parameters_str = ', '.join([str(x) for x in combined_parameters])
generic_type_label = f'typing.Generic[{parameters_str}]'
error_message = (
f'All parameters must be present on typing.Generic;'
f' you should inherit from typing.Generic[{parameters_str}]'
f' you should inherit from {generic_type_label}.'
)
if Generic not in bases: # pragma: no cover
# This branch will only be hit if I have misunderstood how `__parameters__` works.
# If that is the case, and a user hits this, I could imagine it being very helpful
# to have this extra detail in the reported traceback.
error_message += f' (bases={bases})'
# We raise an error here not because it is desirable, but because some cases are mishandled.
# It would be nice to remove this error and still have things behave as expected, it's just
# challenging because we are using a custom `__class_getitem__` to parametrize generic models,
# and not returning a typing._GenericAlias from it.
bases_str = ', '.join([x.__name__ for x in bases] + [generic_type_label])
error_message += (
f' Note: `typing.Generic` must go last: `class {cls.__name__}({bases_str}): ...`)'
)
raise TypeError(error_message)

cls.__pydantic_generic_metadata__ = {
Expand Down
21 changes: 21 additions & 0 deletions tests/test_generics.py
Expand Up @@ -2022,6 +2022,27 @@ class B(A[T], Generic[S]):
...


def test_generic_subclass_with_extra_type_with_hint_message():
E = TypeVar('E', bound=BaseModel)
D = TypeVar('D')

class BaseGenericClass(Generic[E, D], BaseModel):
uid: str
name: str

with pytest.raises(
TypeError,
match=re.escape(
'All parameters must be present on typing.Generic; you should inherit from typing.Generic[~E, ~D].'
' Note: `typing.Generic` must go last:'
' `class ChildGenericClass(BaseGenericClass, typing.Generic[~E, ~D]): ...`'
),
):

class ChildGenericClass(BaseGenericClass[E, Dict[str, Any]]):
...


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

Expand Down

0 comments on commit 8ed6ea4

Please sign in to comment.