Re: [Python-ideas] The AttributeError/__getattr__ mechanism

A dedicated subclass of *Attr*ibuteError to trigger __get*attr*__ is more natural. I mean it's unnatural to convert AttributeError to AttributeDynamicError by property or descriptor. Why not use RuntimeError as that of PEP479?
And there's no need to write custom property-like descriptor factory when you want to trigger __getattr__, although it may be only a few lines of code.

On Mon, Nov 30, 2015 at 5:10 AM, 王珺 <wjun77@gmail.com> wrote:
A dedicated subclass of AttributeError to trigger __getattr__ is more natural. I mean it's unnatural to convert AttributeError to AttributeDynamicError by property or descriptor. Why not use RuntimeError as that of PEP479?
Earlier I posted some example code that would do exactly that. If an AttributeError would escape a property function, it becomes a RuntimeError. Does that do what you're looking for? ChrisA

On Nov 29, 2015, at 10:10, 王珺 <wjun77@gmail.com> wrote:
A dedicated subclass of AttributeError to trigger __getattr__ is more natural. I mean it's unnatural to convert AttributeError to AttributeDynamicError by property or descriptor.
Again, nobody has suggested that the descriptor protocol should do such a thing, so I don't know why you keep arguing about it. I did suggest that maybe the property descriptor should do so. Or maybe not. If your only problem with my alternate proposal is something that was included as a "maybe" optional bit in parentheses, then you don't seem to have an actual problem with my alternate proposal. Which brings us back to the point: My alternate proposal is less disruptive and more backward-compatible. Is there some actual problem with it, or some other reason to prefer your original version?
Why not use RuntimeError as that of PEP479?
Three reasons: 1. There are thousands of lines of working code that handle AttributeError; forcing them to change to handle RuntimeError would be gratuitous backward incompatibility. 2. RuntimeError suggests that the code did something unexpected wrong, rather than a specific thing, possibly intentionally, that you may want to trap and handle. In PEP 479, this makes sense--it was decided that generators should not raise StopIteration instead of returning, and therefore it is a serious error to do so, not just a normal exception. But raising AttributeError from a property or other descriptor is a perfectly reasonable thing to do. It comes naturally from build-time proxies. It's recommended in the descriptor HOWTO. And so on. It makes perfect sense--and there's no other way to accomplish what it does, either. (How would you implement a write-only property that makes "print(spam.eggs)" raise an AttributeError except by raising it in the property function?) 3. In the vast majority of uses of properties and other descriptors, there is no __getattr__, and AttributeError is exactly what people want there. The only problem to be solved here is the rare but unexpected and annoying interaction of such dynamic AttributeErrors and __getattr__. Preventing descriptors from raising AttributeError even in the common cases to block that rare case is like removing a smudge from your table by disintegrating the table. Yes, the smudge is gone, but you haven't come out ahead.
And there's no need to write custom property-like descriptor factory when you want to trigger __getattr__, although it may be only a few lines of code.
Your original suggestion would make it _impossible_ for a descriptor to trigger __getattr__. And so would your suggestion here of converting AttributeError to RuntimeError. That doesn't remove the need; it leaves the need but makes it impossible to satisfy.

Earlier I posted some example code that would do exactly that. If an AttributeError would escape a property function, it becomes a RuntimeError. Does that do what you're looking for?
Again, nobody has suggested that the descriptor protocol should do such a
Is there some actual problem with it, or some other reason to prefer your original version? Your original suggestion would make it _impossible_ for a descriptor to
Yes, while I need to decorate every property with *dontraise*. In python 3.5 or older it do be a handy tool and thanks for your suggestion. However, similar scheme could be used to solve the problem of PEP479, but a change in the interpreter core is made. I would like similar change in interpreter core or stdlib in python 3.6, so no one will encounter the same problem with future versions of python, knowing this discussion or not. thing, so I don't know why you keep arguing about it. I myself suggested it as an option if nobody else, that's all. trigger __getattr__. I proposed 3 versions: AttributeMissError, RuntimeError, or 'object.__getattribute__ calls __getattr__ explicitly; __getattr__ not triggered by AttributeError anymore' in the issue tracker. In fact I think I am not qualified to talk about the detailed implementation because I know little about the related CPython code. I said I personally prefer no change to your version in aesthetic, which means that I don't have any persuasive reasons. I think expressing opinion is more important than persuading others.
Three reasons: These looks reasonable to me and thanks for your explanation. Now I think your version is better than RuntimeError. So if compatibility do count, your version seems the only choice other than no change.
2015-11-30 3:31 GMT+08:00 Andrew Barnert <abarnert@yahoo.com>:
On Nov 29, 2015, at 10:10, 王珺 <wjun77@gmail.com> wrote:
A dedicated subclass of *Attr*ibuteError to trigger __get*attr*__ is more natural. I mean it's unnatural to convert AttributeError to AttributeDynamicError by property or descriptor.
Again, nobody has suggested that the descriptor protocol should do such a thing, so I don't know why you keep arguing about it.
I did suggest that maybe the property descriptor should do so. Or maybe not. If your only problem with my alternate proposal is something that was included as a "maybe" optional bit in parentheses, then you don't seem to have an actual problem with my alternate proposal.
Which brings us back to the point: My alternate proposal is less disruptive and more backward-compatible. Is there some actual problem with it, or some other reason to prefer your original version?
Why not use RuntimeError as that of PEP479?
Three reasons:
1. There are thousands of lines of working code that handle AttributeError; forcing them to change to handle RuntimeError would be gratuitous backward incompatibility.
2. RuntimeError suggests that the code did something unexpected wrong, rather than a specific thing, possibly intentionally, that you may want to trap and handle. In PEP 479, this makes sense--it was decided that generators should not raise StopIteration instead of returning, and therefore it is a serious error to do so, not just a normal exception. But raising AttributeError from a property or other descriptor is a perfectly reasonable thing to do. It comes naturally from build-time proxies. It's recommended in the descriptor HOWTO. And so on. It makes perfect sense--and there's no other way to accomplish what it does, either. (How would you implement a write-only property that makes "print(spam.eggs)" raise an AttributeError except by raising it in the property function?)
3. In the vast majority of uses of properties and other descriptors, there is no __getattr__, and AttributeError is exactly what people want there. The only problem to be solved here is the rare but unexpected and annoying interaction of such dynamic AttributeErrors and __getattr__. Preventing descriptors from raising AttributeError even in the common cases to block that rare case is like removing a smudge from your table by disintegrating the table. Yes, the smudge is gone, but you haven't come out ahead.
And there's no need to write custom property-like descriptor factory when you want to trigger __getattr__, although it may be only a few lines of code.
Your original suggestion would make it _impossible_ for a descriptor to trigger __getattr__. And so would your suggestion here of converting AttributeError to RuntimeError. That doesn't remove the need; it leaves the need but makes it impossible to satisfy.

On Sun, Nov 29, 2015 at 11:31:14AM -0800, Andrew Barnert via Python-ideas wrote:
(How would you implement a write-only property that makes "print(spam.eggs)" raise an AttributeError except by raising it in the property function?)
eggs = property(None, setter, None, "") makes eggs a write-only property. print(spam.eggs) gives AttributeError: unreadable attribute -- Steve

On Nov 30, 2015, at 02:32, Steven D'Aprano <steve@pearwood.info> wrote:
On Sun, Nov 29, 2015 at 11:31:14AM -0800, Andrew Barnert via Python-ideas wrote:
(How would you implement a write-only property that makes "print(spam.eggs)" raise an AttributeError except by raising it in the property function?)
eggs = property(None, setter, None, "")
makes eggs a write-only property. print(spam.eggs) gives
AttributeError: unreadable attribute
And how do you think property implements that? The Descriptor HOWTO shows a pure-Python equivalent to property, which shows exactly how it works: by raising it in the property __get__ function, of course. Also notice that it looks exactly the same as if you'd provided an fget argument to property and that function raised (except that way you can control the text of the error). I think this is a good design. And I think that, except for often-unwanted (but usually-irrelevant) interaction with __getattr__, it's exactly what users would want and expect. But even if that weren't true, it's what Python had done for decades. Which implies that if we are going to gratuitously break it, we need to make it relatively cheap for people whose code depends on the existing behavior to restore it. def __get__(self, obj, objtype=None): if obj is None: return self if self.fget is None: raise AttributeError("unreadable attribute") return self.fget(obj)

On Mon, Nov 30, 2015 at 08:07:14AM -0800, Andrew Barnert wrote:
On Nov 30, 2015, at 02:32, Steven D'Aprano <steve@pearwood.info> wrote:
On Sun, Nov 29, 2015 at 11:31:14AM -0800, Andrew Barnert via Python-ideas wrote:
(How would you implement a write-only property that makes "print(spam.eggs)" raise an AttributeError except by raising it in the property function?)
eggs = property(None, setter, None, "")
makes eggs a write-only property. print(spam.eggs) gives
AttributeError: unreadable attribute
And how do you think property implements that?
Sorry, I misunderstood your (rhetorical?) question -- I read it as implying one should explicitly write a getter which raised AttributeError. -- Steve
participants (4)
-
Andrew Barnert
-
Chris Angelico
-
Steven D'Aprano
-
王珺