Exception with original traceback - 2.6-3.X compat

2019-08-06 11:14发布

问题:

Say I have a context manager like this - which works in Python 2.X and preserves traceback on exit.

class MyContextManager(object):
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_value, traceback):
        raise AssertionError("Failed :-/"), None, traceback

In Python 3, the raise is a syntax error, but I think you can just set the __traceback__ parameter.

def __exit__(self, exc_type, exc_value, traceback):
    e = AssertionError("Failed :-/")
    e.__traceback__ = traceback
    raise e

Is there a way to preserve traceback that's compatible with both Python 2 and Python 3 (i.e., doesn't generate syntax errors on either)? I'm somewhat stuck at this point. It needs to work in 2.6, 2.7, 3.2 and 3.3. The goal would be to make sure that the user still sees the earlier traceback.

回答1:

One ugly, but workable answer that occurred to me (inspired by Ned Batchelder's guide to Python 3 compatibility) is to write a function to evaluate the syntax-breaking code only if it's Python 2. e.g.:

if sys.version_info[0] == 2:
   s = """
def raise_with_traceback(exc, traceback):
    raise exc, None, traceback
"""
   exec (s)
else:
   def raise_with_traceback(exc, traceback):
       raise exc.with_traceback(traceback)

[hat tip to @user2357112 about with_traceback being preferred for Python 3].