[bug] Pyrefly doesn't yet filter non-instance methods when inferring attributes
Pyrefly will currently infer the existence of attributes x and y on instances of C here:
class C:
@classmethod
def f0(cls):
cls.x = 15
@staticmethod
def f0(arg: SomeOtherClass):
arg.y = 15
This happens because the attribute collection is happening at bindings time, so it's naively occurring in all defs (assuming they have at least one parameter) nested inside a class body, without understanding decorator and descriptor behaviors
We should find a way to filter these out (the cls cases arguably could define class attributes, although I think that's very low-priority unless we see a concrete use case).
The two methods I can think of are:
- Using
SpecialExportsto handle this in bindings - Saving an
Idx<Key>insideBindingClassFieldfor the type of the first parameter to check its type
The first approach would be easy and handle most cases, the second approach is more powerful:
- It is done at the type level, so can eventually probably handle custom descriptors and such
- It piggybacks off of work we need for parameter type inference anyway
- It would allow us to skip functions where the annotation on
selfis very restrictive (low priority, maybe handy?)
There's another question of whether to count these as class-level attributes or instance-level attributes. Eric Traut brought it up in this comment.
This issue has someone assigned, but has not had recent activity for more than 2 weeks.
If you are still working on this issue, please add a comment so everyone knows. Otherwise, please unassign yourself and allow someone else to take over.
Thank you for your contributions!
I also met this, so I left a sandbox link.
https://pyrefly.org/sandbox/?code=MQAg6gpgNgxg9gWwiALnVALZAFAngJwgDMpcQBlAQwDsATAIzgA8AaTASwGcQuRKQADpQDmEAFCgA7lkIhccAK4gYNEJPzsUENpwyVZNWiCgR91PowUpUuAe2rCe5vCgxxqAOgkSQACQgGBsqKAiYgRKYoCoTccEQc3JyGjEwAXD4AtCAA4hDWhJRQGSjsSDYCyDBYMADW9o4REAyUtZkgAKqcyIQAbqZQAPoothAAFACUqOj2nBUw1vYR%2BIRGwxWcbRBMFRpI1NaSmhiYyACSACIAouGR0RDcrpQHyJwKAgJw%2BNajEB7CHnwrHB4AhQnltCBhHBitDaMR7Jp2O42G4%2Bvg2HkYOM2mANFo%2BHQQPgFM5cK53ME4SBaOxCPNSE45Ip8CB6Pg4JIuvgxDy4fEtJwUKM0k4UON0iAiXlouYiAAiADeTAAvnKeTAoJRONwAEL6CWS1n60XqzXakAAMTgcANkoAAoKnuwYEhybQxIbqcRwtbhakQHr8OKPZ7DUwPPR9CAALwgACMPNAvX6QxGanYUCggnZtAUMGQ-DWyAC7JZj2sWkz3HkSlcyCLIDwhBIZD0G1AIIEViaPrLWClUXw5lG9gSyi1EMFQbEQA&version=3.12
def test(x: int):
return f"{x}"
class Bar:
bar: int
class Foo:
@staticmethod
def foo(x: Bar):
x.bar = 1
ERROR 20:11-14: Attribute
baris implicitly defined by assignment in methodfoo, which is not a constructor [implicitly-defined-attribute]
This issue has someone assigned, but has not had recent activity for more than 2 weeks.
If you are still working on this issue, please add a comment so everyone knows. Otherwise, please unassign yourself and allow someone else to take over.
Thank you for your contributions!