473,396 Members | 1,833 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

@property decorator doesn't raise exceptions

Hi,

I've encountered a problem which is making debugging less obvious than
it should be. The @property decorator doesn't always raise exceptions.
It seems like it is bound to the class but ignored when called. I can
see the attribute using dir(self.__class__) on an instance, but when
called, python enters __getattr__. If I correct the bug, the attribute
calls work as expected and do not call __getattr__.

I can't seem to make a simple repro. Can anyone offer any clues as to
what might cause this so I can try to prove it?
Cheers,

- Rafe
Oct 24 '08 #1
14 4301
Rafe wrote:
Hi,

I've encountered a problem which is making debugging less obvious than
it should be. The @property decorator doesn't always raise exceptions.
It seems like it is bound to the class but ignored when called. I can
see the attribute using dir(self.__class__) on an instance, but when
called, python enters __getattr__. If I correct the bug, the attribute
calls work as expected and do not call __getattr__.

I can't seem to make a simple repro. Can anyone offer any clues as to
what might cause this so I can try to prove it?
You must subclass from "object" to get a new style class. properties
don't work correctly on old style classes.

Christian

Oct 24 '08 #2
On Oct 24, 2:21 am, Christian Heimes <li...@cheimes.dewrote:
Rafewrote:
Hi,
I've encountered a problem which is making debugging less obvious than
it should be. The @property decorator doesn't always raise exceptions.
It seems like it is bound to the class but ignored when called. I can
see the attribute using dir(self.__class__) on an instance, but when
called, python enters __getattr__. If I correct the bug, the attribute
calls work as expected and do not call __getattr__.
I can't seem to make a simple repro. Can anyone offer any clues as to
what might cause this so I can try to prove it?

You must subclass from "object" to get a new style class. properties
don't work correctly on old style classes.

Christian
All classes are a sub-class of object. Any other ideas?

- Rafe
Oct 24 '08 #3
Rafe wrote:
On Oct 24, 2:21 am, Christian Heimes <li...@cheimes.dewrote:
>Rafewrote:
Hi,
I've encountered a problem which is making debugging less obvious than
it should be. The @property decorator doesn't always raise exceptions.
It seems like it is bound to the class but ignored when called. I can
see the attribute using dir(self.__class__) on an instance, but when
called, python enters __getattr__. If I correct the bug, the attribute
calls work as expected and do not call __getattr__.
I can't seem to make a simple repro. Can anyone offer any clues as to
what might cause this so I can try to prove it?

You must subclass from "object" to get a new style class. properties
don't work correctly on old style classes.

Christian

All classes are a sub-class of object. Any other ideas?
Hard to tell when you don't give any code.
>>class A(object):
.... @property
.... def attribute(self):
.... raise AttributeError
.... def __getattr__(self, name):
.... return "nobody expects the spanish inquisition"
....
>>A().attribute
'nobody expects the spanish inquisition'

Do you mean something like this? I don't think the __getattr__() call can be
avoided here.

Peter
Oct 24 '08 #4
On Fri, 24 Oct 2008 09:34:36 -0700, Rafe wrote:
>You must subclass from "object" to get a new style class. properties
don't work correctly on old style classes.

Christian

All classes are a sub-class of object. Any other ideas?
Only in Python 3. If you are relying on that to be true in Python 2.x,
you're going to disappointed:
>>class A():
.... pass
....
>>issubclass(A, object)
False
There are a lot of classic classes in the standard library. If you
inherit from them, your class will also be a classic class and properties
will fail to work correctly.
Earlier, you wrote:

"I've encountered a problem which is making debugging less obvious than
it should be. The @property decorator doesn't always raise exceptions."

Are you expecting it to raise an exception when the class is defined, or
when the property is called? e.g.

class Fail(object):
@property
"this should raise an exception"

Works for me -- I get a syntax error, as expected.
"It seems like it is bound to the class but ignored when called. I can
see the attribute using dir(self.__class__) on an instance, but when
called, python enters __getattr__. If I correct the bug, the attribute
calls work as expected and do not call __getattr__."
Perhaps you can give us a minimal example showing the code with the bug,
then how you corrected the bug? That might give as a hint as to what is
going on. I know that's hard, when you can't reproduce the problem, but
it's just as hard for us to solve your problem without more information.

--
Steven
Oct 24 '08 #5
On Fri, 24 Oct 2008 01:47:10 -0700, Rafe wrote:
Hi,

I've encountered a problem which is making debugging less obvious than
it should be. The @property decorator doesn't always raise exceptions.
I don't think that's the problem. I think properties do correctly raise
all exceptions that occur inside them, at least built-in exceptions.

I wrote a test that raises exceptions inside properties and they (almost)
all are raised normally.

Whatever your problem is, it isn't that properties don't raise exceptions.

For those who are interested, the test program I used follows.
# test module

def get_exceptions():
import __builtin__
list_of_exceptions = []
for key in dir(__builtin__):
# do not use __builtins__ -- note the 's'.
obj = getattr(__builtin__, key)
if not isinstance(obj, type):
continue
if issubclass(obj, Exception):
list_of_exceptions.append(obj)
return list_of_exceptions

def builder(list_of_exceptions):
class PropertyTester(object):
"""Do properties raise exceptions? Let's find out."""
for exception in list_of_exceptions:
name = exception.__name__
@property
def func(self, exception=exception):
# Make sure we bind a local copy to exception.
raise exception
setattr(PropertyTester, name, func)
return PropertyTester()

def testRaises(obj, exception_type, verbose):
"""Test that appropriate exception is raised when accessing an
attribute."""
name = exception_type.__name__
try:
getattr(obj, name)
except exception_type:
if verbose:
print "Passed: expected exception %s raised correctly" \
% exception_type
except Exception, e:
print "Failed: expected %s but got %r" % (exception_type, e)
else:
print "Failed: expected %s but got no exception at all" \
% exception_type

if __name__ == '__main__':
import sys
if sys.argv[1:] == ['verbose']:
verbose = True
else:
verbose = False
exceptions = get_exceptions()
tester = builder(exceptions)
for exception in exceptions:
testRaises(tester, exception, verbose)

--
Steven
Oct 25 '08 #6
On Oct 24, 9:58*am, Peter Otten <__pete...@web.dewrote:
Rafe wrote:
On Oct 24, 2:21 am, Christian Heimes <li...@cheimes.dewrote:
Rafewrote:
Hi,
I've encountered a problem which is making debugging less obvious than
it should be. The @property decorator doesn't always raise exceptions.
It seems like it is bound to the class but ignored when called. I can
see the attribute using dir(self.__class__) on an instance, but when
called, python enters __getattr__. If I correct the bug, the attribute
calls work as expected and do not call __getattr__.
I can't seem to make a simple repro. Can anyone offer any clues as to
what might cause this so I can try to prove it?
You must subclass from "object" to get a new style class. properties
don't work correctly on old style classes.
Christian
All classes are a sub-class of object. Any other ideas?

Hard to tell when you don't give any code.
>class A(object):

... * * @property
... * * def attribute(self):
... * * * * * * raise AttributeError
... * * def __getattr__(self, name):
... * * * * * * return "nobody expects the spanish inquisition"
...>>A().attribute

'nobody expects the spanish inquisition'

Do you mean something like this? I don't think the __getattr__() call canbe
avoided here.

Peter
You nailed it Peter! I thought __getattr__ was a symptom, not the
cause of the misleading errors. Here is the repro (pretty much
regurgitated):

The expected behavior...
>>class A(object):
.... @property
.... def attribute(self):
.... raise AttributeError("Correct Error.")
>>A().attribute
Traceback (most recent call last):
File "<console>", line 0, in <module>
File "<console>", line 0, in attribute
AttributeError: Correct Error.
The unexpected and misleading exception...
>>class A(object):
.... @property
.... def attribute(self):
.... raise AttributeError("Correct Error.")
.... def __getattr__(self, name):
.... cls_name = self.__class__.__name__
.... msg = "%s has no attribute '%s'." % (cls_name, name)
.... raise AttributeError(msg)
Traceback (most recent call last):
File "<console>", line 0, in <module>
File "<console>", line 0, in __getattr__
AttributeError: A has no attribute 'attribute'.
The docs state:
"Called when an attribute lookup has not found the attribute in the
usual places (i.e. it is not an instance attribute nor is it found in
the class tree for self). name is the attribute name. This method
should return the (computed) attribute value or raise an
AttributeError exception."

Can anyone explain why this is happening? I can hack a work-around,
but even then I could use some tips on how to raise the 'real'
exception so debugging isn't guesswork.
Cheers,

- Rafe
Oct 25 '08 #7
On Oct 24, 9:58*am, Peter Otten <__pete...@web.dewrote:
Rafe wrote:
On Oct 24, 2:21 am, Christian Heimes <li...@cheimes.dewrote:
Rafewrote:
Hi,
I've encountered a problem which is making debugging less obvious than
it should be. The @property decorator doesn't always raise exceptions.
It seems like it is bound to the class but ignored when called. I can
see the attribute using dir(self.__class__) on an instance, but when
called, python enters __getattr__. If I correct the bug, the attribute
calls work as expected and do not call __getattr__.
I can't seem to make a simple repro. Can anyone offer any clues as to
what might cause this so I can try to prove it?
You must subclass from "object" to get a new style class. properties
don't work correctly on old style classes.
Christian
All classes are a sub-class of object. Any other ideas?

Hard to tell when you don't give any code.
>class A(object):

... * * @property
... * * def attribute(self):
... * * * * * * raise AttributeError
... * * def __getattr__(self, name):
... * * * * * * return "nobody expects the spanish inquisition"
...>>A().attribute

'nobody expects the spanish inquisition'

Do you mean something like this? I don't think the __getattr__() call canbe
avoided here.

Peter

Peter nailed it, thanks! I thought __getattr__ was a symptom, not a
cause of the misleading exceptions. Here is a complete repro:
The expected behavior...
>>class A(object):
.... @property
.... def attribute(self):
.... raise AttributeError("Correct Error.")
>>A().attribute
Traceback (most recent call last):
File "<console>", line 0, in <module>
File "<console>", line 0, in attribute
AttributeError: Correct Error.
The misleading/unexpected behavior...
>>class A(object):
.... @property
.... def attribute(self):
.... raise AttributeError("Correct Error.")
.... def __getattr__(self, name):
.... cls_name = self.__class__.__name__
.... msg = "%s has no attribute '%s'." % (cls_name, name)
.... raise AttributeError(msg)
>>A().attribute
Traceback (most recent call last):
File "<console>", line 0, in <module>
File "<console>", line 0, in __getattr__
AttributeError: A has no attribute 'attribute'.
Removing @property works as expected...
>>class A(object):
.... def attribute(self):
.... raise AttributeError("Correct Error.")
.... def __getattr__(self, name):
.... cls_name = self.__class__.__name__
.... msg = "%s has no attribute '%s'." % (cls_name, name)
.... raise AttributeError(msg)
>>A().attribute() # Note the '()'
Traceback (most recent call last):
File "<console>", line 0, in <module>
File "<console>", line 0, in attribute
AttributeError: Correct Error.
The docs seem to suggest this is impossible:
"Called when an attribute lookup has not found the attribute in the
usual places (i.e. it is not an instance attribute nor is it found in
the class tree for self). name is the attribute name. This method
should return the (computed) attribute value or raise an
AttributeError exception."

Can anyone explain why this is happening? Is it a bug? I can write a
workaround to detect this by comparing the attribute name passed
__getattr__ with dir(self.__class__) = self.__dict__.keys(), but how
can I raise the expected exception?
Thanks,

- Rafe
Oct 25 '08 #8
Rafe wrote:
The docs seem to suggest this is impossible:
"Called when an attribute lookup has not found the attribute in the
usual places (i.e. it is not an instance attribute nor is it found in
the class tree for self).
Getting an AttributeError is the way that the interpreter
machinery tells that the attribute wasn't found. So when
your property raises an AttributeError, this is
indistinguishable from the case where the property wasn't
there at all.

To avoid this you would have to raise some exception
that doesn't derive from AttributeError.

--
Greg
Oct 26 '08 #9
On Oct 24, 1:47*am, Rafe <rafesa...@gmail.comwrote:
Hi,

I've encountered a problem which is making debugging less obvious than
it should be. The @property decorator doesn't always raise exceptions.
It seems like it is bound to the class but ignored when called. I can
see the attribute using dir(self.__class__) on an instance, but when
called, python enters __getattr__. If I correct the bug, the attribute
calls work as expected and do not call __getattr__.

I can't seem to make a simple repro. Can anyone offer any clues as to
what might cause this so I can try to prove it?

Cheers,

- Rafe

Peter Oten pointed me in the right direction. I tried to reply to his
post 2 times and in spite of GoogleGroups reporting the post was
successful, it never showed up. Here is the repro:

The expected behavior...
>>class A(object):
.... @property
.... def attribute(self):
.... raise AttributeError("Correct Error.")
>>A().attribute
Traceback (most recent call last):
File "<console>", line 0, in <module>
File "<console>", line 0, in attribute
AttributeError: Correct Error.
The misleading/unexpected behavior...
>>class A(object):
.... @property
.... def attribute(self):
.... raise AttributeError("Correct Error.")
.... def __getattr__(self, name):
.... cls_name = self.__class__.__name__
.... msg = "%s has no attribute '%s'." % (cls_name, name)
.... raise AttributeError(msg)
>>A().attribute
Traceback (most recent call last):
File "<console>", line 0, in <module>
File "<console>", line 0, in __getattr__
AttributeError: A has no attribute 'attribute'.
Removing @property works as expected...
>>class A(object):
.... def attribute(self):
.... raise AttributeError("Correct Error.")
.... def __getattr__(self, name):
.... cls_name = self.__class__.__name__
.... msg = "%s has no attribute '%s'." % (cls_name, name)
.... raise AttributeError(msg)
>>A().attribute() # Note the '()'
Traceback (most recent call last):
File "<console>", line 0, in <module>
File "<console>", line 0, in attribute
AttributeError: Correct Error.
I never suspected __getattr__ was the cause and not just a symptom.
The docs seem to indicate __gettattr__ should never be called when the
attribute exisits in the class:
"Called when an attribute lookup has not found the attribute in the
usual places (i.e. it is not an instance attribute nor is it found in
the class tree for self). name is the attribute name. This method
should return the (computed) attribute value or raise an
AttributeError exception."

Is this a bug? Any idea why this happens? I can write a hack in to
__getattr__ in my class which will detect this, but I'm not sure how
to raise the expected exception.
Cheers,

- Rafe
Oct 26 '08 #10
Rafe <ra*******@gmail.comwrote:
Peter Oten pointed me in the right direction. I tried to reply to his
post 2 times and in spite of GoogleGroups reporting the post was
successful, it never showed up.
This is the third variant on your message that has shown up in the
newsgroup.

Please be aware that messages take time to propogate through usenet: don't
repost just because Google groups hasn't yet got around to displaying your
message. If it says the post was successful then the post was successful.
Just be patient a bit longer for it to become visible to you.
Oct 26 '08 #11
Rafe wrote:
Can anyone explain why this is happening?
When an attribute error is raised that is an indication that the requested
attribute doesn't exist, and __getattr__() must be called as a fallback.
I can hack a work-around,
but even then I could use some tips on how to raise the 'real'
exception so debugging isn't guesswork.
Look at the problem again, maybe you can find a solution without
__getattr__() and use only properties.

Otherwise you have to wrap your getter with something like

try:
...
except AttributeError:
raise BuggyProperty, None, original_traceback
If you put that functionality into a decorator you get:

import sys

class BuggyProperty(Exception):
pass

def safe_getter(get):
def safe_get(self):
try:
return get(self)
except AttributeError:
t, e, tb = sys.exc_info()
raise BuggyProperty("AttributeError in getter %s(); "
"giving original traceback"
% get.__name__), None, tb
return property(safe_get)

class A(object):
@safe_getter
def attr(self):
return self.m(3)

def m(self, n):
if n 0:
return self.m(n-1)
raise AttributeError("it's a bug")

def __getattr__(self, name):
return "<%s>" % name

A().attr

Peter
Oct 27 '08 #12
OT... Sorry about he spam.
Thanks for taking the time to post this Duncan.

I had the same thought. I have posted to this list before but never
experienced anything like this wait. I figured it was possible that I
hit "Reply to Author" the first time so I sent it again. I waited
about 8 hours before sending the third time (and I posted to
GoogleGroups support as well). Even then I didn't see my original
post. I'm surprised it took so long to update. Next time I'll just
make sure the post was made successfully and wait...as long as it
takes.
Cheers,

- Rafe
On Oct 26, 4:23*pm, Duncan Booth <duncan.bo...@invalid.invalidwrote:
Rafe<rafesa...@gmail.comwrote:
Peter Oten pointed me in the right direction. I tried to reply to his
post 2 times and in spite of GoogleGroups reporting the post was
successful, it never showed up.

This is the third variant on your message that has shown up in the
newsgroup.

Please be aware that messages take time to propogate through usenet: don't
repost just because Google groups hasn't yet got around to displaying your
message. If it says the post was successful then the post was successful.
Just be patient a bit longer for it to become visible to you.
Oct 29 '08 #13
On Oct 27, 2:47*pm, Peter Otten <__pete...@web.dewrote:
Rafewrote:
Can anyone explain why this is happening?

When an attribute error is raised that is an indication that the requested
attribute doesn't exist, and __getattr__() must be called as a fallback.
I can hack a work-around,
but even then I could use some tips on how to raise the 'real'
exception so debugging isn't guesswork.

Look at the problem again, maybe you can find a solution without
__getattr__() and use only properties.

Otherwise you have to wrap your getter with something like

try:
* * ...
except AttributeError:
* * raise BuggyProperty, None, original_traceback

If you put that functionality into a decorator you get:

import sys

class BuggyProperty(Exception):
* * pass

def safe_getter(get):
* * def safe_get(self):
* * * * try:
* * * * * * return get(self)
* * * * except AttributeError:
* * * * * * t, e, tb = sys.exc_info()
* * * * * * raise BuggyProperty("AttributeError in getter %s(); "
* * * * * * * * * * * * * * "giving original traceback"
* * * * * * * * * * * * * * % get.__name__), None, tb
* * return property(safe_get)

class A(object):
* * @safe_getter
* * def attr(self):
* * * * return self.m(3)

* * def m(self, n):
* * * * if n 0:
* * * * * * return self.m(n-1)
* * * * raise AttributeError("it's a bug")

* * def __getattr__(self, name):
* * * * return "<%s>" % name

A().attr

Peter

Thanks for the idea Peter. What confuses me is why this only happens
to @Property (and I assume other decorator related bindings?). Does it
have something to do with the way the class gets built? 'Normal'
attributes will raise AttributeErrors as expected, without triggering
__getattr__(). Considering this is a built-in decorator, it would be
nice if this behavior was fixed if possible.

Unfortunately, I need __getattr__() because my class is a wrapper (it
is delegating calls to another object when attributes aren't found in
the class). As a hack, I am testing the passed attr name against the
instance, class and super-class attributes. If there is a match, I
assume it is an error and raise an exception which points the
developer in the right direction. It isn't ideal, but it helps.

You're code is better, as it displays the 'real' traceback, but I need
to know more about the effects of using an exception which is not an
AttrbiuteError. Which brings me to my next question...

In case it isn't obvious, I'm fairly new to Python (and this level of
programming in general). I've been wondering about the optimal use of
custom exceptions. Up until now, I've been sticking to the built-in
exceptions, which seem to work in 90% of situations. Are you aware of
any resources which talk about this aspect of programming (more about
theory than code)?
Thanks again,

- Rafe
Oct 29 '08 #14
Rafe wrote:
Thanks for the idea Peter. What confuses me is why this only happens
to @Property (and I assume other decorator related bindings?). Does it
have something to do with the way the class gets built? 'Normal'
attributes will raise AttributeErrors as expected, without triggering
__getattr__(). Considering this is a built-in decorator, it would be
nice if this behavior was fixed if possible.
Normal attributes either exist, and then they don't raise an AttributeError,
or they don't exist, and then __getattr__() *is* triggered. The problem has
nothing to do with decorators. It is just that properties invoke custom
code, and Python currently has no way of finding out whether an
AttributeError was raised accidentally by a buggy getter or whether it is
meant to signal that the attribute wasn't found in the class hierarchy and
now should be calculated by __getattr__().

I guess (without looking into the C source) that it could be changed to meet
your intuition but that it would complicate the implementation.

Peter

Oct 29 '08 #15

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

6
by: Andrew | last post by:
Hi, To satisfy my curiosity I was wondering if anyone knew if this behaviour was intentional? Is there a specific reason why exceptions are not allowed to be new style classes? Python 2.3...
1
by: Dominic | last post by:
Just in case someone is interested. I came up with this solution since my previous posting. Maybe you've got some other ideas or critcism. I'll listen ;-) Ciao, Dominic P.S. Python is...
2
by: Jeffrey Froman | last post by:
Consider the following class: class Node(object): def __init__(self, name, parent=None): self.name = name self.parent = parent def _ancestors(self, ants=None): if ants is None: ants =
2
by: Torsten Bronger | last post by:
Hallöchen! I write a module, and so I must raise exceptions at several points. The PEP 8 says that a module should define their own exceptions base class, derived from "Exception", and...
2
by: Sam Miller | last post by:
Normally the debugger (visual studio .net environment) is good at pointing out the line of code that caused an exception.... except when that code is executed as part of a timer handler. In the...
5
by: Thomas Lotze | last post by:
Hi, I wonder how to solve the following problem the most pythonic way: Suppose you have a function f which, as part of its protocol, raises some standard exception E under certain,...
4
by: Alex G | last post by:
Does anyone know how I would go about conditionally raising an exception in a decorator (or any returned function for that matter)? For example: def decorator(arg): def raise_exception(fn):...
0
by: Ben Finney | last post by:
Daniel <millerdev@gmail.comwrites: The fact that the 'property' function works as a decorator (in, as you point out, some cases only) is a happy accident. The 'property' function isn't...
8
by: Bryan | last post by:
I want my business objects to be able to do this: class Person(base): def __init__(self): self.name = None @base.validator def validate_name(self): if not self.name: return
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.