Skip to content

Fix stubtest false positive for an alias of a classmethod - #21863

Open
arose26 wants to merge 1 commit into
python:masterfrom
arose26:stubtest-classmethod-alias
Open

Fix stubtest false positive for an alias of a classmethod#21863
arose26 wants to merge 1 commit into
python:masterfrom
arose26:stubtest-classmethod-alias

Conversation

@arose26

@arose26 arose26 commented Aug 17, 2026

Copy link
Copy Markdown

Fixes #15717

Binding a classmethod to a second name makes stubtest reject a stub that matches the runtime exactly:

# mod.py
class T:
    def a(self): pass
    b = a
    @classmethod
    def c(cls): pass
    d = c
# mod.pyi
class T:
    def a(self) -> None: ...
    b = a
    @classmethod
    def c(cls) -> None: ...
    d = c
error: mod.T.d variable differs from runtime type def () -> Any
Stub: in file mod.pyi:6
def (cls: type[mod.T])
Runtime: in file mod.py:4
def ()

The plain-method alias b is fine; only the classmethod alias errors.

Cause

Both sides are right, and the comparison is what is wrong. d = c in the stub names the classmethod object, whose type keeps the cls argument, whereas reading T.d at runtime goes through the descriptor and yields a method already bound to the class. verify_var then compares an unbound signature against a bound one and reports a difference that does not exist.

Change

When the runtime value is a method bound to a class, verify_var now compares against the stub type with its leading cls removed.

The check is deliberately narrow. It applies only when:

  • the runtime object is a bound method whose __self__ is a class — an instance method bound to an instance is untouched,
  • the stub type is a callable with at least one argument, and
  • that first argument is a type[...], or is named cls / mcls / metacls.

So a stub that already accounts for the binding, or one whose first parameter is a genuine value parameter, keeps being compared as before. A real mismatch still fails: annotating the alias as c_alias: int against a runtime method is still an error, and there is a test for it.

Tests

Two cases added to test_static_class_method:

  • a class with both a plain-method alias and a classmethod alias, matching at runtime: no error (fails before this change)
  • a classmethod alias annotated as int in the stub: still an error

mypy/test/teststubtest.py passes (67 tests), self-check on mypy/stubtest.py is clean, and ruff / black are clean.

Note

The enum branch of verify_var now reads the same local variable rather than stub.type directly. The value is identical there — an enum member is never a bound method — but self-check cannot narrow stub.type to non-None once the surrounding condition tests the local.

A stub assignment such as d = c refers to the classmethod object and keeps
its cls argument, while reading the same name off the class at runtime
yields a method already bound to it.
Sign up for free to 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.

Classmethod bound with an additional name trigger stubtest errors

1 participant