Skip to content

[ty] Add tests for interactions of @classmethod, @staticmethod, and protocol method members#20555

Merged
AlexWaygood merged 1 commit intomainfrom
alex/proto-classmethod-staticmethod-tests
Sep 25, 2025
Merged

[ty] Add tests for interactions of @classmethod, @staticmethod, and protocol method members#20555
AlexWaygood merged 1 commit intomainfrom
alex/proto-classmethod-staticmethod-tests

Conversation

@AlexWaygood
Copy link
Copy Markdown
Member

Summary

This PR adds more tests for interactions between @classmethod, @staticmethod, and subtyping/assignability checks for protocols with method members. This partially addresses @sharkdp's post-merge review comment in #20165 (comment).

There are a lot of TODOs in these tests that are tricky to fix right now due to the fact that we currently consider function-types as not being fully static if their signature includes a self or cls argument that isn't annotated. That means that doing tests regarding the type of a method when accessed on the class object itself is currently quite tricky. I expect it to be a lot easier to resolve these TODOs after #20517 has landed (but I also don't think it's urgent to resolve these TODOs, since @classmethod and @staticmethod members are quite rare).

Comment on lines +2103 to +2107
# A nominal type with a `@staticmethod` can satisfy a protocol with a `@classmethod`
# if the staticmethod duck-types the same as the classmethod member
# both when accessed on the class and when accessed on an instance of the class
# The same also applies for a nominal type with a `@classmethod` and a protocol
# with a `@staticmethod` member
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can understand the reasoning, but this seems a bit surprising to me. Can I extend this reasoning to instance methods as well? Does C implement the HasFInstanceMethod protocol? All type checkers seem to think so, but it does feel wrong to me. Shouldn't there be some constraints on the implicit type of the first parameter that would prevent this?

from typing import Protocol

class HasFInstanceMethod(Protocol):
    def f(self, x: int, /) -> str: ...

class C:
    @classmethod
    def f(cls, *args) -> str:
        return ""

def accept(_: HasFInstanceMethod): ...

accept(C())  # pyright, mypy, pyrefly, ty: fine!

Copy link
Copy Markdown
Member Author

@AlexWaygood AlexWaygood Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does C implement the HasFInstanceMethod protocol? All type checkers seem to think so, but it does feel wrong to me. Shouldn't there be some constraints on the implicit type of the first parameter that would prevent this?

Yes, I agree with you -- and I added (currently failing) tests in this PR that assert this, too ;) they're up on lines 1791-1795.

The rule I intend to implement for ty is that (given a protocol P, a method member f and a would-be nominal subtype N), two things must be true in order for N to satisfy P

  • the get-type of f when accessed on instances of N must be consistent with the signature of f when accessed on instances of P
  • the get-type of f on type[N] must be consistent with the signature of f on when accessed on the class object P

As you note, this is a bit stricter than any other type checker currently implements, so we'll see how it goes; we might have to back out of this. But I'd like to give it a go, at least.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added (currently failing) tests in this PR that assert this, too ;) they're up on lines 1791-1795.

They looked slightly different to me, because with the *args signature in my comment, you can even make type checkers think that the classmethod signature is compatible with a non-classmethod protocol signature.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, sorry -- I missed that. Hmm, I can see how that might be counter-intuitive, but yes, I can't immediately see a principled reason whereby we'd decide that C shouldn't be a subtype of HasFInstanceMethod there...

Copy link
Copy Markdown
Contributor

@sharkdp sharkdp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@AlexWaygood AlexWaygood merged commit b7d5dc9 into main Sep 25, 2025
38 checks passed
@AlexWaygood AlexWaygood deleted the alex/proto-classmethod-staticmethod-tests branch September 25, 2025 09:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

testing Related to testing Ruff itself ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants