IPython behavior different for .py and .ipy files

2019-06-23 17:15发布

问题:

I have written an exception handler that is meant to log all uncaught exceptions in the code before calling the normal python exception hooks. Python and iPython have slightly different ways of doing this. What i have found is that the iPython way of doing it only works when run from within an interactive session, or when using a ".ipy" filename extension, despite the fact that I don't believe i am using any 'Magic,' and the regular python way of doing it won't work within ipython even if the script is named .py and is 'Pure python'.

I want a way to be able to set my own exception hook for iPython from within a script, even if it is called from the command line as a ".py" file.

Example code testcase.py:

import sys

def my_exc(exc_type, exc_value, exc_traceback):
    """Regular Python exception handler with inputs mirroring sys.excepthook"""

    print('\nDoing Custom Stuff (Python exc handler)\n')
    # Call the old sys.excepthook as well
    sys.__excepthook__(exc_type, exc_value, exc_traceback)

def imy_exc(shell, exc_type, exc_value, exc_tb, tb_offset=None):
    """IPythonic exception handler, following instructions in 
    set_custom_exc here:
    http://ipython.org/ipython-doc/stable/api/generated/IPython.core.interactiveshell.html"""

    print('\nDoing Custom Stuff (iPython exc handler)\n')
    # Do regular IPython stuff as well
    shell.showtraceback((exc_type, exc_value, exc_tb), tb_offset=tb_offset)

try:
    __IPYTHON__
    from IPython import get_ipython
    shell = get_ipython()
    if shell:
        print('Shell Active')
        # Set my custom exception handler for the current IPython shell
        get_ipython().set_custom_exc((Exception,), imy_exc)
    else:
        print('In IPython, but no active shell')
        sys.excepthook = my_exc    
except NameError:
    print('NameError, falling back on regular Python way')
    # use the standard python excepthook override
    sys.excepthook = my_exc

def makeproblem():
    1/0

makeproblem()

Behavior:

Works:

1. $ ipython testcase.ipy
- result: "shell active", "Doing Custom Stuff (iPython exc handler)""

2. $ ipython -c="execfile('testcase.py')
- result: "NameError, falling back on regular Python way", "Doing Custom Stuff (Python exc handler)" (This one is particularly puzzling)

3. $ python testcase.py
- result: "NameError, falling back on regular Python way", "Doing Custom Stuff (Python exc handler)"

4. $ ipython
In[0]: execfile('testcase.py')
or
In[0]: %paste #pasting testcase.py    
- result: "shell active", "Doing Custom Stuff (iPython exc handler)""

Doesn't work:

1. $ ipython testcase.py
- result: "Shell Active", no override statement

2. $ ipython -c="% run 'testcase.py'"
- result: "Shell Active", no override statement

the %run vs execfile behavior suggest to me that it has something to do with the script being run in a self-contained namespace, but I haven't been able to figure it out. This could be a red herring though. The other hint I have is that if i dump the frames using inspect in the testcase script above the cases that work have the following calls to the interactiveshell at the top, which don't appear otherwise:

(<frame object at 0x10264b500>, '/Library/Frameworks/EPD64.framework/Versions/7.3/lib/python2.7/site-packages/IPython/core/interactiveshell.py', 2883, 'run_code', ['exec(code_obj, self.user_global_ns, self.user_ns)\n'], 0)
(<frame object at 0x10264a790>, '/Library/Frameworks/EPD64.framework/Versions/7.3/lib/python2.7/site-packages/IPython/core/interactiveshell.py', 2827, 'run_ast_nodes', ['if self.run_code(code):\n'], 0)
(<frame object at 0x10263eda0>, '/Library/Frameworks/EPD64.framework/Versions/7.3/lib/python2.7/site-packages/IPython/core/interactiveshell.py', 2741, 'run_cell', ['interactivity=interactivity, compiler=compiler)\n'], 0)
(<frame object at 0x10263e7c0>, '/Library/Frameworks/EPD64.framework/Versions/7.3/lib/python2.7/site-packages/IPython/core/interactiveshell.py', 2604, 'safe_execfile_ipy', ['self.run_cell(cell, silent=True, shell_futures=False)\n'], 0)