Skip to content

reject arbitrary attribute assignment on bound methods#4201

Closed
knQzx wants to merge 1 commit into
facebook:mainfrom
knQzx:fix/bound-method-attr-assignment
Closed

reject arbitrary attribute assignment on bound methods#4201
knQzx wants to merge 1 commit into
facebook:mainfrom
knQzx:fix/bound-method-attr-assignment

Conversation

@knQzx

@knQzx knQzx commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

a bound method is a types.MethodType, which doesn't override __setattr__/__delattr__, so assigning an arbitrary attribute to it fails at runtime. pyrefly allowed it: lookup_magic_dunder_attr1 had no BoundMethod arm, so the base fell through to the general lookup and resolved the dunder via MethodType.__getattr__ (returns Any), which made the assignment look valid

add a BoundMethod arm mirroring the ClassInstance handling: when the mutating dunder is inherited from object, report it as not-found so the set/delete is rejected. attribute reads are unchanged

closes #4163

A bound method is a types.MethodType, which does not override
__setattr__/__delattr__, so assigning an arbitrary attribute to it fails
at runtime. Pyrefly permitted it: lookup_magic_dunder_attr1 had no
BoundMethod arm, so the base fell through to the general lookup and
resolved __setattr__/__delattr__ via MethodType.__getattr__ (which
returns Any), making the assignment look valid.

Add a BoundMethod arm mirroring the ClassInstance handling: when the
mutating dunder is inherited from object, report the attribute as
not-found so the set/delete is rejected. Attribute reads are unchanged.

Closes facebook#4163
@github-actions

Copy link
Copy Markdown

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

dragonchain (https://github.com/dragonchain/dragonchain)
+ ERROR dragonchain/transaction_processor/level_5_actions_utest.py:256:9-81: Object of class `FunctionType` has no attribute `return_value` [missing-attribute]
+ ERROR dragonchain/transaction_processor/level_5_actions_utest.py:275:9-80: Object of class `FunctionType` has no attribute `side_effect` [missing-attribute]
+ ERROR dragonchain/transaction_processor/level_5_actions_utest.py:294:9-81: Object of class `FunctionType` has no attribute `return_value` [missing-attribute]
+ ERROR dragonchain/transaction_processor/level_5_actions_utest.py:312:9-81: Object of class `FunctionType` has no attribute `return_value` [missing-attribute]

@knQzx

knQzx commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

heads up on the mypy_primer diff: the 4 new errors on dragonchain are all the mock.method.return_value = ... pattern, where the object is typed as the real class instead of Mock. they're technically correct (bound methods really don't have those attrs, mypy flags them too), just noisy on mock-heavy tests. if you'd rather keep test code quiet i can look into special-casing Mock/MagicMock, lmk what you prefer)

@yangdanny97

yangdanny97 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

I think this is probably fine. Thanks for the PR!

@meta-codesync

meta-codesync Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

@yangdanny97 has imported this pull request. If you are a Meta employee, you can view this in D113405966.

@connernilsen connernilsen left a comment

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.

Review automatically exported from Phabricator review in Meta.

@meta-codesync meta-codesync Bot closed this in a950986 Jul 23, 2026
@meta-codesync meta-codesync Bot added the Merged label Jul 23, 2026
@meta-codesync

meta-codesync Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

@yangdanny97 merged this pull request in a950986.

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.

Bound method objects incorrectly allow arbitrary attribute assignment

4 participants