xoutil.eight.exceptions - Exceptions handling compatibility

Solve compatibility issues for exceptions handling.

This module tries to solve, as much as possible, differences between how the raise`, and try ... except statements are executed between Python versions 2 and 3. See Exception Chaining and Embedded Tracebacks.

First issue is that before Python 3, module exceptions was defined, but not anymore. We decided not to implement this concept in xoutil.future package because all these exception classes are built-ins in both Python major versions, so use any of them directly. But StandardError is undefined in Python 3, we introduce it here.

Raising an exception it’s the biggest difference between both major versions, starting with that there is no way to achieve syntax compatibility. In Python 2 the syntax for raise is:

"raise" [type ["," value ["," traceback]]]

and in Python 3:

"raise" [error[.with_traceback(traceback)] ["from" cause]]

There are two partial solutions in this module:

  • Using the function throw() function instead the raise statement:

    throw([error[, traceback=traceback[, cause=cause]]])
    

    This is suitable only when you need the trace-back information is fully tracked when you raise the exception (see below).

  • Using the function grab() the context attributes are assigned, but the trace-back information is not tracked in the system call context.

    raise grab(error[, traceback=traceback[, cause=cause]])

Python 3 is consistent by always assigning context information in the following scenarios:

  1. If an exception is raised inside an exception handler or a finally clause: the previous exception is then attached as the new exception’s __context__ attribute. This is guaranteed in Python 2 using either the grab() function or the throw() function.
  2. Clause from is used for exception chaining, the expression must be another exception instance or class, which will be attached to the newly raised exception in the __cause__ attribute. This information is printed when the raised exception is not handled, but in Python 2 this information can be custom managed by using either grab() or throw() function.
  3. Trace-back information is automatically attached to the attribute __traceback__ when an exception is raised. You can set your own trace-back using the with_traceback method (or in the compatibility function throw() using traceback argument). This can be partially guaranteed in Python 2 by using the catch() function in some exception handler or finally clause.
xoutil.eight.exceptions.catch(base=Unset)[source]

Fix sys.exc_info context in Python 2.

Parameters:base – A subclass of BaseException or Unset.

Next example of Python 3 doesn’t work in Python 2:

>>> def test():
...     try:
...         try:
...             1/0
...         except Exception as error:
...             raise RuntimeError('xxx') from error
...     except Exception as error:
...         return error

>>> error = test()

>>> error.__context__ is error.__cause__
True

>>> isinstance(error.__cause__, ZeroDivisionError)
True

In this case, we can solve most issues by two replacements (throw and catch):

>>> def test():
...     try:
...         try:
...             1/0
...         except catch(Exception) as error:
...             throw(RuntimeError('xxx'), cause=error)
...     except catch(Exception) as error:
...         return error

If base is unset, the catched system error value is returned, so never use this function as:

>>> try:
...     1/0
... except catch():
...     pass
xoutil.eight.exceptions.catch_traceback(error, traceback)[source]

Catch a trace-back information in a corresponding error.

xoutil.eight.exceptions.grab(error, traceback=Unset, cause=Unset)[source]

Prepare an error with traceback, and context to raise it.

xoutil.eight.exceptions.throw(error=Unset, traceback=Unset, cause=Unset)[source]

Unify syntax for raising an error with trace-back information.

Instead of using the Python raise statement, use throw:

"raise" [error[.with_traceback(traceback)] ["from" cause]]

becomes:

throw([error, [traceback=traceback, ][cause=cause]])

If traceback argument is not given, it is looked up in the error.

xoutil.eight.exceptions.traceof(error)[source]

Get the trace-back information of the given error.

Python 3 is consistent by always assigning the trace-back information in the attribute __traceback__, but in Python 2 it’s a “little tricky” to obtain a good solution, and not always, only in some contexts.

Returns None if it could not be found.