Code: class A: @property def text(self): return self.foo Behavior: >>> hasattr(A(), 'text') False Actually, this is absolutely correct. "A().y" does not give a result -- in fact, it raises AttributeError. Behavior wise, this is exactly what it means for an attribute to not exist. The problem is that this may disguise other issues in one's code. Suppose one tries to do "duck-type checking", where a function might get either an object of type A, or an object of type B. It checks if the object has a certain attribute that objects of type A have, and treats it as one or the other as a result. This is ugly, but works, and even works if someone writes a new type that emulates the API of A or B. Real Problem: class B(object): def __init__(self, x): if hasattr(x, 'text'): x = x.text self.x = x Because of an error inside the text property, B has classified x as the wrong type, and this will cause errors later in the execution of the program that might be hard to diagnose. That is what happens when mistakes are made. Worse is that the error is silenced, in a manner similar to how hasattr used to silence things like ValueError. I would not ask that the semantics of hasattr() be changed. hasattr() has a specific meaning, and I don't know what happens if it is changed again. What I would ask is that enough information be added to AttributeError that I can figure out things for myself. Specifically an attribute that stores the object for which attribute access failed, and the attribute (string) for which attribute access failed. This information is known when AttributeError is instantiated, but it's used to generate a string description and then thrown out. I would like to be able to write this function: def hasattr_lite(obj, attr): try: getattr(obj, attr) except AttributeError as e: if e.object is obj and e.attribute is attr: return False raise return True This would let me do this "duck type checking", but also would reduce the number of errors hidden in the process. So I would like it if AttributeError gained those attributes, or some equivalent functionality. Does that sound reasonable? -- Devin
On 04/26/2013 09:44 PM, Devin Jeanpierre wrote:
Code: class A: @property def text(self): return self.foo
Behavior: >>> hasattr(A(), 'text') False
Actually, this is absolutely correct. "A().y" does not give a result -- in fact, it raises AttributeError. Behavior wise, this is exactly what it means for an attribute to not exist.
I think you "A().foo" and not "A().y" above.
The problem is that this may disguise other issues in one's code.
Like bugs? ;)
I would like to be able to write this function:
def hasattr_lite(obj, attr): try: getattr(obj, attr) except AttributeError as e: if e.object is obj and e.attribute is attr: return False raise return True
Does that sound reasonable?
While it's always nice to have extra info in exceptions, why are you coding against bugs? If this is your own code you should have unit tests to catch such things. If this is someone else's code... well, it's their bug. -- ~Ethan~
On Sat, Apr 27, 2013 at 1:49 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
I think you "A().foo" and not "A().y" above.
Oops. Sorry.
The problem is that this may disguise other issues in one's code.
Like bugs? ;)
Yes.
While it's always nice to have extra info in exceptions, why are you coding against bugs?
If this is your own code you should have unit tests to catch such things.
If this is someone else's code... well, it's their bug.
Discovering that there is a bug is one thing, discovering why is another. The problems that result from silently doing the wrong thing can be significantly harder to diagnose than an exception traceback is, and this hasattr_lite would let me get an exception in cases where I might otherwise have silently wrong behavior. I mean, yes, this error was in fact found in my unit test suite. I spent a lot of time tracking it down (perhaps too much time, because I was expecting something else [oops, too many changes in one changeset :X]), and eventually narrowed it down to a try/except AttributeError I had. Even then, trying to filter out the legitimate AttributeErrors and the illegitimate one from the same test case was annoying. I ended up breaking at the except block and individually examining the exceptions, both legitimate and not. This solved everything. hasattr_lite (if it worked) would've reported the problem as an AttributeError, with exactly the typo I had made in an attribute name inside a property. Seconds to figure out and fix. So what I mean is, it isn't necessary, but I would find it helpful and convenient. -- Devin
On 04/26/2013 11:59 PM, Devin Jeanpierre wrote:
On Sat, Apr 27, 2013 at 1:49 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
Discovering that there is a bug is one thing, discovering why is another. The problems that result from silently doing the wrong thing can be significantly harder to diagnose than an exception traceback is, and this hasattr_lite would let me get an exception in cases where I might otherwise have silently wrong behavior.
I mean, yes, this error was in fact found in my unit test suite. I spent a lot of time tracking it down (perhaps too much time, because I was expecting something else [oops, too many changes in one changeset :X]), and eventually narrowed it down to a try/except AttributeError I had. Even then, trying to filter out the legitimate AttributeErrors and the illegitimate one from the same test case was annoying. I ended up breaking at the except block and individually examining the exceptions, both legitimate and not. This solved everything.
hasattr_lite (if it worked) would've reported the problem as an AttributeError, with exactly the typo I had made in an attribute name inside a property. Seconds to figure out and fix.
So what I mean is, it isn't necessary, but I would find it helpful and convenient.
I absolutely agree, and would like to see the available info on other exceptions (KeyError, IndexError, etc.) as well. I suspect it would take some serious effort to upgrade all the exceptions from all the places they can be raised from, though. -- ~Ethan~
On 4/27/2013 3:39 AM, Ethan Furman wrote:
On 04/26/2013 11:59 PM, Devin Jeanpierre wrote:
On Sat, Apr 27, 2013 at 1:49 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
Discovering that there is a bug is one thing, discovering why is another. The problems that result from silently doing the wrong thing can be significantly harder to diagnose than an exception traceback is, and this hasattr_lite would let me get an exception in cases where I might otherwise have silently wrong behavior.
I mean, yes, this error was in fact found in my unit test suite. I spent a lot of time tracking it down (perhaps too much time, because I was expecting something else [oops, too many changes in one changeset :X]), and eventually narrowed it down to a try/except AttributeError I had. Even then, trying to filter out the legitimate AttributeErrors and the illegitimate one from the same test case was annoying. I ended up breaking at the except block and individually examining the exceptions, both legitimate and not. This solved everything.
hasattr_lite (if it worked) would've reported the problem as an AttributeError, with exactly the typo I had made in an attribute name inside a property. Seconds to figure out and fix.
So what I mean is, it isn't necessary, but I would find it helpful and convenient.
I absolutely agree, and would like to see the available info on other exceptions (KeyError, IndexError, etc.) as well. I suspect it would take some serious effort to upgrade all the exceptions from all the places they can be raised from, though.
I also agree that more information can only be a good thing. Unless someone can show why it could be harmful (cycles caused by the exception keeping a reference to the offending object??), the only downside I can see is the work needed to change the throw points. --Ned.
-- ~Ethan~ _______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
Am 27.04.2013 13:53, schrieb Ned Batchelder:
hasattr_lite (if it worked) would've reported the problem as an AttributeError, with exactly the typo I had made in an attribute name inside a property. Seconds to figure out and fix.
So what I mean is, it isn't necessary, but I would find it helpful and convenient.
I absolutely agree, and would like to see the available info on other exceptions (KeyError, IndexError, etc.) as well. I suspect it would take some serious effort to upgrade all the exceptions from all the places they can be raised from, though.
I also agree that more information can only be a good thing. Unless someone can show why it could be harmful (cycles caused by the exception keeping a reference to the offending object??), the only downside I can see is the work needed to change the throw points.
It is kind of harmful to duck-typing and, to a lesser degree, inheritance: so far Python has never guaranteed anything about exception arguments. If the exception attributes become part of the interface of standard types, everyone implementing a replacement will have to conform (and there are lots and lots of such replacements out there). This change should be treated akin to adding a new method to dictionaries, for example. That said, personally I would be in favour of such a change, because the advantage for unhandled exceptions alone is significant. Georg
On Sat, Apr 27, 2013 at 8:04 AM, Georg Brandl <g.brandl@gmx.net> wrote:
Am 27.04.2013 13:53, schrieb Ned Batchelder:
hasattr_lite (if it worked) would've reported the problem as an AttributeError, with exactly the typo I had made in an attribute name inside a property. Seconds to figure out and fix.
So what I mean is, it isn't necessary, but I would find it helpful and convenient.
I absolutely agree, and would like to see the available info on other exceptions (KeyError, IndexError, etc.) as well. I suspect it would take some serious effort to upgrade all the exceptions from all the places they can be raised from, though.
I also agree that more information can only be a good thing. Unless someone can show why it could be harmful (cycles caused by the exception keeping a reference to the offending object??), the only downside I can see is the work needed to change the throw points.
It is kind of harmful to duck-typing and, to a lesser degree, inheritance: so far Python has never guaranteed anything about exception arguments. If the exception attributes become part of the interface of standard types, everyone implementing a replacement will have to conform (and there are lots and lots of such replacements out there). This change should be treated akin to adding a new method to dictionaries, for example.
That said, personally I would be in favour of such a change, because the advantage for unhandled exceptions alone is significant.
I can speak from two bits of experience on this. First is http://python.org/dev/peps/pep-0352/ where I tried to make BaseException only accept a single argument and add a 'message' attribute. I actually gave up on the single argument version because too much called relied on the *args acceptance of BaseException. I gave up on 'message' because too many people subclassed exceptions and used 'message' as an attribute. But I also added 'name' and 'path' to ImportError in Python 3.3 successfully. I made them keyword-only arguments to the module's constructor to avoid API problems and not enough people construct ImportError instances directly for me to have heard complaints about the attribute names. My point is that Georg is right: tweaking the base exceptions by even adding an attribute can be touchy, but worth it.
I don't know if it ties into this proposal or not, but I've had a pair of issues with attribute resolution in the past and more specifically with __getattr__: 1. __getattr__ is not implemented on object, thus implementing __getattr__ in an inheritance hierarchy (where an other object in an MRO may also have implemented __getattr__) requires boilerplate along the lines of: sup = getattr(super(Cls, self), '__getattr__', None) if sup is not None: return sup(key) 2. __getattr__ *must raise* to signify a missing attribute as None will simply be returned. The issue here is twofold: * the exception message will often be nothing like the default one * the stack will be all wrong, as it will show "within" the __getattr__ call, making it harder to discriminate between an expected attribute error and something unexpectedly blowing up within __getattr__ I was wondering if it wouldn't be possible to add __getattr__ to object, which would return NotImplemented. And NotImplemented would be interpreted by the attribute resolution process as "raise the normal AttributeError" as if there had not been a __getattr__. This way, attribute errors from __getattr__ not matching the provided name would look much more natural. I also believe it is backwards compatible: current __getattr__ implementations which just raise & don't delegate to a super() will behave exactly the same way, with the same issues.
Masklinn wrote:
1. __getattr__ is not implemented on object, thus implementing __getattr__ in an inheritance hierarchy (where an other object in an MRO may also have implemented __getattr__) requires boilerplate
The same applies to any other special method that object doesn't implement. What is it about __getattr__ that makes it deserving of this treatment?
I was wondering if it wouldn't be possible to add __getattr__ to object, which would return NotImplemented. And NotImplemented would be interpreted by the attribute resolution process as "raise the normal AttributeError"
That would make it impossible for NotImplemented to be the value of any attribute of anything. -- Greg
On Apr 26, 2013 10:45 PM, "Devin Jeanpierre" <jeanpierreda@gmail.com> wrote:
Code: class A: @property def text(self): return self.foo
Behavior: >>> hasattr(A(), 'text') False
Actually, this is absolutely correct. "A().y" does not give a result -- in fact, it raises AttributeError. Behavior wise, this is exactly what it means for an attribute to not exist.
What about using inspect.getattr_static()? def hasattr_static(obj, name): try: getattr_static(obj, name) except AttributeError: return False else: return True -eric
On 04/27/2013 12:44 AM, Devin Jeanpierre wrote:
Code: class A: @property def text(self): return self.foo
Behavior: >>> hasattr(A(), 'text') False
Real Problem: class B(object): def __init__(self, x): if hasattr(x, 'text'): x = x.text self.x = x
What this is telling me: class A: def __hasattr__(self, name): if name == 'text': return True return super(A, self).__hasattr__(name) This may or may not be something that @property, or some other decorator, ought to take care of.
participants (9)
-
Brett Cannon
-
Devin Jeanpierre
-
Eric Snow
-
Ethan Furman
-
Georg Brandl
-
Greg Ewing
-
Masklinn
-
Ned Batchelder
-
Random832