FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

mypy on python 3.9 does not support @staticmethod in Protocols · Issue #15257 · python/mypy · GitHub

Repository navigation

mypy on python 3.9 does not support @staticmethod in Protocols #15257

Closed

Description

Bug Report

(A clear and concise description of what the bug is.)

To Reproduce

from __future__ import annotations

from typing import Protocol

class Demo(Protocol):
    @staticmethod
    def demo(v: int) -> int:
        pass


def returns_one(v: int) -> int:
    return 1

class Inheriting(Demo):
    demo = staticmethod(returns_one)

https://mypy-play.net/?mypy=latest&python=3.9&gist=1438683686842d8a03bfbc32646c7952&flags=strict

Expected Behavior

Success: no issues found in 1 source file

Actual Behavior

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 version used: 3.9

Activity

  1. graingert commented on May 17, 2023

    ContributorAuthor

    note that using @staticmethod does work:

    from __future__ import annotations
    
    from typing import Protocol
    
    class Demo(Protocol):
        @staticmethod
        def demo(v: int) -> int:
            pass
    
    
    def returns_one(v: int) -> int:
        return 1
    
    class Inheriting(Demo):
        @staticmethod
        def demo(v: int) -> int:
            return 1
  2. ikonst commented on May 17, 2023

    Contributor

    Likely "culprit" is python/typeshed#9771:
    https://github.com/python/typeshed/blob/274f449edce829201ef63da2e566497b28d3d97c/stdlib/builtins.pyi#L126-L131
    specifically

    def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _R_co: ...

    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?

  3. graingert commented on May 17, 2023

    ContributorAuthor

    Python 3.9 doesn't have __call__

    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__

  4. AlexWaygood commented on May 17, 2023

    Member

    @AlexWaygood tl;dr on why def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _R_co: ... is not applicable to <3.10?

    Staticmethods aren't callable on Python 3.9! The __call__ method was added in 3.10. If you do the following...

    x = staticmethod(lambda: 42)
    
    class Foo:
        @staticmethod
        def bar(): return 42
    
    y = Foo.__dict__['bar']

    ... then you'll find that both x and y are callable on Python 3.10, but not on Python 3.9

  5. ikonst commented on May 17, 2023

    Contributor

    FWIW I also don't understand how the presence of __call__ makes it work.

  6. AlexWaygood commented on May 17, 2023

    Member

    note that using @staticmethod does work:

    from __future__ import annotations
    
    from typing import Protocol
    
    class Demo(Protocol):
        @staticmethod
        def demo(v: int) -> int:
            pass
    
    
    def returns_one(v: int) -> int:
        return 1
    
    class Inheriting(Demo):
        @staticmethod
        def demo(v: int) -> int:
            return 1

    I think this bug is just a specific manifestation of #4574. Looks like the same bug to me.

  7. ikonst commented on May 17, 2023

    Contributor

    Still unclear to me why it's "fixed" with the addition of __call__.

  8. AlexWaygood commented on May 17, 2023

    Member

    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.

  9. sterliakov commented on Sep 7, 2025

    Collaborator

    Mypy accepts this snippet since 1.16.0 (more specifically - since #18847)

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrongtopic-descriptorsProperties, class vs. instance attributes

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL