-
-
Notifications
You must be signed in to change notification settings - Fork 31k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bpo-41295: Allow overriding __setattr__ on multiple inheritance #21473
Conversation
I would appreciate some guidance on where to put tests for this :) |
79a0723
to
e0bc87d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please add your regression as test case, too?
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
e0bc87d
to
204f00a
Compare
I added the test to test_desrc.py, I'm not sure if that's where it should go, let me know, thanks for the reviews! :) |
I have made the requested changes; please review again |
Thanks for making the requested changes! @tiran: please review the changes made to this pull request. |
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
204f00a
to
3feca47
Compare
Awesome, thanks a lot for the quick review :) I have made the requested changes; please review again |
Thanks for making the requested changes! @tiran: please review the changes made to this pull request. |
Thanks! I'm leaving the final review to @scoder . |
I'll have to take a deeper look at this, just a quick comment: |
3feca47
to
d6bb0e1
Compare
I started looking a bit more closely into this (I'm kinda new to the codebase :) ), fixed a bit the variable name, and added comments there. For reference, I'm trying to do what's said in the email thread: https://mail.python.org/pipermail/python-dev/2003-April/034605.html |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't believe this is right yet. Here's another test case (which should raise TypeError on obj.test = False
):
class A(type):
def __setattr__(cls, key, value):
object.__setattr__(cls, key, value)
class B: # **UPDATE**: I mistakenly has B(A) here
pass
class C(B, A):
pass
obj = C('D', (object,), {})
try:
obj.test = False
except Exception as err:
print("Good, it raised", repr(err))
else:
assert False, "Didn't raise!!"
Lib/test/test_descr.py
Outdated
@@ -1458,6 +1458,23 @@ class someclass(metaclass=dynamicmetaclass): | |||
pass | |||
self.assertNotEqual(someclass, object) | |||
|
|||
def test_attribute_assignment_bpo41295(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would move the test after test_carloverre
(see line 4296).
Objects/typeobject.c
Outdated
for (i = 0; i < n; i++) { | ||
PyTypeObject *base = (PyTypeObject*) PyTuple_GET_ITEM(mro, i); | ||
if (base->tp_setattro == func) { | ||
/* 'func' is the earliest non-Python implementation in the MRO. */ | ||
/* found a base class who's setattro matches the function being |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"whose"
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
Sorry, I had a typo in my example; it should be The way I understand the example, just because |
The change introduced in bpo-39960, broke being able to override the __setattr__ function unless it's done by the next class in the MRO. For example: ``` class Meta(type): def __setattr__(cls, key, value): type.__setattr__(cls, key, value) class OtherClass: pass class DefaultMeta(OtherClass, Meta): pass obj = DefaultMeta('A', (object,), {}) obj.test = True print(obj.test) ``` Would break as it would first check if `OtherClass` has a custom `__setattr__` and if not would raise error right away. This change goes through the whole MRO, until it finds `type` or `object`, and if only if no class owns the method being called it will throw an error. Signed-off-by: David Caro <[email protected]>
d6bb0e1
to
ecc3b8a
Compare
I've gotten the tests to pass if I look explicitly for PyType_Type and PyBaseObject_Type, but the some tests fail, I think because I should be checking in a more generic way (not only for |
PS. Please don't use |
for (i = 0; i < n; i++) { | ||
PyTypeObject *base = (PyTypeObject*) PyTuple_GET_ITEM(mro, i); | ||
if (base->tp_setattro == func) { | ||
/* 'func' is the earliest non-Python implementation in the MRO. */ | ||
if (base == &PyType_Type || base == &PyBaseObject_Type) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is definitely not right. Special-casing types here (any type, really) will not help the general case.
Let's see if the algorithm in #21528 works in all cases. |
@david-caro I'm sorry, but the fix by @scoder in GH-21528 seems to be more accurate, so I'm closing this PR. Thanks for your efforts -- they helped us to find the right fix. Better luck with your next PR! |
The change introduced in bpo-39960, broke being able to override the
__setattr__
function unless it's done by the next class in the MRO.For example:
Would break as it would first check if
OtherClass
has a custom__setattr__
and if not would raise error right away.This change goes through the whole MRO to find out if there's any class
that overrides it before throwing error.
https://bugs.python.org/issue41295