From b31da5eef895454b011591931761279f6ba0ba34 Mon Sep 17 00:00:00 2001 From: "Nathaniel J. Smith" Date: Mon, 27 Aug 2018 22:51:39 -0700 Subject: [PATCH 1/2] At startup, count how many frames Context.run adds to tracebacks This is kind of ridiculous overkill, but I was thinking about how you could do it, then wrote some code to experiment, then realized that I had implemented it so we might as well use it... See also: https://github.com/python-trio/trio/pull/631 --- trio/_core/_run.py | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/trio/_core/_run.py b/trio/_core/_run.py index aa4af1b9a3..4d4a703fcd 100644 --- a/trio/_core/_run.py +++ b/trio/_core/_run.py @@ -64,6 +64,34 @@ INSTRUMENT_LOGGER = logging.getLogger("trio.abc.Instrument") +# On 3.7+, Context.run() is implemented in C and doesn't show up in +# tracebacks. On 3.6 and earlier, we use the contextvars backport, which is +# currently implemented in Python and adds 1 frame to tracebacks. So this +# function is a super-overkill version of "0 if sys.version_info >= (3, 7) +# else 1". But if Context.run ever changes, we'll be ready! +# +# This can all be removed once we drop support for 3.6. +def _count_context_run_tb_frames(): + def function_with_unique_name_xyzzy(): + 1 / 0 + + ctx = copy_context() + try: + ctx.run(function_with_unique_name_xyzzy) + except ZeroDivisionError as exc: + tb = exc.__traceback__ + # Skip the frame where we caught it + tb = tb.tb_next + count = 0 + while tb.tb_frame.f_code.co_name != "function_with_unique_name_xyzzy": + tb = tb.tb_next + count += 1 + return count + + +CONTEXT_RUN_TB_FRAMES = _count_context_run_tb_frames() + + @attr.s(frozen=True) class SystemClock: # Add a large random offset to our clock to ensure that if people @@ -1392,13 +1420,14 @@ def run_impl(runner, async_fn, args): except StopIteration as stop_iteration: final_result = Value(stop_iteration.value) except BaseException as task_exc: - # Store for later, removing uninteresting top frames: - # 1. trio._core._run.run_impl() - # 2. contextvars.Context.run() (< Python 3.7 only) - tb_next = task_exc.__traceback__.tb_next - if sys.version_info < (3, 7): - tb_next = tb_next.tb_next - final_result = Error(task_exc.with_traceback(tb_next)) + # Store for later, removing uninteresting top frames: 1 frame + # we always remove, because it's this function catching it, + # and then in addition we remove however many more Context.run + # adds. + tb = task_exc.__traceback__.tb_next + for _ in range(CONTEXT_RUN_TB_FRAMES): + tb = tb.tb_next + final_result = Error(task_exc.with_traceback(tb)) if final_result is not None: # We can't call this directly inside the except: blocks above, From 1cfadda68aa386c3b7df3034470566b326cdfe88 Mon Sep 17 00:00:00 2001 From: John Belmonte Date: Sat, 1 Sep 2018 15:44:08 +0900 Subject: [PATCH 2/2] remove unused import --- trio/_core/_run.py | 1 - 1 file changed, 1 deletion(-) diff --git a/trio/_core/_run.py b/trio/_core/_run.py index 4d4a703fcd..9658fac8b2 100644 --- a/trio/_core/_run.py +++ b/trio/_core/_run.py @@ -3,7 +3,6 @@ import os import random import select -import sys import threading from collections import deque import collections.abc