Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Checking for an exception

72 views
Skip to first unread message

Steve D'Aprano

unread,
Jun 24, 2017, 6:31:11 AM6/24/17
to
What's the right/best way to test whether an object is an exception ahead of
time? (That is, without trying to raise from it.)

I have:

return (isinstance(obj, type) and issubclass(obj, BaseException)
or isinstance(obj, BaseException))


Any better ideas?



--
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.

Message has been deleted

Steve D'Aprano

unread,
Jun 24, 2017, 7:48:48 AM6/24/17
to
On Sat, 24 Jun 2017 09:23 pm, mbyr...@gmail.com wrote:

> On Saturday, June 24, 2017 at 11:31:11 AM UTC+1, Steve D'Aprano wrote:
>> What's the right/best way to test whether an object is an exception ahead of
>> time? (That is, without trying to raise from it.)
>>
>> I have:
>>
>> return (isinstance(obj, type) and issubclass(obj, BaseException)
>> or isinstance(obj, BaseException))
>>
>>
>> Any better ideas?


> Would something along these lines help?
>
> import exceptions

py> import exceptions
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named 'exceptions'


What module are you importing?



> if 'ArithmeticError' in dir(exceptions):
> print 'is an exception'


I don't understand that. I'm not looking for strings which happen to match the
names of built-in exceptions, I'm looking to test for any object which is an
exception. To be an exception, you must inherit from BaseException.

(String exceptions did work until Python 2.5, but I don't care about them.)



> try:
> if exceptions.__getattribute__('ArithmeticError'):

You shouldn't call __getattribute__ directly -- that's reserved for Python's
use. You should call getattr() with an optional default value:

if getattr(exceptions, 'ArithmeticError', False):
print 'is an exception'
else:
print 'is an exception'


But in any case, no, I don't want to look up a string in some list of
exceptions. That won't test for exceptions that aren't in the list.

class MyCustomException(LookupError):
pass

Cameron Simpson

unread,
Jun 24, 2017, 7:37:55 PM6/24/17
to
On 24Jun2017 20:31, Steve D'Aprano <steve+...@pearwood.info> wrote:
>What's the right/best way to test whether an object is an exception ahead of
>time? (That is, without trying to raise from it.)
>
>I have:
>
>return (isinstance(obj, type) and issubclass(obj, BaseException)
> or isinstance(obj, BaseException))

I haven't a better idea.

Are you supporting Python 2 here, where one can raise bare exceptions instead
of instances? Also, do you need the "isinstance(obj, type)" precursor to the
issubclass?

Might you be better with:

return ( issubclass(obj, BaseException)
if isinstance(obj, type)
else isinstance(obj, BaseException)
)

?

Curious: why do you need to test this? Some function which may return a "value"
or an exception?

Cheers,
Cameron Simpson <c...@zip.com.au>

Ben Finney

unread,
Jun 24, 2017, 8:49:39 PM6/24/17
to
Steve D'Aprano <steve+...@pearwood.info> writes:

> What's the right/best way to test whether an object is an exception
> ahead of time? (That is, without trying to raise from it.)

This being Python, it is Easier to Ask for Forgiveness than for
Permission.

The corollary of that is, if you try to ask permission first (to Look
Before You Leap), it will likely not be as easy as simply using the
object as you intend to use it.

So, EAFP would suggest just raising the object:

raise obj

and thereby reveal the bug in the *caller's* code if it tries to pass a
not-exception object for that purpose.

> return (isinstance(obj, type) and issubclass(obj, BaseException)
> or isinstance(obj, BaseException))
>
> Any better ideas?

It's clumsy and noisy, but I think that's the cost of trying to Look
Before You Leap in code.

--
\ “My girlfriend has a queen sized bed; I have a court jester |
`\ sized bed. It's red and green and has bells on it, and the ends |
_o__) curl up.” —Steven Wright |
Ben Finney

Steve D'Aprano

unread,
Jun 24, 2017, 9:15:12 PM6/24/17
to
On Sun, 25 Jun 2017 09:37 am, Cameron Simpson wrote:

> On 24Jun2017 20:31, Steve D'Aprano <steve+...@pearwood.info> wrote:
>>What's the right/best way to test whether an object is an exception ahead of
>>time? (That is, without trying to raise from it.)
>>
>>I have:
>>
>>return (isinstance(obj, type) and issubclass(obj, BaseException)
>> or isinstance(obj, BaseException))
>
> I haven't a better idea.
>
> Are you supporting Python 2 here, where one can raise bare exceptions instead
> of instances?

Both Python 2 and Python 3 support raising either exception classes or exception
instances:

raise ValueError
raise ValueError('message')

both work. (If you use a type alone, the raise statement automatically
instantiates it, otherwise it just uses the instance you give.)


> Also, do you need the "isinstance(obj, type)" precursor to the
> issubclass?

Yes, because annoyingly issubclass raises if you pass something which isn't a
type, instead of just returning False:


py> issubclass(99, int)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: issubclass() arg 1 must be a class



> Might you be better with:
>
> return ( issubclass(obj, BaseException)
> if isinstance(obj, type)
> else isinstance(obj, BaseException)
> )
>
> ?

I didn't think of that. If I were just supporting Python 2.7 or 3.6, I think I
would prefer that, but for my sins I'm supporting 2.4 :-(


> Curious: why do you need to test this? Some function which may return a
> "value" or an exception?

I have a decorator which converts exceptions in the decorated function from one
type to another. It requires as two arguments:

- the exception to be caught: an exception type, or a tuple of exception types

- the exception to be re-raised: an exception type, or an exception instance


If the caller provides bad arguments to the decorator, I want to raise
*immediately*, not when the decorated function is called.

Steve D'Aprano

unread,
Jun 24, 2017, 9:31:37 PM6/24/17
to
On Sun, 25 Jun 2017 10:49 am, Ben Finney wrote:

> Steve D'Aprano <steve+...@pearwood.info> writes:
>
>> What's the right/best way to test whether an object is an exception
>> ahead of time? (That is, without trying to raise from it.)
>
> This being Python, it is Easier to Ask for Forgiveness than for
> Permission.

Sometimes...


> The corollary of that is, if you try to ask permission first (to Look
> Before You Leap), it will likely not be as easy as simply using the
> object as you intend to use it.
>
> So, EAFP would suggest just raising the object:
>
> raise obj

Unfortunately it's not that simple, as the result of passing a non-exception to
raise is to raise an exception, so I cannot trivially distinguish
between "caller passes an exception" and "caller passes a non-exception"
(result is still an exception).

I could write something like:

def is_exception(obj):
try:
raise obj # This unconditionally raises.
except TypeError:
# Either obj is an instance or subclass of TypeError,
# or it's some arbitrary non-exception object.
if isinstance(obj, TypeError):
return True
try:
return issubclass(obj, TypeError)
except TypeError:
return False
except:
# Any exception other than TypeError means obj is that exception.
return True
else:
# In Python 2.4 and 2.5 you can (but shouldn't) raise strings.
assert isinstance(obj, str)
return False


but I don't think that's much of an improvement :-)

Ben Finney

unread,
Jun 24, 2017, 11:48:20 PM6/24/17
to
Steve D'Aprano <steve+...@pearwood.info> writes:

> […] the result of passing a non-exception to raise is to raise an
> exception, so I cannot trivially distinguish between "caller passes an
> exception" and "caller passes a non-exception" (result is still an
> exception).

Yes, hence my characterising this problem as the caller's problem.

I'd say: document the expectation that the value will be an exception,
use it based on that specification, and let the caller deal with the
consequences of violating that expectation.

--
\ “I went to court for a parking ticket; I pleaded insanity. I |
`\ said ‘Your Honour, who in their right mind parks in the passing |
_o__) lane?’” —Steven Wright |
Ben Finney

Cameron Simpson

unread,
Jun 25, 2017, 2:27:07 AM6/25/17
to
On 25Jun2017 13:47, Ben Finney <ben+p...@benfinney.id.au> wrote:
>Steve D'Aprano <steve+...@pearwood.info> writes:
>> […] the result of passing a non-exception to raise is to raise an
>> exception, so I cannot trivially distinguish between "caller passes an
>> exception" and "caller passes a non-exception" (result is still an
>> exception).
>
>Yes, hence my characterising this problem as the caller's problem.
>
>I'd say: document the expectation that the value will be an exception,
>use it based on that specification, and let the caller deal with the
>consequences of violating that expectation.

I'm a "fail early" kind of guy, and to me Steve's approach is in the same
spirit as raising ValueError when a function is handed invalid arguments.

Particularly if the mistake is easy to make, having one's attention brought to
it immediately (at "declaration" time, since Steve's example is a decorator),
seems very desirable.

Cheers,
Cameron Simpson <c...@zip.com.au>

Paul Rubin

unread,
Jun 25, 2017, 3:51:04 AM6/25/17
to
Steve D'Aprano <steve+...@pearwood.info> writes:
> What's the right/best way to test whether an object is an exception
> ahead of time? (That is, without trying to raise from it.)

Maybe I'm missing something but
isinstance(obj, Exception)
seems to work.

Steve D'Aprano

unread,
Jun 25, 2017, 12:11:18 PM6/25/17
to
Not well enough I'm afraid:



py> isinstance(KeyboardInterrupt(), Exception)
False
py> isinstance(ValueError, Exception)
False

Skip Montanaro

unread,
Jun 25, 2017, 12:40:42 PM6/25/17
to
>
> py> isinstance(KeyboardInterrupt(), Exception)
> False
> py> isinstance(ValueError, Exception)
> False
>

I might have missed something, but don't you want to be using BaseException
as your class/type? Also, Checking isinstance() between two classes isn't
likely to work, I don't think.

Both the 2.7 and 3.6 docs indicate that BaseException is the base class for
all built-in exceptions:

https://docs.python.org/2/library/exceptions.html
https://docs.python.org/3/library/exceptions.html

Of course, YMMV when dealing with user-defined exception classes.

Skip

D'Arcy Cain

unread,
Jun 25, 2017, 12:55:15 PM6/25/17
to
On 06/25/17 12:10, Steve D'Aprano wrote:
> py> isinstance(KeyboardInterrupt(), Exception)
> False
> py> isinstance(ValueError, Exception)
> False

That's because KeyboardInterrupt is not a subclass of Exception. If you
want to catch that as well you need to check against BaseException.

https://docs.python.org/3.6/library/exceptions.html#exception-hierarchy

--
D'Arcy J.M. Cain
Vybe Networks Inc.
http://www.VybeNetworks.com/
IM:da...@Vex.Net VoIP: sip:da...@VybeNetworks.com

Steve D'Aprano

unread,
Jun 25, 2017, 1:31:46 PM6/25/17
to
On Mon, 26 Jun 2017 02:40 am, Skip Montanaro wrote:

>> py> isinstance(KeyboardInterrupt(), Exception)
>> False
>> py> isinstance(ValueError, Exception)
>> False
>>
>
> I might have missed something, but don't you want to be using BaseException
> as your class/type?

Yes I do, which is why I was demonstrating to Paul that his suggestion to use
Exception instead of BaseException is not good enough.

*wink*

> Also, Checking isinstance() between two classes isn't
> likely to work, I don't think.

Indeed it doesn't, which is why I was demonstrating that Paul's suggestion to
use isinstance instead of issubclass is not good enough.

*wink*


> Both the 2.7 and 3.6 docs indicate that BaseException is the base class for
> all built-in exceptions:

Indeed it is. Which is why in my original post I used BaseException.

*eye spasms from winking too much*


> https://docs.python.org/2/library/exceptions.html
> https://docs.python.org/3/library/exceptions.html
>
> Of course, YMMV when dealing with user-defined exception classes.

No, user-defined exceptions must derive from BaseException too.
0 new messages