Exceptions in atexit handlers crash PyPy and are slightly scary looking

Create issue
Issue #2259 resolved
Julian Berman created an issue
  pypy -c 'import atexit; atexit.register(lambda : object().explode)'
Error in atexit._run_exitfuncs:
Traceback (most recent call last):
  File "/usr/local/Cellar/pypy/5.0.0/libexec/lib-python/2.7/atexit.py", line 24, in _run_exitfuncs
    func(*targs, **kargs)
  File "<string>", line 1, in <lambda>
AttributeError: 'object' object has no attribute 'explode'
debug: OperationError:
debug:  operror-type: AttributeError
debug:  operror-value: 'object' object has no attribute 'explode'

is somewhat scary looking, it looks like the kind of output one would be used to seeing when PyPy crashes. I'm not sure if PyPy is crashing there or not (probably could tell by adding a second handler and seeing if it runs?) but compare to CPython:

Error in atexit._run_exitfuncs:
Traceback (most recent call last):
  File "/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/python2.7/atexit.py", line 24, in _run_exitfuncs
    func(*targs, **kargs)
  File "<string>", line 1, in <lambda>
AttributeError: 'object' object has no attribute 'explode'
Error in sys.exitfunc:
Traceback (most recent call last):
  File "/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/python2.7/atexit.py", line 24, in _run_exitfuncs
    func(*targs, **kargs)
  File "<string>", line 1, in <lambda>
AttributeError: 'object' object has no attribute 'explode'

also not completely ideal, I'm not sure why it shows the traceback twice, but a bit less scary.

Comments (9)

  1. Julian Berman reporter

    Yeah looks like it might be crashing. Compare PyPy to CPython on:

      python -c 'import atexit, sys; atexit.register(lambda : object().explode); atexit.register(lambda : sys.stdout.write("SECOND HANDLER RAN"))'                                                                                                                                                                                                           Julian@Macnetic 
    Error in atexit._run_exitfuncs:
    Traceback (most recent call last):
      File "/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/python2.7/atexit.py", line 24, in _run_exitfuncs
        func(*targs, **kargs)
      File "<string>", line 1, in <lambda>
    AttributeError: 'object' object has no attribute 'explode'
    Error in sys.exitfunc:
    SECOND HANDLER RANTraceback (most recent call last):
      File "/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/python2.7/atexit.py", line 24, in _run_exitfuncs
        func(*targs, **kargs)
      File "<string>", line 1, in <lambda>
    AttributeError: 'object' object has no attribute 'explode'
    
    ~/Desktop
      pypy -c 'import atexit, sys; atexit.register(lambda : object().explode); atexit.register(lambda : sys.stdout.write("SECOND HANDLER RAN"))'                                                                                                                                                                                                             Julian@Macnetic 
    Error in atexit._run_exitfuncs:
    Traceback (most recent call last):
      File "/usr/local/Cellar/pypy/5.0.0/libexec/lib-python/2.7/atexit.py", line 24, in _run_exitfuncs
        func(*targs, **kargs)
      File "<string>", line 1, in <lambda>
    AttributeError: 'object' object has no attribute 'explode'
    debug: OperationError:
    debug:  operror-type: AttributeError
    debug:  operror-value: 'object' object has no attribute 'explode'
    
  2. Philip Jenvey

    Something along the lines of this should match CPython's behavior

    (code is somewhat untested)

    --- a/pypy/interpreter/baseobjspace.py  Mon Dec 28 20:28:08 2015 -0800
    +++ b/pypy/interpreter/baseobjspace.py  Wed Mar 16 13:03:42 2016 -0700
    @@ -417,7 +417,14 @@
             self.wait_for_thread_shutdown()
             w_exitfunc = self.sys.getdictvalue(self, 'exitfunc')
             if w_exitfunc is not None:
    -            self.call_function(w_exitfunc)
    +            try:
    +                self.call_function(w_exitfunc)
    +            except OperationError as e:
    +                extra_line = None
    +                if not e.match(self, self.w_SystemExit):
    +                    extra_line = "Error in sys.exitfunc:\n"
    +                e.write_unraisable(self, None, with_traceback=True,
    +                                   extra_line=extra_line)
             from pypy.interpreter.module import Module
             for w_mod in self.builtin_modules.values():
                 if isinstance(w_mod, Module) and w_mod.startup_called:
    
  3. Armin Rigo

    Should be fixed in 53be1372d9ce. CPython prints the traceback twice because of how atexit.py is written (it will print a traceback for every registered function which raises, and then re-raise the last exception, which will be re-printed). So I picked an arguably better route and, the 2nd time, used a more compact error message.

  4. Armin Rigo

    Sorry Philip! Parallel work here.

    What is Py_Flushline? And are you sure it isn't done already by one of the shutdown() calls that follows?

  5. Log in to comment