Skip to content

Detect metaclass conflicts, refs #13563 - #13598

Merged
sobolevn merged 3 commits into
python:masterfrom
sobolevn:issue-13563-2
Sep 4, 2022
Merged

Detect metaclass conflicts, refs #13563#13598
sobolevn merged 3 commits into
python:masterfrom
sobolevn:issue-13563-2

Conversation

@sobolevn

Copy link
Copy Markdown
Member

Recreate of #13565

@sobolevn

Copy link
Copy Markdown
MemberAuthor

@AlexWaygood do you have any suggestions about the error message? 🤔

@AlexWaygood
AlexWaygood self-requested a review September 3, 2022 18:05
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

1 similar comment
@github-actions

Copy link
Copy Markdown
Contributor

According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉

Comment threadmypy/checker.py
Comment on lines +2398 to +2414
metaclasses = [
entry.metaclass_type
for entry in typ.mro[1:-1]
if entry.metaclass_type
and not is_named_instance(entry.metaclass_type, "builtins.type")
]
if not metaclasses:
return
if typ.metaclass_type is not None and all(
is_subtype(typ.metaclass_type, meta) for meta in metaclasses
):
return
self.fail(
"Metaclass conflict: the metaclass of a derived class must be "
"a (non-strict) subclass of the metaclasses of all its bases",
typ,
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you could do something like this, to improve the error message:

Suggested change
metaclasses= [
entry.metaclass_type
forentryintyp.mro[1:-1]
ifentry.metaclass_type
andnotis_named_instance(entry.metaclass_type, "builtins.type")
]
ifnotmetaclasses:
return
iftyp.metaclass_typeisnotNoneandall(
is_subtype(typ.metaclass_type, meta) formetainmetaclasses
):
return
self.fail(
"Metaclass conflict: the metaclass of a derived class must be "
"a (non-strict) subclass of the metaclasses of all its bases",
typ,
)
subclass_metaclass=typ.metaclass_type
ifsubclass_metaclassisNone:
return
forsuperclassintyp.mro[1:-1]:
superclass_metaclass=superclass.metaclass_type
ifsuperclass_metaclassisNoneoris_named_instance(
superclass_metaclass, "builtins.type"
):
continue
ifnotis_subtype(subclass_metaclass, superclass_metaclass):
self.fail(
(
f"Metaclass conflict: metaclass {subclass_metaclass.fullname!r} "
f"is not a subclass of {superclass_metaclass.fullname!r}, "
f"the metaclass of base class {superclass.fullname!r}"
),
typ
)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried it and I didn't like it. Why?
Because it either reports several errors for several bases with incompatible metaclasses or reports just one (if break is added) which is potentially confusing or we need even more complex code to accumulate all problematic base types and then raise a single one.

Let's keep it as-is for now?

If someone complains, then we can easily make this more readable and more complex.

(This is why I love coping CPython's error messages 😆 )

@AlexWaygoodAlexWaygoodSep 4, 2022

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep it as-is for now?

Sure, the PR looks pretty good right now! It was just an idea :)

@sobolevn
sobolevn merged commit 9d21615 into python:masterSep 4, 2022
@sobolevn

Copy link
Copy Markdown
MemberAuthor

Thanks everyone! 👏

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@sobolevn@ilevkivskyi@AlexWaygood