Skip to content

Metaclass conflict error when base class uses an imported metaclass with no annotation #14182

Closed

Description

@nsoranzo

Bug Report

Since mypy 0.990 I am getting a "Metaclass conflict" error for the definition of a class derived from a base class which uses a metaclass imported from a third-party library that doesn't provide type annotations.

To Reproduce

# Import metaclass from external module which doesn't provide type annotations
from mymodule import MyMeta


class Base(metaclass=MyMeta):
    pass


# This generates the error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases  [misc]
class Derived(Base):
    pass


# If there is type annotation for the metaclass, no issue.
class MyMeta2(type):
    pass


class Base2(metaclass=MyMeta2):
    pass


class Derived2(Base2):
    pass

Also available as gist at: https://mypy-play.net/?mypy=latest&python=3.11&gist=6d41ba53b2226e3bef16df71554b10b1

Expected Behavior

No errors.

Actual Behavior

metaclass_conflict.py:10: error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases  [misc]
    class Derived(Base):
    ^
Found 1 error in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 0.911
  • Mypy command-line flags:
  • Mypy configuration options from mypy.ini (and other config files):
    show_error_codes = True
    ignore_missing_imports = True
    pretty = True
  • Python version used: 3.8

More info

I tried some debugging and found out that:

  • the error comes from the TypeChecker.check_metaclass_compatibility() method in mypy/checker.py
  • Using the reproducer example above, when mypy enters the check_metaclass_compatibility() method for the derived classes, the main difference between Derived (which errors) and Derived2 (which doesn't) is that typ.metaclass_type is None in the first case (instead of the expected MyMeta) and MyMeta2 in the second (correctly).
  • This pointed me to look at the TypeInfo.calculate_metaclass_type() method in mypy/nodes.py . When run for the Derived object, the only candidate metaclass is MyMeta (correctly), but then this is excluded in the following for loop because MyMeta.type.mro is [] (I supposed because it's missing type annotation).

Activity

  1. kbehlers commented on Dec 16, 2022

    @kbehlers

    For anyone else that wanders through here because they are getting error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases [misc]

    As mentioned in the description on this issue, check_metaclass_compatibility was introduced in v0.990 and is the source of the error.

    I was getting the error when defining a custom Permission for Django Rest Framework.

    That eventually led me to find a solved issue on djangorestframework-stubs typeddjango/djangorestframework-stubs#280. So in my case, although I couldn't see the conflict looking at the drf source code, it instead existed in the stub where there was an additional _SupportsHasPermission(Protocol) class in the mro.

    mypy playground reproducing the specific red herring: https://mypy-play.net/?mypy=0.990&python=3.11&gist=bf79ced1e7ed3fa71a869056e8d42f8b

  2. zcutlip commented on Jan 28, 2023

    @zcutlip

    Here's a similar problem. Not if it's related. In this case, inheriting from dict while setting a custom metaclass results in a mypy error, but no run-time error:

    # Metclass type annotation clearly available
    class MyMeta(type):
        pass
    
    # mypy metclass conflict error
    class MyExampleDict(dict, metaclass=MyMeta):
    
        def __init__(self, data: Dict):
            super().__init__(data)

    However, if we make a metaclass that extends python's ABCMeta, mypy doesn't complain:

    from abc import ABCMeta
    
    class MyMeta2(ABCMeta):
        pass
    
    # no mypy error with MyMeta2
    class MyExampleDict2(dict, metaclass=MyMeta2):
    
        def __init__(self, data: Dict):
            super().__init__(data)

    Note that neither of the above results in a Python run-time error.

  3. hauntsaninja commented on Jun 20, 2025

    @hauntsaninja
    Collaborator

    I think this is resolved on latest master

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

Metadata

Metadata

Assignees

Labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions