🌐 AI搜索 & 代理 主页
Skip to content

Commit 66172c8

Browse files
authored
Merge pull request #1528 from njsmith/apport-excepthook
Make Trio's excepthook play nicely with Ubuntu's excepthook
2 parents 51dd435 + 0e56e28 commit 66172c8

File tree

5 files changed

+88
-12
lines changed

5 files changed

+88
-12
lines changed

ci.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,11 @@ else
407407
# Actual tests
408408
python -m pip install -r test-requirements.txt
409409

410+
# So we can run the test for our apport/excepthook interaction working
411+
if [ -e /etc/lsb-release ] && grep -q Ubuntu /etc/lsb-release; then
412+
sudo apt install -q python3-apport
413+
fi
414+
410415
# If we're testing with a LSP installed, then it might break network
411416
# stuff, so wait until after we've finished setting everything else
412417
# up.

newsfragments/1065.bugfix.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
On Ubuntu systems, the system Python includes a custom
2+
unhandled-exception hook to perform `crash reporting
3+
<https://wiki.ubuntu.com/Apport>`__. Unfortunately, Trio wants to use
4+
the same hook to print nice `MultiError` tracebacks, causing a
5+
conflict. Previously, Trio would detect the conflict, print a warning,
6+
and you just wouldn't get nice `MultiError` tracebacks. Now, Trio has
7+
gotten clever enough to integrate its hook with Ubuntu's, so the two
8+
systems should Just Work together.

trio/_core/_multierror.py

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
439439
if "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

462462
if 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+
)

trio/_core/tests/test_multierror.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,3 +709,26 @@ def test_ipython_custom_exc_handler():
709709
)
710710
# Make sure our other warning doesn't show up
711711
assert "custom sys.excepthook" not in completed.stdout.decode("utf-8")
712+
713+
714+
@slow
715+
@pytest.mark.skipif(
716+
not Path("/usr/lib/python3/dist-packages/apport_python_hook.py").exists(),
717+
reason="need Ubuntu with python3-apport installed"
718+
)
719+
def test_apport_excepthook_monkeypatch_interaction():
720+
completed = run_script("apport_excepthook.py")
721+
stdout = completed.stdout.decode("utf-8")
722+
723+
# No warning
724+
assert "custom sys.excepthook" not in stdout
725+
726+
# Proper traceback
727+
assert_match_in_seq(
728+
[
729+
"Details of embedded",
730+
"KeyError",
731+
"Details of embedded",
732+
"ValueError",
733+
], stdout
734+
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# The apport_python_hook package is only installed as part of Ubuntu's system
2+
# python, and not available in venvs. So before we can import it we have to
3+
# make sure it's on sys.path.
4+
import sys
5+
sys.path.append("/usr/lib/python3/dist-packages")
6+
import apport_python_hook
7+
apport_python_hook.install()
8+
9+
import trio
10+
11+
raise trio.MultiError([KeyError("key_error"), ValueError("value_error")])

0 commit comments

Comments
 (0)