classification
Title: Tracebacks are unnecessarily verbose when using 'python -m'
Type: enhancement Stage: needs patch
Components: Library (Lib) Versions: Python 3.7
process
Status: open Resolution:
Dependencies: 31299 Superseder:
Assigned To: Nosy List: Arfrever, brett.cannon, eric.snow, ezio.melotti, mattheww, ncoghlan, vaultah
Priority: normal Keywords:

Created on 2012-10-13 12:51 by mattheww, last changed 2017-08-29 09:27 by ncoghlan.

Messages (8)
msg172806 - (view) Author: Matthew Woodcraft (mattheww) Date: 2012-10-13 12:51
If I run my code using 'python -m' and there is an unhandled exception, the tracebacks include lines from runpy.py (and now sometimes from importlib._bootstrap) which don't provide useful information, and tend to overwhelm the valuable part of the traceback.

I suppose this is in some sense a regression from Python 2.4.


echo fail > fail.py
python3.3 -m fail

python -m fail
Traceback (most recent call last):
  File "/usr/lib/python3.3/runpy.py", line 160, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib/python3.3/runpy.py", line 73, in _run_code
    exec(code, run_globals)
  File "./fail.py", line 2, in <module>
    fail
NameError: name 'fail' is not defined

Here I think it would be better to omit the first two traceback entries.


Syntax errors are worse:
echo syntax error > fail.py
python3.3 -m fail

Traceback (most recent call last):
  File "/usr/lib/python3.3/runpy.py", line 140, in _run_module_as_main
    mod_name, loader, code, fname = _get_module_details(mod_name)
  File "/usr/lib/python3.3/runpy.py", line 114, in _get_module_details
    code = loader.get_code(mod_name)
  File "<frozen importlib._bootstrap>", line 981, in get_code
  File "<frozen importlib._bootstrap>", line 313, in _call_with_frames_removed
  File "./fail.py", line 1
    syntax error

Here there are four traceback entries which could be omitted.
msg172808 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2012-10-13 14:52
Agreed. We do have a mechanism in place to deal with this specifically for importlib in 3.3 (which is why the first traceback is cleaner, although it's not triggering in the SyntaxError case for some reason), but it makes sense to hide the noise from runpy as well.

Flagging 3.3 for the moment, but if the change is too intrusive it will end up being 3.4 only.
msg245898 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2015-06-28 03:23
Hmm, when I haven't moved an issue forward in over two years, I guess that's a fair sign I'm not *actually* working on it...
msg300711 - (view) Author: Dmitry Kazakov (vaultah) * Date: 2017-08-22 16:43
In order to load and compile the module code, runpy calls the loader's get_code method. Because that happens outside of the normal import process, and PyImport_ImportModuleLevelObject is currently the only place where remove_importlib_frames is being invoked, tracebacks of exceptions occurring in get_code are kept unmodified.

Would it be wrong to drop all importlib frames from all unhandled exceptions, except when the -v flag is present? On the other hand, since the patch from #issue15486 seems to work fine in most scenarios, I propose removing both runpy and importlib traceback entries locally inside the runpy module. There may be better solutions, though...

In any case, I'm willing to write a patch.
msg300742 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-08-23 05:26
The reason we don't always drop the importlib frames in general is because we need them when we're debugging importlib itself, as well as when folks are calling into it directly. However, I think it would be reasonable to do it unconditionally for unhandled exceptions triggered via "-m": if we want the unfiltered exception in that case, we can either edit runpy to skip the filtering (when working on runpy), or else call the runpy API directly, rather than via -m.

That means I like the idea of implementing the traceback filtering and display inside runpy._run_module_as_main itself, rather than relying on the default interpreter level traceback display.

One potential approach to that would be to expand the current code that suppresses tracebacks entirely for runpy._Error exceptions to also handle the "except Exception as exc:" case and print out a traceback variants that omits any frames from runpy, importlib, or _frozen_importlib (KeyboardInterrupt and SystemExit would still escape unmodified)

As far as the implementation goes, this could potentially be made a general feature of the new(ish) TracebackException https://docs.python.org/3/library/traceback.html#tracebackexception-objects class by accepting an "ignore_modules" iterable as a parameter to TracebackException.format() and then ignoring frames running code from those modules (conveniently, runpy, importlib, and _frozen_importlib between them will exercise the cases of a single-file module, a self-contained package, and a frozen module, so only namespace package support would end up relying entirely on synthetic test cases).
msg300959 - (view) Author: Dmitry Kazakov (vaultah) * Date: 2017-08-28 18:25
Since runpy also handles the execution of directories and zip files, I think it would be reasonable to clean up tracebacks in these cases, too.

The ignore_modules argument would be a great addition to the traceback module. Should I create a separate issue/pull request for its implementation?
msg300975 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-08-29 07:46
Aye, separating the two discussions is a good idea. I've filed https://bugs.python.org/issue31299 for the traceback RFE, and added it as a dependency here.
msg300978 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2017-08-29 09:27
Regarding directories and zipfiles, runpy._run_module_as_main also handles those - the CLI effectively transforms directory and zipfile execution into a "python -m __main__" call with a suitably adjusted sys.path[0] entry (i.e. the given directory or zip archive, rather than the current directory).
History
Date User Action Args
2017-08-29 09:27:06ncoghlansetmessages: + msg300978
2017-08-29 07:46:41ncoghlansetdependencies: + Add "ignore_modules" option to TracebackException.format()
messages: + msg300975
2017-08-28 18:25:21vaultahsetmessages: + msg300959
2017-08-23 05:26:26ncoghlansetmessages: + msg300742
versions: + Python 3.7, - Python 3.3, Python 3.4
2017-08-22 16:43:56vaultahsetnosy: + vaultah
messages: + msg300711
2015-06-28 03:23:27ncoghlansetassignee: ncoghlan ->
messages: + msg245898
2014-12-12 01:13:42Arfreversetnosy: + Arfrever
2012-11-13 07:02:53eric.snowsetnosy: + eric.snow
2012-10-25 16:36:05ezio.melottisetnosy: + ezio.melotti

components: + Library (Lib)
stage: needs patch
2012-10-13 14:52:32ncoghlansetversions: + Python 3.3, Python 3.4
nosy: + brett.cannon

messages: + msg172808

assignee: ncoghlan
2012-10-13 13:31:42pitrousetnosy: + ncoghlan
2012-10-13 12:51:41matthewwcreate