@@ -434,8 +434,8 @@ def trio_excepthook(etype, value, tb):
434434 sys .stderr .write (chunk )
435435
436436
437- IPython_handler_installed = False
438- warning_given = False
437+ monkeypatched_or_warned = False
438+
439439if "IPython" in sys .modules :
440440 import IPython
441441 ip = IPython .get_ipython ()
@@ -448,7 +448,7 @@ def trio_excepthook(etype, value, tb):
448448 "tracebacks." ,
449449 category = RuntimeWarning
450450 )
451- warning_given = True
451+ monkeypatched_or_warned = True
452452 else :
453453
454454 def trio_show_traceback (self , etype , value , tb , tb_offset = None ):
@@ -457,15 +457,44 @@ def trio_show_traceback(self, etype, value, tb, tb_offset=None):
457457 trio_excepthook (etype , value , tb )
458458
459459 ip .set_custom_exc ((MultiError ,), trio_show_traceback )
460- IPython_handler_installed = True
460+ monkeypatched_or_warned = True
461461
462462if sys .excepthook is sys .__excepthook__ :
463463 sys .excepthook = trio_excepthook
464- else :
465- if not IPython_handler_installed and not warning_given :
466- warnings .warn (
467- "You seem to already have a custom sys.excepthook handler "
468- "installed. I'll skip installing Trio's custom handler, but this "
469- "means MultiErrors will not show full tracebacks." ,
470- category = RuntimeWarning
471- )
464+ monkeypatched_or_warned = True
465+
466+ # Ubuntu's system Python has a sitecustomize.py file that import
467+ # apport_python_hook and replaces sys.excepthook.
468+ #
469+ # The custom hook captures the error for crash reporting, and then calls
470+ # sys.__excepthook__ to actually print the error.
471+ #
472+ # We don't mind it capturing the error for crash reporting, but we want to
473+ # take over printing the error. So we monkeypatch the apport_python_hook
474+ # module so that instead of calling sys.__excepthook__, it calls our custom
475+ # hook.
476+ #
477+ # More details: https://github.com/python-trio/trio/issues/1065
478+ if sys .excepthook .__name__ == "apport_excepthook" :
479+ import apport_python_hook
480+ assert sys .excepthook is apport_python_hook .apport_excepthook
481+
482+ # Give it a descriptive name as a hint for anyone who's stuck trying to
483+ # debug this mess later.
484+ class TrioFakeSysModuleForApport :
485+ pass
486+
487+ fake_sys = TrioFakeSysModuleForApport ()
488+ fake_sys .__dict__ .update (sys .__dict__ )
489+ fake_sys .__excepthook__ = trio_excepthook
490+ apport_python_hook .sys = fake_sys
491+
492+ monkeypatched_or_warned = True
493+
494+ if not monkeypatched_or_warned :
495+ warnings .warn (
496+ "You seem to already have a custom sys.excepthook handler "
497+ "installed. I'll skip installing Trio's custom handler, but this "
498+ "means MultiErrors will not show full tracebacks." ,
499+ category = RuntimeWarning
500+ )
0 commit comments