Inconsistent exception for read-only properties?
I've noticed an apparent inconsistency in the exception thrown for read-only properties for C extension types vs. Python new-style classes. I'm wondering if this is intentional, a bug, a bug worth fixing, or whether I'm just missing something. class other(object): def __init__(self, value): self._value = value def _get_value(self): return self._value value = property(_get_value) With this class, if you attempt "other(1).value = 7" you will get an AttributeError. However, if you define something similar in C using a tp_getset, where the structure has NULL for the setter, you will get a TypeError (code available upon request). At best, this is inconsistent. What's the "right" exception to raise? I think the documentation I've seen (e.g. Raymond's How To for Descriptors) describes AttributeError as the thing to raise when trying to set read-only properties. Thoughts? Should this be fixed (in 2.4?). -Barry
On Thu, 2005-04-14 at 23:46, Barry Warsaw wrote:
I've noticed an apparent inconsistency in the exception thrown for read-only properties for C extension types vs. Python new-style classes.
I haven't seen any follow ups on this, so I've gone ahead and posted a patch, assigning it to Raymond: http://sourceforge.net/tracker/index.php?func=detail&aid=1184449&group_id=5470&atid=105470 I would have attached a patch to that issue but SF it being finicky. -Barry
On Sat, Apr 16, 2005 at 07:24:27PM -0400, Barry Warsaw wrote:
On Thu, 2005-04-14 at 23:46, Barry Warsaw wrote:
I've noticed an apparent inconsistency in the exception thrown for read-only properties for C extension types vs. Python new-style classes.
I haven't seen any follow ups on this, so I've gone ahead and posted a patch, assigning it to Raymond:
http://sourceforge.net/tracker/index.php?func=detail&aid=1184449&group_id=5470&atid=105470
In 2.4 & 2.3 does it make sense to raise an exception that multiply inherits from both TypeError and AttributeError? If anyone currently does catch the error raising only AttributeError will break their code. 2.5 should just raise an AttributeError, of course. If that's acceptable I'll gladly submit a similar patch for mmap.get_byte() PyErr_SetString (PyExc_ValueError, "read byte out of range"); has always irked me (the same thing with mmap[i] is an IndexError). I hadn't thought of a clean way to fix it, but MI on the error might work. -jackdied
On Sun, 2005-04-17 at 11:53, Jack Diederich wrote:
In 2.4 & 2.3 does it make sense to raise an exception that multiply inherits from both TypeError and AttributeError? If anyone currently does catch the error raising only AttributeError will break their code. 2.5 should just raise an AttributeError, of course.
Without introducing a new exception class (which I think is out of the question for anything but 2.5), the only common base is StandardError, which seems too general for this exception. -Barry
On Sun, Apr 17, 2005, Barry Warsaw wrote:
On Sun, 2005-04-17 at 11:53, Jack Diederich wrote:
In 2.4 & 2.3 does it make sense to raise an exception that multiply inherits from both TypeError and AttributeError? If anyone currently does catch the error raising only AttributeError will break their code. 2.5 should just raise an AttributeError, of course.
Without introducing a new exception class (which I think is out of the question for anything but 2.5), the only common base is StandardError, which seems too general for this exception.
Why is changing an exception more acceptable than creating a new one? (I don't have a strong opinion either way, but I'd like some reasoning; Jack's approach at least doesn't break code.) Especially if the new exception isn't "public" (in the builtins with other exceptions). -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ "The joy of coding Python should be in seeing short, concise, readable classes that express a lot of action in a small amount of clear code -- not in reams of trivial code that bores the reader to death." --GvR
On Sun, 2005-04-17 at 12:25, Aahz wrote:
Why is changing an exception more acceptable than creating a new one? (I don't have a strong opinion either way, but I'd like some reasoning; Jack's approach at least doesn't break code.) Especially if the new exception isn't "public" (in the builtins with other exceptions).
Adding an exception that we have to live with forever (even if it's localized to this one module) seems like it would fall under the new feature rubric, whereas I think the choice of exception was just a bug. -Barry
On Sun, Apr 17, 2005 at 11:53:31AM -0400, Jack Diederich wrote:
On Sat, Apr 16, 2005 at 07:24:27PM -0400, Barry Warsaw wrote:
On Thu, 2005-04-14 at 23:46, Barry Warsaw wrote:
I've noticed an apparent inconsistency in the exception thrown for read-only properties for C extension types vs. Python new-style classes.
I haven't seen any follow ups on this, so I've gone ahead and posted a patch, assigning it to Raymond:
http://sourceforge.net/tracker/index.php?func=detail&aid=1184449&group_id=5470&atid=105470
In 2.4 & 2.3 does it make sense to raise an exception that multiply inherits from both TypeError and AttributeError? If anyone currently does catch the error raising only AttributeError will break their code. 2.5 should just raise an AttributeError, of course.
If that's acceptable I'll gladly submit a similar patch for mmap.get_byte() PyErr_SetString (PyExc_ValueError, "read byte out of range"); has always irked me (the same thing with mmap[i] is an IndexError). I hadn't thought of a clean way to fix it, but MI on the error might work.
I just did a quick grep for raised ValueErrors with "range" in the explanation string and didn't find any general consensus. I dunno what that means, if anything. wopr:~/src/python_head/dist/src# find ./ -name '*.c' | xargs grep ValueError | grep range | wc -l 13 wopr:~/src/python_head/dist/src# find ./ -name '*.c' | xargs grep IndexError | grep range | wc -l 31 (long versions below) -jackdied wopr:~/src/python_head/dist/src# find ./ -name '*.c' | xargs grep -n IndexError | grep range ./Modules/arraymodule.c:599: PyErr_SetString(PyExc_IndexError, "array index out of range"); ./Modules/arraymodule.c:997: PyErr_SetString(PyExc_IndexError, "pop index out of range"); ./Modules/mmapmodule.c:639: PyErr_SetString(PyExc_IndexError, "mmap index out of range"); ./Modules/mmapmodule.c:727: PyErr_SetString(PyExc_IndexError, "mmap index out of range"); ./Modules/_heapqmodule.c:19: PyErr_SetString(PyExc_IndexError, "index out of range"); ./Modules/_heapqmodule.c:58: PyErr_SetString(PyExc_IndexError, "index out of range"); ./Modules/_heapqmodule.c:136: PyErr_SetString(PyExc_IndexError, "index out of range"); ./Modules/_heapqmodule.c:173: PyErr_SetString(PyExc_IndexError, "index out of range"); ./Modules/_heapqmodule.c:310: PyErr_SetString(PyExc_IndexError, "index out of range"); ./Modules/_heapqmodule.c:349: PyErr_SetString(PyExc_IndexError, "index out of range"); ./Objects/bufferobject.c:403: PyErr_SetString(PyExc_IndexError, "buffer index out of range"); ./Objects/listobject.c:876: PyErr_SetString(PyExc_IndexError, "pop index out of range"); ./Objects/rangeobject.c:94: PyErr_SetString(PyExc_IndexError, ./Objects/stringobject.c:1055: PyErr_SetString(PyExc_IndexError, "string index out of range"); ./Objects/structseq.c:62: PyErr_SetString(PyExc_IndexError, "tuple index out of range"); ./Objects/tupleobject.c:104: PyErr_SetString(PyExc_IndexError, "tuple index out of range"); ./Objects/tupleobject.c:310: PyErr_SetString(PyExc_IndexError, "tuple index out of range"); ./Objects/unicodeobject.c:5164: PyErr_SetString(PyExc_IndexError, "string index out of range"); ./Python/exceptions.c:1504:PyDoc_STRVAR(IndexError__doc__, "Sequence index out of range."); ./RISCOS/Modules/drawfmodule.c:534: { PyErr_SetString(PyExc_IndexError,"drawf index out of range"); ./RISCOS/Modules/drawfmodule.c:555: { PyErr_SetString(PyExc_IndexError,"drawf index out of range"); ./RISCOS/Modules/drawfmodule.c:578: { PyErr_SetString(PyExc_IndexError,"drawf index out of range"); ./RISCOS/Modules/swimodule.c:113: { PyErr_SetString(PyExc_IndexError,"block index out of range"); ./RISCOS/Modules/swimodule.c:124: { PyErr_SetString(PyExc_IndexError,"block index out of range"); ./RISCOS/Modules/swimodule.c:136: { PyErr_SetString(PyExc_IndexError,"block index out of range"); ./RISCOS/Modules/swimodule.c:150: { PyErr_SetString(PyExc_IndexError,"block index out of range"); ./RISCOS/Modules/swimodule.c:164: { PyErr_SetString(PyExc_IndexError,"block index out of range"); ./RISCOS/Modules/swimodule.c:225: { PyErr_SetString(PyExc_IndexError,"block index out of range"); ./RISCOS/Modules/swimodule.c:237: { PyErr_SetString(PyExc_IndexError,"block index out of range"); ./RISCOS/Modules/swimodule.c:248: { PyErr_SetString(PyExc_IndexError,"block index out of range"); ./RISCOS/Modules/swimodule.c:264: { PyErr_SetString(PyExc_IndexError,"block index out of range"); wopr:~/src/python_head/dist/src# find ./ -name '*.c' | xargs grep -n ValueError | grep range ./Modules/mmapmodule.c:181: PyErr_SetString (PyExc_ValueError, "read byte out of range"); ./Modules/mmapmodule.c:301: PyErr_SetString (PyExc_ValueError, "data out of range"); ./Modules/mmapmodule.c:524: PyErr_SetString (PyExc_ValueError, "seek out of range"); ./Modules/timemodule.c:405: PyErr_SetString(PyExc_ValueError, "month out of range"); ./Modules/timemodule.c:409: PyErr_SetString(PyExc_ValueError, "day of month out of range"); ./Modules/timemodule.c:413: PyErr_SetString(PyExc_ValueError, "hour out of range"); ./Modules/timemodule.c:417: PyErr_SetString(PyExc_ValueError, "minute out of range"); ./Modules/timemodule.c:421: PyErr_SetString(PyExc_ValueError, "seconds out of range"); ./Modules/timemodule.c:427: PyErr_SetString(PyExc_ValueError, "day of week out of range"); ./Modules/timemodule.c:431: PyErr_SetString(PyExc_ValueError, "day of year out of range"); ./Objects/rangeobject.c:61: PyErr_SetString(PyExc_ValueError, "xrange() arg 3 must not be zero"); ./Objects/rangeobject.c:106: PyErr_SetString(PyExc_ValueError, ./RISCOS/Modules/drawfmodule.c:450: { PyErr_SetString(PyExc_ValueError,"Object out of range");
In 2.4 & 2.3 does it make sense to raise an exception that multiply inherits from both TypeError and AttributeError? If anyone currently does catch the error raising only AttributeError will break their code. 2.5 should just raise an AttributeError, of course.
I think that sets a bad precedent. I understand you want to do this for backwards compatibility, but it's a real ugly thing in the exception inheritance tree and once it's in it's hard to get rid of. It's also introducing a new feature so it's a no-no to do this for 2.3 or 2.4 anyway. I wonder if long-term, AttributeError shouldn't inherit from TypeError? AttributeError really feels to me like a particular case of the stuff that typically raises TypeError. Unfortunately this is *also* a b/w compatibility problem, since people currently might have code like this: try: ... except TypeError: ... except AttributeError: ... and the AttributeError branch would become unreachable. Personally, I think it would be fine to just change the TypeError to AttributeError. I expect that very few people would be hurt by that change (they'd be building *way* too much specific arcane knowledge into their program if they had code for which it mattered). So why, given two different backwards incompatible choices, do I prefer changing the exception raised in this specific case over making AttributeError inherit from TypeError? Because the latter change has a much larger scope; it can affect much more code (including code that doesn't have anything to do with the problem we're trying to solve). -- --Guido van Rossum (home page: http://www.python.org/~guido/)
On Sun, 2005-04-17 at 14:36, Guido van Rossum wrote:
Personally, I think it would be fine to just change the TypeError to AttributeError. I expect that very few people would be hurt by that change (they'd be building *way* too much specific arcane knowledge into their program if they had code for which it mattered).
Unless there are any objections in the next few days, I will take this as a pronouncement and make the change at least in 2.5 and 2.4. -Barry
Personally, I think it would be fine to just change the TypeError to AttributeError. I expect that very few people would be hurt by that change (they'd be building *way* too much specific arcane knowledge into their program if they had code for which it mattered).
Unless there are any objections in the next few days, I will take this as a pronouncement and make the change at least in 2.5 and 2.4.
You meant 2.5 only of course. It's still a new feature and as such can't be changed in 2.4. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
Barry Warsaw
On Sun, 2005-04-17 at 14:36, Guido van Rossum wrote:
Personally, I think it would be fine to just change the TypeError to AttributeError. I expect that very few people would be hurt by that change (they'd be building *way* too much specific arcane knowledge into their program if they had code for which it mattered).
Unless there are any objections in the next few days, I will take this as a pronouncement and make the change at least in 2.5 and 2.4.
I don't think this should be changed in 2.4. Cheers, mwh -- <spiv> As far as I'm concerned, the meat pie is the ultimate unit of currency. -- from Twisted.Quotes
On Monday 18 April 2005 05:17, Barry Warsaw wrote:
Unless there are any objections in the next few days, I will take this as a pronouncement and make the change at least in 2.5 and 2.4.
God no - this isn't suitable for a bugfix release. It seems fine for 2.5,
though.
--
Anthony Baxter
participants (6)
-
Aahz
-
Anthony Baxter
-
Barry Warsaw
-
Guido van Rossum
-
Jack Diederich
-
Michael Hudson