-
-
Notifications
You must be signed in to change notification settings - Fork 31.1k
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-46730: Add more info to @property AttributeError messages #31311
Conversation
The property decorator raises AttributeErrors with messages that neither help to identify the problem nor lead to a possible solution. This commit proposes changes in error messages to address the issue.
Hello, and thanks for your contribution! I'm a bot set up to make sure that the project can legally accept this contribution by verifying everyone involved has signed the PSF contributor agreement (CLA). CLA MissingOur records indicate the following people have not signed the CLA: For legal reasons we need all the people listed to sign the CLA before we can look at your contribution. Please follow the steps outlined in the CPython devguide to rectify this issue. If you have recently signed the CLA, please wait at least one business day You can check yourself to see if the CLA has been received. Thanks again for the contribution, we look forward to reviewing it! |
Now, there are a few things to discuss: I absolutely agree with it, since it was the initial idea, however when trying it I've found out that the testing system expects the name of the attribute to be at the end:
So in order to make the message in the proposed format, this should be subject to change as well. P.S. I have signed the CLA, just haven't got the response yet. |
I think those test cases can be adjusted appropriately to match the behavior.
Yep, it usually takes a business day or two to go through. I also think it would be useful to include the type name in the message:
Compare to the current behavior for other attributes in other situations:
I believe this is the relevant code for that case: Lines 1405 to 1407 in 6331c08
|
Using |
Isn't >>> from collections import defaultdict
>>> dict.pop is defaultdict.pop
True
>>> dd = defaultdict()
>>> dd.pop = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'collections.defaultdict' object attribute 'pop' is read-only And there are parallel situations like this: >>> class Base:
... def __float__(self):
... return None
...
>>> class Sub(Base):
... pass
...
>>> float(Sub())
Traceback (most recent call last):
File "<pyshell#28>", line 1, in <module>
float(Sub())
TypeError: Sub.__float__ returned non-float (type NoneType) My thinking is that specifying the type is helpful for identifying which objects in the client code are being used and which line to look at, not where the implementation happens to live: >>> class BaseThingDoer:
... @property
... def attr(self):
... pass
>>> # ( ... Suppose lots of other concrete implementations ... )
>>> class SpecialThingDoer(BaseThingDoer):
... pass
>>> thing = SpecialThingDoer() # These two lines are the lines I'm looking at.
>>> thing.attr = 42 # I think I would expect the error message to say SpecialThingDoer.
AttributeError: can't set attribute 'attr' It's safer and easier to implement if I understand right. |
This is an improvement to the initial patch for the bpo-46730. It adds additional information on class that owns the property that caused an AttributeError.
@sweeneyde, I think your idea doesn't solve the problem of "which line to look at" because of subclasses. class A:
@property
def a(self) -> int:
return None
class B(A):
@property
def b(self) -> int:
return None
class C(B):
pass
>>> A().a = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: property 'a' originating from 'A' has no setter
>>> B().a = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: property 'a' originating from 'A' has no setter
>>> B().b = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: property 'b' originating from 'B' has no setter
>>> C().b = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: property 'b' originating from 'B' has no setter Few points to discuss though:
|
Oh, actually I think I've misunderstood your comment. To the example of Perhaps this could be resolved by choosing the sentence for the error message:
This one implies that the problem is within the current object and thus
And this one implies that the property is the problem and it should be fixed wherever it's defined - in I don't see any convenience difference, however implementing the first one requires less changes, resolves the issue with sketchiness of the first attempt and removes unnecessary size increase. Seems like I should retry it :) |
I think I agree with lots of your assessments -- either type might be more helpful in different circumstances. But for simplicity of implementation and consistency with other similar situations, I'd prefer to just display the type of I don't know what the best verbiage is to convey that that's what is being displayed, but I agree that some word choices are more suggestive of that than others. Maybe something like what Bruce Leban suggested on Python-Ideas, or
Another reason to avoid adding extra state to the property is that the same property object can theoretically live in multiple classes:
|
The misleading behaviour when a property is in two classes isn't really solvable, and also applies if you put it under a different attribute name - functions have the same issue. Since the decorator methods create copies, it's probably not too big of an issue. |
I'm suggesting that it is "solvable" in one sense: just don't store inside a |
I've assessed all variants and came to a conclusion that a combination of all our suggestions would work great: class A:
@property
def a(self) -> int:
return None
class B(A):
@property
def b(self) -> int:
return None
B().b = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: property 'b' in object of type 'B' has no setter
B().a = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: property 'a' in object of type 'B' has no setter
UPD. And yes, it's based on qualname |
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 mostly looking good, just some minor nits remaining.
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 |
Properly escape dot character and return `_format_exc_msg` function to its initial state.
I have made the requested changes; please review again P.S. Seems like I also should make a NEWS entry, right? |
Thanks for making the requested changes! @sweeneyde: please review the changes made to this pull request. |
Yes, please do. You can either click on "Details" of the bevedere/news check or you can use blurb |
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.
LGTM. I'll leave it open for a day or so to see if there are any more comments, but after that, I can merge.
If you want, you can add your name to MISC/ACKS as well.
Shouldn't "object %R" be "%R object"? 42 is an int object, not object int. |
Oh, that's what caused my confusion and I couldn't understand it! :) |
@eryksun, what do you think now? |
I'm okay with either "property %R of %R objects" or "property %R of %R object". I prefer the former. It's common in English when generalizing to a category or class of things, unless that class is already a mass noun. You can see this style used in "Objects/descrobject.c":
|
Reasonable people will disagree, but the pattern I see is that usages of the plural as in "{p} for {T} objects" tend to be situations where the descriptor knows what type it's supposed to apply to, but there is no well-defined notion of "the associated type" for property objects, since you can do In contrast, other error messages tend to use the singular "object" when they display the type of the particular object in question (as this PR is set to do):
|
The commit introduced a reference leak and code was missing checks for NULL. #31389 addresses both problems. |
The property decorator raises AttributeErrors with messages
that neither help to identify the problem nor lead to a possible solution.
This PR proposes changes in error messages to address the issue.
https://bugs.python.org/issue46730