Skip to content

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

Closed
@nsoranzo

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).

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions