Skip to content

Commit

Permalink
At startup, count how many frames Context.run adds to tracebacks (#634)
Browse files Browse the repository at this point in the history
Count how many frames Context.run adds to tracebacks and use this info 
to elide frames in run_impl().

See also: #631
  • Loading branch information
njsmith authored and belm0 committed Sep 1, 2018
1 parent fbb4543 commit a27696b
Showing 1 changed file with 36 additions and 8 deletions.
44 changes: 36 additions & 8 deletions trio/_core/_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import os
import random
import select
import sys
import threading
from collections import deque
import collections.abc
Expand Down Expand Up @@ -64,6 +63,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
Expand Down Expand Up @@ -1392,13 +1419,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,
Expand Down

0 comments on commit a27696b

Please sign in to comment.