Skip to content

staticmethod wrapped around function sets first TypeVar argument to class name #15769

@b-kamphorst

Description

@b-kamphorst

Bug Report

Wrapping a function that accepts TypeVar arguments in a staticmethod will fool mypy into thinking that the first TypeVar argument is of type <classname> | None. This only applies if a function is wrapped, not when a method decorated.

To Reproduce

from typing import TypeVar

T = TypeVar("T")


def foo(a: T) -> T:
    return a


class Foo:
    foo = staticmethod(foo)

    @staticmethod
    def bar(a: T) -> T:
        return a


reveal_type(foo)      # Revealed type is "def [T] (a: T`-1) -> T`-1"
reveal_type(Foo.foo)  # Revealed type is "def [T] (a: Union[__main__.Foo, None]) -> Union[__main__.Foo, None]"
reveal_type(Foo.bar)  # Revealed type is "def [T] (a: T`-1) -> T`-1"

Foo.foo("a")          # error: Argument 1 has incompatible type "str"; expected "Foo | None"  [arg-type]
Foo.bar("a")          # no issue

https://mypy-play.net/?mypy=latest&python=3.11&gist=2c0e679c14a40c36932905bdc4299cc1

The issue persists if the TypeVar is not the first variable, e.g. def foo(a: str, b: T) has the same issue. If there are multiple TypeVars, then the issue is only with the first one, e.g. def foo(a: T1, b: T2).

The issue was introduced in mypy 1.2.0 -- the example works fine in 1.1.1.

Expected Behavior

I would expect mypy to behave identical for the invocations of staticmethod as an applied function and as a decorator. Both should behave as the staticmethod decorator does (Foo.bar).

Actual Behavior

Mypy incorrectly thinks that the first TypeVar becomes Foo | None, whereas it should still be a fully generic TypeVar.

Your Environment

  • Mypy version used: 1.2.0 - 1.4.1
  • Mypy command-line flags:
  • Mypy configuration options from mypy.ini (and other config files):
  • Python version used: 3.11

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions