Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork32.4k
Description
Bug report
The optimisation to LOAD_SUPER_ATTR introduced by#104270 appears to have introduced a backwards incompatibility.
The following code will reproduce the problem:
class MyInstance: def __new__(cls, ptr, name, bases, attrs): self = super().__new__(cls, name, bases, attrs) print(f"{MyInstance=} {self=} {type(self)=}, {super(MyInstance, type(self))=}") super(MyInstance, type(self)).__setattr__(self, "ptr", ptr) return self def __setattr__(self, name, value): raise Exception()class MyClass(MyInstance, type): def __new__(cls, name): self = super().__new__(cls, id(name), name, (MyInstance,), {}) return selfclass1 = MyClass("Class1")print(f"{class1.ptr=}")class2 = MyClass("Class2")print(f"{class2.ptr=}")
Under 3.12.0a7 (and previous stable Python versions going back to at least 3.7), this will succeed, outputting:
MyInstance=<class '__main__.MyInstance'> self=<class '__main__.Class1'> type(self)=<class '__main__.MyClass'>, super()=<super: <class 'MyInstance'>, <MyClass object>>class1.ptr=4364457392MyInstance=<class '__main__.MyInstance'> self=<class '__main__.Class2'> type(self)=<class '__main__.MyClass'>, super()=<super: <class 'MyInstance'>, <MyClass object>>class2.ptr=4365761904
Under 3.12.0b1, it raises an error:
MyInstance=<class '__main__.MyInstance'> self=<class '__main__.Class1'> type(self)=<class '__main__.MyClass'>, super()=<super: <class 'MyInstance'>, <MyClass object>>class1.ptr=4370144336MyInstance=<class '__main__.MyInstance'> self=<class '__main__.Class2'> type(self)=<class '__main__.MyClass'>, super()=<super: <class 'MyInstance'>, <MyClass object>>Traceback (most recent call last): File "/Users/rkm/beeware/rubicon/objc/repro.py", line 24, in <module> class2 = MyClass("Class2") ^^^^^^^^^^^^^^^^^ File "/Users/rkm/beeware/rubicon/objc/repro.py", line 16, in __new__ self = super().__new__(cls, id(name), name, (MyInstance,), {}) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/rkm/beeware/rubicon/objc/repro.py", line 7, in __new__ super().__setattr__(self, "ptr", ptr)TypeError: expected 2 arguments, got 3
That is - the firstMyClass
instance can be instantiated; however, the second instance fails in mid-construction when invoking__setattr__
. The state of the objects prior to the__setattr__
invocation appear to be identical, but the__setattr__
method behaves differently on the second invocation.
Git bisect has narrowed the cause down to#104270 (CC@carljm,@markshannon).
The reproduction case also fails if the call tosuper
:
super(MyInstance, type(self)).__setattr__(self, "ptr", ptr)
is replaced with the simpler:
super().__setattr__(self, "ptr", ptr)
Background
This test case has been extracted fromrubicon-objc, causingbeeware/rubicon-objc#313. Rubicon is a wrapper around the Objective C runtime used by macOS and iOS;MyInstance
is an analog ofObjCInstance
, andMyClass
is a an analog ofObjCClass
.ObjCInstance
has an implementation of__setattr__
to redirect attribute access to the underlying ObjC calls. However, during construction,ObjCInstance
needs to store aptr
of the underlying ObjC instance. This isn't a valid ObjC attribute, sosuper()
is used to access the underlying__setattr__
implementation to set the attribute.
Your environment
- CPython versions tested on: 3.7.9, 3.10.11, 3.12.0a7, 3.12.0b1
- Operating system and architecture: macOS ARM64