Skip to content

Bind self for un-annotated Callable class attributes#4203

Open
ANU-2524 wants to merge 1 commit into
facebook:mainfrom
ANU-2524:devin/1784397585-callable-method-binding
Open

Bind self for un-annotated Callable class attributes#4203
ANU-2524 wants to merge 1 commit into
facebook:mainfrom
ANU-2524:devin/1784397585-callable-method-binding

Conversation

@ANU-2524

Copy link
Copy Markdown

Class attributes whose type is a Callable (rather than a def) were only treated as methods when named like a dunder or annotated ClassVar[Callable]. Any other Callable stored in the class body was treated as a plain data attribute, so calling it on an instance did not drop self. This produced a false bad-argument-count for factory-produced methods such as PySpark's Column.isNull/asc/eqNullSafe, which are assigned as isNull = _unary_op(...) (issue #3465).

At runtime a plain function object stored on a class is a descriptor, so it binds self. mypy and pyright model this by binding self for any class-body Callable unless it carries an explicit, non-ClassVar annotation. is_method now follows the same rule: an inferred (un-annotated) Callable, a ClassVar[Callable], or a dunder binds self; an explicitly annotated bare Callable does not. This also makes a class-body lambda a bound method, matching mypy/pyright.

Summary

Fixes #3465

Test Plan

  • Added regression tests covering factory-assigned Callable class attributes.
  • Verified that instance access correctly binds self while class access does not.
  • Confirmed behavior matches mypy and pyright for inferred Callable, explicit Callable, and ClassVar[Callable].
  • Reproduced the original PySpark example (isNull, asc, desc, eqNullSafe) and verified the false bad-argument-count errors are resolved.

Class attributes whose type is a `Callable` (rather than a `def`) were only treated as methods when named like a dunder or annotated `ClassVar[Callable]`. Any other `Callable` stored in the class body was treated as a plain data attribute, so calling it on an instance did not drop `self`. This produced a false `bad-argument-count` for factory-produced methods such as PySpark's `Column.isNull`/`asc`/`eqNullSafe`, which are assigned as `isNull = _unary_op(...)` (issue facebook#3465).

At runtime a plain function object stored on a class is a descriptor, so it binds `self`. mypy and pyright model this by binding `self` for any class-body Callable unless it carries an explicit, non-`ClassVar` annotation. `is_method` now follows the same rule: an inferred (un-annotated) Callable, a `ClassVar[Callable]`, or a dunder binds `self`; an explicitly annotated bare Callable does not. This also makes a class-body lambda a bound method, matching mypy/pyright.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: False positive bad-argument-count for PySpark Column methods

1 participant