main.py:15: error: Incompatible types in assignment (expression has type "staticmethod[[int], int]", base class "Demo" defined the type as "Callable[[int], int]") [assignment]
Your Environment
Mypy version used: 1.3.0
Mypy command-line flags:
Mypy configuration options from mypy.ini (and other config files):
Python 3.9.16 (main, Dec 7 2022, 01:12:08)
[GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> staticmethod(lambda: None).__call__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'staticmethod' object has no attribute '__call__'
but that shouldn't be considered by mypy because it's called via .__get__(...).__call__
Still unclear to me why it's "fixed" with the addition of __call__.
I'd imagine it's because mypy applies some special-casing to methods decorated with @staticmethod that means that it considers those methods to be callable, even though that's not what the stubs say, and even though it's strictly true on Python <3.10 (the object returned by staticmethod.__get__ is callable on Python 3.9, but staticmethod instances themselves aren't). This special-casing probably predates the point even mypy added generalised support for descriptors, so it's possible it could be partly or fully removed now in 2023. I believe mypy only applies this special-casing to methods decorated with @staticmethod, not with direct calls to staticmethod, hence the bug.
As such, because it (incorrectly) sees methods decorated with @staticmethod as callable (due to the special-casing), it emits an error when a staticmethod in the superclass is overridden by something that doesn't have a __call__ method in the subclass.
Activity
graingert commented on May 17, 2023
this passes on 3.10, 3.11 and 3.12
https://mypy-play.net/?mypy=latest&python=3.10&gist=1438683686842d8a03bfbc32646c7952&flags=strict
https://mypy-play.net/?mypy=latest&python=3.11&gist=1438683686842d8a03bfbc32646c7952&flags=strict
https://mypy-play.net/?mypy=latest&python=3.12&gist=1438683686842d8a03bfbc32646c7952&flags=strict
graingert commented on May 17, 2023
note that using @staticmethod does work:
ikonst commented on May 17, 2023
Likely "culprit" is python/typeshed#9771:
https://github.com/python/typeshed/blob/274f449edce829201ef63da2e566497b28d3d97c/stdlib/builtins.pyi#L126-L131
specifically
which makes ... staticmethods overridable with regular callables?
@AlexWaygood tl;dr on why def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _R_co: ... is not applicable to <3.10?
graingert commented on May 17, 2023
Python 3.9 doesn't have __call__
but that shouldn't be considered by mypy because it's called via .__get__(...).__call__
AlexWaygood commented on May 17, 2023
Staticmethods aren't callable on Python 3.9! The __call__ method was added in 3.10. If you do the following...
... then you'll find that both x and y are callable on Python 3.10, but not on Python 3.9
ikonst commented on May 17, 2023
FWIW I also don't understand how the presence of __call__ makes it work.
AlexWaygood commented on May 17, 2023
I think this bug is just a specific manifestation of #4574. Looks like the same bug to me.
ikonst commented on May 17, 2023
Still unclear to me why it's "fixed" with the addition of __call__.
AlexWaygood commented on May 17, 2023
I'd imagine it's because mypy applies some special-casing to methods decorated with @staticmethod that means that it considers those methods to be callable, even though that's not what the stubs say, and even though it's strictly true on Python <3.10 (the object returned by staticmethod.__get__ is callable on Python 3.9, but staticmethod instances themselves aren't). This special-casing probably predates the point even mypy added generalised support for descriptors, so it's possible it could be partly or fully removed now in 2023. I believe mypy only applies this special-casing to methods decorated with @staticmethod, not with direct calls to staticmethod, hence the bug.
As such, because it (incorrectly) sees methods decorated with @staticmethod as callable (due to the special-casing), it emits an error when a staticmethod in the superclass is overridden by something that doesn't have a __call__ method in the subclass.
sterliakov commented on Sep 7, 2025
Mypy accepts this snippet since 1.16.0 (more specifically - since #18847)