Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling same type exceptions separately in Python

Say I have the following four variables:

>>> f1 = (print, 1, 2 ,3 ,4)
>>> f2 = (exit, 1, 2 ,3 ,4)
>>> f3 = (1, 2, 3, 4)
>>> f4 = 4

In a hypothetical program, I expect each of those variables to hold a tuple, whose first item should be the name of a function, and whose subsequent items should be said function's parameters, in order.

I could call a function stored in this way like so:

>>> f1[0](*f1[1:])
1 2 3 4

However, most of these variables are not in the excepted format, and I would like to be able to encapsulate the calling of them inside try/except blocks in order to handle those situations.

Now, even though the function calls of f2, f3 and f4 break for radically different reason, they all throw the same kind of exception, a TypeError:

>>> f2[0](*f2[1:])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __call__() takes from 1 to 2 positional arguments but 5 were given
>>> f3[0](*f3[1:])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
>>> f4[0](*f4[1:])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not subscriptable

So that doing a generic:

try:
    f[0](*f[1:])
except TypeError:
    # handle exception

Would not provide me with enough information to handle each exception accordingly.

What would be the correct way to differentiate between distinct exceptions of the same type in Python?

like image 978
zayora Avatar asked Dec 26 '15 04:12

zayora


1 Answers

So, you need to use a different method of calling than function(*tuple). Here is an example of a function fun_apply that calls a particular function with positional and keyword arguments. It adds an explicit check to make sure that args is iterable and kwargs inherits from collection.Mapping.

from collections import Mapping

def fun_apply(f, args=None, kwargs=None):                                                                                
    if args is None:
        args = []
    if kwargs is None:
        kwargs = {}

    try:
        args = iter(args)
    except TypeError:
        # handle args being non-iterable
        pass

    if isinstance(kwargs, collections.Mapping):
        pass
    else:
        # handle kwargs being a non-mapping
        pass

    if hasattr(f, '__call__'):
        pass
    else:
        # handle f being non-callable
        pass

    try:
        f(*args, **kwargs)
    except TypeError as e:
        raise TypeError(e)

The other option is to process the message string explicitly, but this could have unintended consequences and the error messages themselves might differ between versions of Python.

like image 152
Gregory Nisbet Avatar answered Oct 20 '22 07:10

Gregory Nisbet