How can I modify a Python traceback object when raising an exception?

后端 未结 7 1322
梦如初夏
梦如初夏 2020-11-29 09:56

I\'m working on a Python library used by third-party developers to write extensions for our core application.

I\'d like to know if it\'s possible to modify the trace

7条回答
  •  天涯浪人
    2020-11-29 10:42

    Starting with Python 3.7, you can instantiate a new traceback object and use the .with_traceback() method when throwing. Here's some demo code using either sys._getframe(1) (or a more robust alternative) that raises an AssertionError while making your debugger believe the error occurred in myassert(False): sys._getframe(1) omits the top stack frame.

    What I should add is that while this looks fine in the debugger, the console behavior unveils what this is really doing:

    Traceback (most recent call last):
      File ".\test.py", line 35, in 
        myassert_false()
      File ".\test.py", line 31, in myassert_false
        myassert(False)
      File ".\test.py", line 26, in myassert
        raise AssertionError().with_traceback(back_tb)
      File ".\test.py", line 31, in myassert_false
        myassert(False)
    AssertionError
    

    Rather than removing the top of the stack, I have added a duplicate of the second-to-last frame.

    Anyway, I focus on how the debugger behaves, and it seems this one works correctly:

    """Modify traceback on exception.
    
    See also https://github.com/python/cpython/commit/e46a8a
    """
    
    import sys
    import types
    
    
    def myassert(condition):
        """Throw AssertionError with modified traceback if condition is False."""
        if condition:
            return
    
        # This function ... is not guaranteed to exist in all implementations of Python.
        # https://docs.python.org/3/library/sys.html#sys._getframe
        # back_frame = sys._getframe(1)
        try:
            raise AssertionError
        except AssertionError:
            traceback = sys.exc_info()[2]
            back_frame = traceback.tb_frame.f_back
    
        back_tb = types.TracebackType(tb_next=None,
                                      tb_frame=back_frame,
                                      tb_lasti=back_frame.f_lasti,
                                      tb_lineno=back_frame.f_lineno)
        raise AssertionError().with_traceback(back_tb)
    
    
    def myassert_false():
        """Test myassert(). Debugger should point at the next line."""
        myassert(False)
    
    
    if __name__ == "__main__":
        myassert_false()
    

提交回复
热议问题