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/jax/_src/nn/initializers.py#L44

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/mypy/subtypes.py#L1090-L1092

--
Oscar