You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Offer a simpler way to resolve metaclass conflicts #14033
error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases [misc]
Same if I use
classMyMeta(tuple.__class__): pass
or
classMyMeta(type(tuple)): pass
instead
Pitch
I propose 2 alternatives to help users deal with this
Teach mypy that tuple.__class__ is type and that this is fine (preferred)
Tell users to use # typing: ignore to resolve such cases.
Technically, this is already possible today, but I suggest 2 improvements:
https://mypy.readthedocs.io/en/stable/metaclasses.html#gotchas-and-limitations-of-metaclass-support only lists what mypy can't handle. It does not talk about how to work around it => add a note telling users to # typing: ignore[misc] it. I'd like to mention that this feels pretty broad of an exception and might hide other real problems. Since this is an area where mypy might be overly restrictive and a workaround might be necessary, it should maybe be in a less broad category.
This is the more important point to me: this comment needs to be added to every subclass of MyTuple which can be frustratingly tedious. Maybe there should be away to tell mypy "ok, you might not understand this, but trust me that this class MyTuple has a valid metaclass, assume so for all child classes".
As I played further with it, I found that annotating class MyMeta(type(tuple)): pass with # typing: ignore[misc] allows me to not have to annotate every child class. That's something, I guess. But at least the docs should tell people about this workaround. Ideally, as said, it shouldn't even be necessary.
I'm happy to contribute. a) I haven't contributed to mypy before, so I wouldn't exactly know where to start, guidance welcome b) Can you elaborate on "Your ideas seem reasonable"? I'm not sure which of the two approaches I mentioned you'd like to see me implement. If the first one: I wouldn't know where to start looking. Judging by what AlexWaygood wrote, this seems to not be solvable in mypy, but typshed which again would need extended typing features in Python itself... so that seems a little unfeasible to me?
I'm happy to jump into a chat somewhere to talk about the details, if you want to help me getting started.
only lists what mypy can't handle. It does not talk about how to work around it => add a note telling users to # typing: ignore[misc] it. I'd like to mention that this feels pretty broad of an exception and might hide other real problems. Since this is an area where mypy might be overly restrictive and a workaround might be necessary, it should maybe be in a less broad category.
There are two cool ideas in this:
We can totally list this as a known problem in the docs
We can use a separate error code for metaclass issues
@robsdedude you can schedule a meeting if you want: https://calendly.com/sobolevn/ Don't worry that it says 2hours (we can do much faster). I will guide you through the contribution process :)
Or drop me an email: mail @ sobolevn.me if that's easier for you.
class CorrectSubclass1(A1, B, E, metaclass=CorrectMeta): ...
class CorrectSubclass2(A, B, E, metaclass=CorrectMeta): ...
class CorrectSubclass3(B, A, metaclass=CorrectMeta): ...
class ChildOfCorrectSubclass1(CorrectSubclass1): ...
class CorrectWithType1(C, A1): ...
class CorrectWithType2(B, C): ...
class Conflict1(A1, B, E): ... # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
class Conflict2(A, B): ... # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
class Conflict3(B, A): ... # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
class ChildOfConflict1(Conflict3): ... # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
class ChildOfConflict2(Conflict3, metaclass=CorrectMeta): ...
class ConflictingMeta(MyMeta1, MyMeta3): ...
class Conflict4(A1, B, E, metaclass=ConflictingMeta): ... # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
class ChildOfCorrectButWrongMeta(CorrectSubclass1, metaclass=ConflictingMeta): # E: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
Digging further into it and working on improving the error experience (including the message) as discussed in our meeting, I'm looking at testMetaclassSubclassSelf and in particular ChildOfConflict1. My opinion on this is that, while mypy is (most of the time, anyway) correct in assuming that this class has a metaclass conflict, I don't think the error helps the user at all, because this is not where the error originates (which would be the class definition of Conflict3). I think, this behavior of mypy just a) pollutes the output, making it harder for the user to find the place in the code they need to fix and b) should mypy be incorrect about some assumptions (like with some built-in types), it's not enough to just mute the single place of failure (Conflict3) but every child class of it as well, which can be very tedious.
What do you think about only emitting a metaclass conflict at the place it's introduced at?
This poses some technical difficulty as it's unclear, which metaclass mypy should assume for a class it considered having a metaclass conflict. But I think this can be resolved by traversing the mro and accepting the first explicitly declared metaclass as truth while falling back to type if there is none (which I believe can't actually happen).
@sobolevn, you said "So, it has ABCMeta metaclass according to the typeshed". Where is ABCMeta introduced? Does mypy assume that all Protocol classes implicitly have ABCMeta as a metaclass? Or perhaps it assumes this for protocol classes that have one or more @abstractmethod? Pyright doesn't make this assumption, so it doesn't report a metaclass conflict in the code sample at the top of this thread. I mention this because it could be a simple solution if mypy were not to assume that ABCMeta is the metaclass for protocols.
Another thing to discuss: changing the error code is a breaking change. For users that have silenced mypy complaining about metaclass conflicts (# type: ignore[misc]) this change would make their typecheck start to fail. So I don't know how you handle these kind of things.
Moreover, I noticed that changing the error code didn't make any tests fail. So it appears there are no tests for those, are there?
While the interpreter is, rightfully so, not amused.
Traceback (most recent call last):
File "foo.py", line 12, in <module>
class D(A2, B1, C1): pass
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
I'll see if I can also address this bug as it is very much related.
Note for myself: class D(A2, C1, B1): pass however, is fine.
Reading Python's documentation it seems mypy should actually be right and Python should accept class D(A2, B1, C1): pass. So I belief this is either a bug in the interpreter or the docs aren't quite accurate.
Can someone shed some light on which one is the case?
Sorry for the notification, but I've been waiting patiently for my PRs to get reviewed without luck. Could somebody please have a look before they go even more stale and have even more merge conflicts?
@JukkaL reaching out to you because you are the top contributor for this repository. Could you please help get this PR reviewed and merged as a lot of us are facing issues due to this.
Thanks & apologies for bothering you.
@hauntsaninja thanks for reviewing and merging my PR. Can you please reopen the issue? The PR only fixed the incorrect metaclass resolution that I found while working on this issue. I incorrectly made the PR close it. There's still another PR that addresses the actual issue: typeshed pretends tuple's type to not be type. That can cause metaclass conflicts at type checking time while for code that is fine at runtime.
Activity
robsdedude commentedon Nov 8, 2022
As I played further with it, I found that annotating
class MyMeta(type(tuple)): passwith# typing: ignore[misc]allows me to not have to annotate every child class. That's something, I guess. But at least the docs should tell people about this workaround. Ideally, as said, it shouldn't even be necessary.sobolevn commentedon Nov 8, 2022
Right now
tupleis defined asclass tuple(Sequence[_T_co], Generic[_T_co]):(and it is not justtuple, other types are affected as well)So, it has
ABCMetametaclass according to the typeshed 🤔I don't think we can remove it.
Your ideas seem reasonable. Do you want to work on it? :)
AlexWaygood commentedon Nov 8, 2022
This problem was previously discussed in python/typeshed#1595
robsdedude commentedon Nov 8, 2022
I'm happy to contribute. a) I haven't contributed to mypy before, so I wouldn't exactly know where to start, guidance welcome b) Can you elaborate on "Your ideas seem reasonable"? I'm not sure which of the two approaches I mentioned you'd like to see me implement. If the first one: I wouldn't know where to start looking. Judging by what AlexWaygood wrote, this seems to not be solvable in mypy, but typshed which again would need extended typing features in Python itself... so that seems a little unfeasible to me?
I'm happy to jump into a chat somewhere to talk about the details, if you want to help me getting started.
sobolevn commentedon Nov 8, 2022
There are two cool ideas in this:
@robsdedude you can schedule a meeting if you want: https://calendly.com/sobolevn/ Don't worry that it says 2hours (we can do much faster). I will guide you through the contribution process :)
Or drop me an email:
mail@sobolevn.meif that's easier for you.sobolevn commentedon Nov 9, 2022
@robsdedude meeting notes:
mypy/mypy/checker.py
Lines 2496 to 2523 in dbcbb3f
mypy/test-data/unit/check-classes.test
Lines 6821 to 6852 in dbcbb3f
pytest -k TEST_NAMErobsdedude commentedon Nov 11, 2022
Digging further into it and working on improving the error experience (including the message) as discussed in our meeting, I'm looking at
testMetaclassSubclassSelfand in particularChildOfConflict1. My opinion on this is that, while mypy is (most of the time, anyway) correct in assuming that this class has a metaclass conflict, I don't think the error helps the user at all, because this is not where the error originates (which would be the class definition ofConflict3). I think, this behavior of mypy just a) pollutes the output, making it harder for the user to find the place in the code they need to fix and b) should mypy be incorrect about some assumptions (like with some built-in types), it's not enough to just mute the single place of failure (Conflict3) but every child class of it as well, which can be very tedious.What do you think about only emitting a metaclass conflict at the place it's introduced at?
This poses some technical difficulty as it's unclear, which metaclass mypy should assume for a class it considered having a metaclass conflict. But I think this can be resolved by traversing the mro and accepting the first explicitly declared metaclass as truth while falling back to
typeif there is none (which I believe can't actually happen).Thoughts on this proposal?
erictraut commentedon Nov 11, 2022
@sobolevn, you said "So, it has ABCMeta metaclass according to the typeshed". Where is
ABCMetaintroduced? Does mypy assume that allProtocolclasses implicitly haveABCMetaas a metaclass? Or perhaps it assumes this for protocol classes that have one or more@abstractmethod? Pyright doesn't make this assumption, so it doesn't report a metaclass conflict in the code sample at the top of this thread. I mention this because it could be a simple solution if mypy were not to assume thatABCMetais the metaclass for protocols.robsdedude commentedon Nov 14, 2022
Another thing to discuss: changing the error code is a breaking change. For users that have silenced mypy complaining about metaclass conflicts (
# type: ignore[misc]) this change would make their typecheck start to fail. So I don't know how you handle these kind of things.Moreover, I noticed that changing the error code didn't make any tests fail. So it appears there are no tests for those, are there?
sobolevn commentedon Nov 14, 2022
In my opinion changing codes from
misctosome_exact_codeis a good thing.Because it adds more context to it. And changing failing places is rather easy for end users.
But, I am open for other opinions.
sobolevn commentedon Nov 14, 2022
@erictraut yes, this is something I will investigate. Thanks!
robsdedude commentedon Nov 14, 2022
For the record: I found that
mypy 0.990happily acceptsWhile the interpreter is, rightfully so, not amused.
I'll see if I can also address this bug as it is very much related.
Note for myself:
class D(A2, C1, B1): passhowever, is fine.robsdedude commentedon Nov 14, 2022
Reading Python's documentation it seems mypy should actually be right and Python should accept
class D(A2, B1, C1): pass. So I belief this is either a bug in the interpreter or the docs aren't quite accurate.Can someone shed some light on which one is the case?
erictraut commentedon Nov 14, 2022
I think the interpreter is correct here. Mypy should flag
Das a metaclass conflict. (FWIW, pyright reports an error here.)As you noted, if you change the order of the base classes for
Dand moveC1beforeB1, the problem is eliminated.robsdedude commentedon Oct 22, 2024
Sorry for the notification, but I've been waiting patiently for my PRs to get reviewed without luck. Could somebody please have a look before they go even more stale and have even more merge conflicts?
mukundjalan commentedon Dec 24, 2024
@JukkaL reaching out to you because you are the top contributor for this repository. Could you please help get this PR reviewed and merged as a lot of us are facing issues due to this.
Thanks & apologies for bothering you.
Fix metaclass resolution algorithm (#17713)
robsdedude commentedon Jun 23, 2025
@hauntsaninja thanks for reviewing and merging my PR. Can you please reopen the issue? The PR only fixed the incorrect metaclass resolution that I found while working on this issue. I incorrectly made the PR close it. There's still another PR that addresses the actual issue: typeshed pretends
tuple's type to not betype. That can cause metaclass conflicts at type checking time while for code that is fine at runtime.The aforementioned PR: #17682
hauntsaninja commentedon Jun 23, 2025
Looks like Brian re-opened the issue. Feel free to tag me if you can't get tests passing after fixing the conflicts!
Metaclass conflict check improvements (#17682)