What is @staticmethod in Protocols?
Our typechecker stumbled across this Protocol: https://github.com/google/jax/blob/1c7f8efce64ca2927fd3f2de9250063d7c5fe09e/... This is a Protocol with a @staticmethod. PEP 544 ( https://peps.python.org/pep-0544/) says only: "Static methods, class methods, and properties are equally allowed in protocols." The PEP doesn't say anything at all about what it *means* to have one of these in a structural supertype. Structurally, consider a Protocol like: class P(Protocol): @staticmethod def foo(x: str) -> int: ... The protocol has an attribute named `foo` with type `(x: str) -> int`. @staticmethod is an implementation detail which does not affect the structure of P. The protocol should be satisfied by *any* type with an attribute named `foo` with type `(x: str) -> int`. (Similarly, if a protocol requires a @property getter you could satisfy it with a field, etc.) Test: ``` from typing import Protocol class P(Protocol): @staticmethod def foo(x: str) -> int: ... class A: @staticmethod def foo(x: str) -> int: return len(x) class B: def foo(self, x: str) -> int: return len(x) class Str2Int(Protocol): def __call__(self, x: str) -> int: ... class C: foo: Str2Int def __init__(self): self.foo = lambda x: 0 def test(x: P) -> None: return test(A()) test(B()) test(C()) ``` Pyright agrees with me. $ pyright --version pyright 1.1.306 $ pyright static-protocols.py 0 errors, 0 warnings, 0 informations Mypy does not :( : $ mypy --version mypy 1.2.0 (compiled: yes) $ mypy static-protocols.py static-protocols.py:27: error: Argument 1 to "test" has incompatible type "B"; expected "P" [arg-type] static-protocols.py:27: note: Protocol member P.foo expected class or static method static-protocols.py:28: error: Argument 1 to "test" has incompatible type "C"; expected "P" [arg-type] static-protocols.py:28: note: Protocol member P.foo expected class or static method Found 2 errors in 1 file (checked 1 source file) -- [image: logo] Kevin Millikin Software Engineer kmillikin@deepmind.com If you received this email in error, please do not forward it to anyone else (it may contain confidential or privileged information), erase all copies and attachments, and let me know that it has gone to the wrong person.
On Fri, 5 May 2023 at 10:42, Kevin Millikin via Typing-sig < typing-sig@python.org> wrote:
Our typechecker stumbled across this Protocol:
https://github.com/google/jax/blob/1c7f8efce64ca2927fd3f2de9250063d7c5fe09e/...
This is a Protocol with a @staticmethod. PEP 544 ( https://peps.python.org/pep-0544/) says only:
"Static methods, class methods, and properties are equally allowed in protocols."
The PEP doesn't say anything at all about what it *means* to have one of these in a structural supertype. Structurally, consider a Protocol like:
class P(Protocol): @staticmethod def foo(x: str) -> int: ...
The protocol has an attribute named `foo` with type `(x: str) -> int`. @staticmethod is an implementation detail which does not affect the structure of P.
The protocol should be satisfied by *any* type with an attribute named `foo` with type `(x: str) -> int`.
(Similarly, if a protocol requires a @property getter you could satisfy it with a field, etc.)
...
Pyright agrees with me.
$ pyright --version pyright 1.1.306 $ pyright static-protocols.py 0 errors, 0 warnings, 0 informations
Mypy does not :( :
$ mypy --version mypy 1.2.0 (compiled: yes) $ mypy static-protocols.py static-protocols.py:27: error: Argument 1 to "test" has incompatible type "B"; expected "P" [arg-type] static-protocols.py:27: note: Protocol member P.foo expected class or static method static-protocols.py:28: error: Argument 1 to "test" has incompatible type "C"; expected "P" [arg-type] static-protocols.py:28: note: Protocol member P.foo expected class or static method Found 2 errors in 1 file (checked 1 source file)
It is possible to satisfy both checkers by using a property in this case: class P(Protocol): @property def foo(self) -> Callable[[str], int]: ... I guess that is the literal way to say "any type with an immutable attribute named foo with type (str) -> int". I don't know how you could specify that the parameter is called x like this though. I'm not sure whether mypy has a good reason to complain about this but the rejection comes directly from a hard-coded check: https://github.com/python/mypy/blob/541639e474085f18ed61527d97ba4620e4334e09... -- Oscar
participants (2)
-
Kevin Millikin
-
Oscar Benjamin