Skip to content

Move self argument checks to a later phase - after decorator application, if any - #19490

Merged
ilevkivskyi merged 8 commits into
python:masterfrom
sterliakov:feature/gh-19392-decorator-selftypes
Jul 28, 2025
Merged

ilevkivskyi merged 8 commits into
python:masterfrom
sterliakov:feature/gh-19392-decorator-selftypes

Conversation

@sterliakov

@sterliakov sterliakov commented Jul 23, 2025 •

Copy link
Copy Markdown
Collaborator

Fixes #19392.
Fixes #18989.
Fixes #18720.
Fixes #13434 (correct support for staticmethod wrappers, but not for equivalent classmethods reported in #18968).

Deferring this check in presence of decorators allows decorators that perform non-trivial transformations (such as making methods from non-methods and vice versa).

@sterliakov

Copy link
Copy Markdown
Collaborator Author

Surprisingly there are a few more more related tickets, I'll go through them tomorrow.

@github-actions

This comment has been minimized.

@bzoracler

Copy link
Copy Markdown
Contributor

This is working towards a much-needed QOL change IMO - thank you.

But thinking about this problem - is some of the self-check procedure itself redundant? Given this example:

class A:
    def method() -> None: ...  # self-check error
    def method2(arg: str, /) -> None: ...  # self-check error

# mypy already picks these up; are the self-check errors above redundant?
A().method()  # Attribute function "method" with type "Callable[[], None]" does not accept self argument  [misc]
A().method2()  # Invalid self argument "A" to attribute function "method2" with type "Callable[[str], None]"

Removing part of the self-check would naturally allow descriptor method transforms (and thus solve the original issue). It would also allow skipping the typing ceremony in several cases, such as omitting @{class,static}method for methods that would never be accessed from an instance, or defining temporary/throwaway decorators in class bodies.

(Of course, an unannotated first positional argument is still implicitly Self-like.)

@sterliakov

Copy link
Copy Markdown
Collaborator Author

@bzoracler For me current behaviour (errors at the definition site) is superior by a huge margin. Tracing selftypes across modules is inconvenient, I really want mypy to say "hey, you're probably missing self here" as early as possible. They are redundant, but otherwise you may not notice missing self on unused methods (please don't ask me why do I care about unused methods...)

I know one and only one use case where this is still a problem (one-time decorators you mentioned). I don't really like when people do that, it takes a few more more seconds to parse in my head, but it's a common pattern that has some merit. We can make that an exception as well: do not emit these errors if func is later used as a decorator in the same class.

omitting @{class,static}method for methods that would never be accessed from an instance

Could you elaborate?

@bzoracler

Copy link
Copy Markdown
Contributor

omitting @{class,static}method for methods that would never be accessed from an instance

Forget the mention of @classmethod here, that was a mistake. Omitting @staticmethod isn't incredibly common, but @staticmethod is redundant for a class used as a static attribute and method holder where instances of the class are never created, so omitting @staticmethod cuts verbosity and saves an extra descriptor call path. (To be clear, I'm not supportive of using such a pattern; it's obviously non-Pythonic.)

Thinking about it a bit more, several other popular languages (C++, Java) don't use explicit self in the signature, so I can see that it does help many people to employ a missing self check at the definition site. Those same languages would also force declaration of static types from the first parameter, so it looks like an invalid self annotation would help the same group of people. I'll discard this idea for now.

@sterliakov

Copy link
Copy Markdown
Collaborator Author

zope.interface changes are correct, I have no idea why they weren't reported before, but now they are.

@sterliakov
sterliakov marked this pull request as ready for review July 25, 2025 21:53
@github-actions

This comment has been minimized.

@sterliakov
sterliakov requested a review from ilevkivskyi July 27, 2025 15:02

@ilevkivskyi ilevkivskyi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LG, this makes sense! I have few suggestions for extra tests.

Comment thread test-data/unit/check-classes.test
@github-actions

This comment has been minimized.

@ilevkivskyi

Copy link
Copy Markdown
Member

@sterliakov The new zope errors are not correct. Previously they were correctly ignored because they were inside an untyped function. You probably need to add a self.in_checked_function() somewhere (like it was in semanal.py from where you moved that error).

@sterliakov

Copy link
Copy Markdown
Collaborator Author

Ough. Thanks! I decided to go with slightly different logic - only silence inside untyped function (same as self.in_checked_function but ignoring the func we are currently analyzing) iff the method is also untyped. This aligns with our checking of nested functions.

@github-actions

Copy link
Copy Markdown
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

zope.interface (https://github.com/zopefoundation/zope.interface)
- src/zope/interface/common/numbers.py:42: error: Method must have at least one argument. Did you forget the "self" argument?  [misc]
- src/zope/interface/common/numbers.py:52: error: Method must have at least one argument. Did you forget the "self" argument?  [misc]
- src/zope/interface/common/collections.py:119: error: Method must have at least one argument. Did you forget the "self" argument?  [misc]
- src/zope/interface/common/collections.py:134: error: Method must have at least one argument. Did you forget the "self" argument?  [misc]
- src/zope/interface/common/collections.py:174: error: Method must have at least one argument. Did you forget the "self" argument?  [misc]
- src/zope/interface/common/collections.py:182: error: Method must have at least one argument. Did you forget the "self" argument?  [misc]

@ilevkivskyi
ilevkivskyi merged commit ad76e16 into python:master Jul 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants