AFAIK Python never hides stdlib codelines in tracebacks. Why we should start to do it in asyncio?
On Tue, Nov 14, 2017 at 4:54 PM Mark E. Haase <meha...@gmail.com> wrote: > If an exception is thrown while the `asyncio` event loop is running, the > stack trace is pretty complicated. Here's an example: > > Traceback (most recent call last): > File "sample_client.py", line 616, in <module> > loop.run_until_complete(main_task) > File "/usr/lib/python3.6/asyncio/base_events.py", line 437, in > run_until_complete > self.run_forever() > File "/usr/lib/python3.6/asyncio/base_events.py", line 405, in > run_forever > self._run_once() > File "/usr/lib/python3.6/asyncio/base_events.py", line 1382, in > _run_once > handle._run() > File "/usr/lib/python3.6/asyncio/events.py", line 126, in _run > self._callback(*self._args) > File "/usr/lib/python3.6/asyncio/tasks.py", line 315, in _wakeup > self._step() > File "/usr/lib/python3.6/asyncio/tasks.py", line 239, in _step > result = coro.send(None) > File "sample_client.py", line 319, in main > await actions[args.action](args, socket) > File "/usr/lib/python3.6/asyncio/coroutines.py", line 105, in > __next__ > return self.gen.send(None) > File "sample_client.py", line 558, in sync_job > await asyncio.sleep(args.delay) > File "/usr/lib/python3.6/asyncio/coroutines.py", line 105, in > __next__ > return self.gen.send(None) > File "/usr/lib/python3.6/asyncio/tasks.py", line 522, in sleep > future, result) > File "/usr/lib/python3.6/asyncio/base_events.py", line 525, in > call_later > timer = self.call_at(self.time() + delay, callback, *args) > File "/usr/lib/python3.6/asyncio/base_events.py", line 541, in > call_at > timer = events.TimerHandle(when, callback, args, self) > File "/usr/lib/python3.6/asyncio/events.py", line 148, in __init__ > super().__init__(callback, args, loop) > File "/usr/lib/python3.6/asyncio/events.py", line 92, in __init__ > self._source_traceback = traceback.extract_stack(sys._getframe(1)) > File "/usr/lib/python3.6/traceback.py", line 207, in extract_stack > stack = StackSummary.extract(walk_stack(f), limit=limit) > File "/usr/lib/python3.6/traceback.py", line 352, in extract > filename, lineno, name, lookup_line=False, locals=f_locals)) > KeyboardInterrupt > > Most of the stack frames are inside asyncio (_run, _wakeup, _step) and > reveal the mechanics of the event loop. The stack trace would be a lot > easier to read (and more similar to a stack trace of an equivalent > synchronous program) if it looked like this: > > Traceback (most recent call last): > File "sample_client.py", line 616, in <module> > loop.run_until_complete(main_task) > ... > File "sample_client.py", line 319, in main > await actions[args.action](args, socket) > ... > File "sample_client.py", line 558, in sync_job > await asyncio.sleep(args.delay) > KeyboardInterrupt > > I recognize that the event loop mechanics are probably useful in some > circumstances, but in my experience working with asyncio for the last year, > I've found the stack traces are generally too noisy. For a while, I was > pasting stack traces into a text editor and cleaning them up by hand, but a > few months ago I wrote a custom excepthook that generates stack traces > similar to the one above. > > def async_excepthook(type_, exc, tb): > cause_exc = None > cause_str = None > > if exc.__cause__ is not None: > cause_exc = exc.__cause__ > cause_str = 'The above exception was the direct cause ' \ > 'of the following exception:' > elif exc.__context__ is not None and not exc.__suppress_context__: > cause_exc = exc.__context__ > cause_str = 'During handling of the above exception, ' \ > 'another exception occurred:' > > if cause_exc: > async_excepthook(type(cause_exc), cause_exc, > cause_exc.__traceback__) > > if cause_str: > print('\n{}\n'.format(cause_str)) > > print('Async Traceback (most recent call last):') > for frame in traceback.extract_tb(tb): > head, tail = os.path.split(frame.filename) > if (head.endswith('asyncio') or tail == 'traceback.py') and \ > frame.name.startswith('_'): > print(' ...') > continue > print(' File "{}", line {}, in {}' > .format(frame.filename, frame.lineno, frame.name)) > print(' {}'.format(frame.line)) > print('{}: {}'.format(type_.__name__, exc)) > > The meat of it is towards the bottom, "if > head.endswith('asyncio')..."There are a lot of debatable details and this > implementation is pretty hacky and clumsy, but I have found it valuable in > my own usage, and I haven't yet missed the omitted stack frames. > > I'm posting here to get constructive criticism on the concept and would > also like to hear if Curio or Trio have done anything like this. (Based on > a quick skim of the docs, I don't think they are.) I might publish a PyPI > package if anybody is interested. > > Cheers, > Mark > _______________________________________________ > Async-sig mailing list > Async-sig@python.org > https://mail.python.org/mailman/listinfo/async-sig > Code of Conduct: https://www.python.org/psf/codeofconduct/ > -- Thanks, Andrew Svetlov
_______________________________________________ Async-sig mailing list Async-sig@python.org https://mail.python.org/mailman/listinfo/async-sig Code of Conduct: https://www.python.org/psf/codeofconduct/