Instance Exception Oddity: Implicit and Explicit not the same?

R

RT Lange

class E1(Exception): pass
Traceback (most recent call last):
File "<pyshell#5>", line 1, in ?
raise E1(i)
E1: fooTraceback (most recent call last):
File "<pyshell#6>", line 1, in ?
raise E1, i
E2: foo
Is there a reason the exception type is not the same?
Is this behavior something that should be expected?
 
P

Peter Otten

RT said:
Traceback (most recent call last):
File "<pyshell#5>", line 1, in ?
raise E1(i)
E1: foo
Traceback (most recent call last):
File "<pyshell#6>", line 1, in ?
raise E1, i
E2: foo

Is there a reason the exception type is not the same?
Is this behavior something that should be expected?

This is interesting. I thought of

raise E, o # 1

and

raise E(o) # 2

as equivalent. Well, until today:

"If the first object is a class, it becomes the type of the exception. The
second object is used to determine the exception value: If it is an
instance of the class, the instance becomes the exception value. If the
second object is a tuple, it is used as the argument list for the class
constructor; if it is None, an empty argument list is used, and any other
object is treated as a single argument to the constructor. The instance so
created by calling the constructor is used as the exception value."

(quoted from http://www.python.org/doc/current/ref/raise.html)

So, if isinstance(o, E), the first form is indeed equivalent to

raise o

and your code works as advertised.


Peter
 
A

Aahz

Traceback (most recent call last):
File "<pyshell#5>", line 1, in ?
raise E1(i)
E1: foo

<scratch head> Why are you passing an exception instance to the
constructor for a different exception?
 
R

RT Lange

This is interesting. I thought of

raise E, o # 1

and

raise E(o) # 2

as equivalent. Well, until today:

"If the first object is a class, it becomes the type of the exception.
The
second object is used to determine the exception value: If it is an
instance of the class, the instance becomes the exception value. If the
second object is a tuple, it is used as the argument list for the class
constructor; if it is None, an empty argument list is used, and any other
object is treated as a single argument to the constructor. The instance
so
created by calling the constructor is used as the exception value."

(quoted from http://www.python.org/doc/current/ref/raise.html)

So, if isinstance(o, E), the first form is indeed equivalent to

raise o
but if o is an instance of an E subclass (hence isinstance(o, E) is still
true),
shouldn't the first form raise an exception with type E (not E's subclass)
and value o?
 
P

Peter Otten

RT said:
but if o is an instance of an E subclass (hence isinstance(o, E) is still
true),
shouldn't the first form raise an exception with type E (not E's subclass)
and value o?

Again:

"If it is an instance of the class, the *instance* *becomes* the *exception*
*value*"

Or, directly from the source:

/* if the value was not an instance, or is not an instance
whose class is (or is derived from) type, then use the
value as an argument to instantiation of the type
class.
*/

(comment in function PyErr_NormalizeException() in error.c)

As clear as you can get. I cannot comment on the rationale of that design
decision, though. I would replace the

raise E, args # disallow in 3.0?

form completely with

raise E(args)

which would avoid the ambiguity altogether.


Peter

PS: I think you owe me an answer to Aahz' pending question now :)
 
R

rt lange

Peter Otten said:
"If it is an instance of the class, the *instance* *becomes* the *exception*
*value*"
this says nothing about the *type* of exception, just the *value*.


"6.9 The raise statement
....The first two objects are used to determine the type and value of
the exception.
If the first object is an instance, the TYPE of the exception is the
class of the instance, the instance itself is the VALUE, and the
second object must be None."
raise i
except:
print sys.exc_info()[:2]


(<class __main__.E at 0x00A88090>, <__main__.E instance at
0x00A5A850>)

*makes perfect sense*

"If the first object is a class, it becomes the TYPE of the exception.
The second object is used to determine the exception value: If it is
an instance of the class, the instance becomes the exception VALUE."
raise Exception, i #first object is a class: becomes the type
#second object instance of (sub)class: becomes the valu
except:
print sys.exc_info()[:2]


*hmmm "Exception" did NOT become the TYPE of the exception*

As for why I was passing an exception instance to the
constructor for a different exception...just pointing out the
nonequivalence of the two forms.
Why would someone want to do this? Don't ask me.
I only use string exceptions. :)

RT
 
S

Shalabh Chaturvedi

*hmmm "Exception" did NOT become the TYPE of the exception*

One could argue that Exception is in fact the type of the exception.
.... pass
........ raise Exception, i
.... except:
.... print isinstance(sys.exc_info()[1], Exception)
....
True

It just so happens that the exception is also of type E. In fact
'except Exception:' will also catch the exception.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top