classification
Title: Expose `capture_locals` parameter in `traceback` convenience functions
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.11
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: fhackdroid, iritkatriel, r.david.murray, ulope
Priority: normal Keywords: patch

Created on 2018-06-08 15:52 by ulope, last changed 2021-09-16 18:04 by iritkatriel. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 24231 merged iritkatriel, 2021-01-16 18:57
Messages (14)
msg319079 - (view) Author: Ulrich Petri (ulope) * Date: 2018-06-08 15:52
Since 3.5 the internal machinery of the `traceback` module has gained the very useful ability to capture locals.

It would be useful to also expose that ability through the various convenience functions.
msg319809 - (view) Author: Farhaan Bukhsh (fhackdroid) * Date: 2018-06-17 08:47
Hey, I would like to work on this, where do I start or how can I help?
msg320176 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2018-06-21 13:11
I think we should get one or more of the core devs who were involved in the changes that added that parameter to sign off on whether this is a good idea or not (I have no opinion at the moment).  You should be able to find them via git blame and looking at the issues related to the changesets you find.  Unless someone who remembers comes along and just adds them to nosy :)
msg320191 - (view) Author: Farhaan Bukhsh (fhackdroid) * Date: 2018-06-21 15:33
Hahah! Let me try to put this on the IRC channel may be someone can help us there.
msg385028 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2021-01-13 14:51
I don't understand this request. If you ask TracebackExceptions to capture_locals then the FrameSummary object gets a dict of the reprs of the locals, which can be introspected and participate in __eq__ checks but nothing else happens with them. What would the convenience functions do with this parameter?

Ulrich, can you explain the use case or how you see this working?
msg385029 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2021-01-13 14:53
Sorry, I spoke too soon - see now that the locals are use in the StackSummary.format().
msg385030 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2021-01-13 14:57
That said, you can always use 

TracebackException.from_exception(exc, capture_locals=True).format()

which is not much longer than 

print_exception(exc, capture_locals=True)
msg385055 - (view) Author: Ulrich Petri (ulope) * Date: 2021-01-13 21:52
Functionally equivalent code would be:

print("".join(TracebackException.from_exception(ex, capture_locals=True).format()))

vs. (hypothetically)

print_exc(capture_locals=True)

Which is quite a significant difference IMO.
msg385056 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2021-01-13 22:21
I’m not sure I agree that it’s a big win.
You can always add such a utility function in your code.
msg385089 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2021-01-14 23:14
On the other hand, I think it makes sense to add a print() method to TracebackException so that you can do

TracebackException.from_exception(ex, capture_locals=True).print(file)

or whatever other combination of current or future params.
msg385106 - (view) Author: Ulrich Petri (ulope) * Date: 2021-01-15 11:09
That would make it slightly better, but I still think it's a pretty arcane incantation (esp. for newer people).

What makes you hesitant to adding the parameter to the convenience functions?
msg385108 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2021-01-15 11:24
Generally speaking, I don't think it's a good idea to create redundant APIs. If we copy all the params from TracebackException (and it will be all params, why only this one?) that means more code, more tests, more documentation and a higher cognitive load for someone reading the documentation to learn the API.  And the users don't really get anything tangible in return - they can do exactly the same things, maybe with a slightly smaller number of characters.  A good standard library API has a small number of buttons that can be used in combination to get a lot of functionality.

In this particular case, I think it's nice that print_exception() gives novices a simple way to get some basic functionality, and more sophisticated users can go to TracebackException for more options. I agree with you that at the moment it's a bit clumsy, and a print() method would make it more usable. Note that a programmer who understands the different parts of a traceback and wants to tweak its representation is not a novice and should be able to use that with ease.
msg401975 - (view) Author: Ulrich Petri (ulope) * Date: 2021-09-16 17:41
> If we copy all the params from TracebackException (and it will be all params, why only this one?)

Why expose the internal machinery at all?
If all additional parameters (and unless I'm miscounting we're talking about 2) were exposed on the convenience functions why add to the developers mental load with the underlying implementation?

> more documentation and a higher cognitive load for someone reading the documentation to learn the API.

I would agree if the API in question was ergonomic and the documentation easy to read in the first place. I think neither is true for the traceback module and its documentation.

Another example from today where I helped a coworker who was unable to figure out on his own how to print the current stack with locals:


  from traceback import StackSummary, walk_stack
  
  print("".join(StackSummary.extract(walk_stack(None), capture_locals=True).format()))

There are multiple things that make this API non-intuitive:
- Why does walk_stack() need an explicit None argument?
- Why is StackSummary created via the extract() classmethod instead of having a regular __init__?
- Why can't extract() be smart if no iterator is passed and figure out on it's own if we're in an exception context or not and call walk_stack / walk_tb accordingly?
- Why is the result a list if each item can contain multiple internal newlines?
msg401976 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2021-09-16 18:04
Feel free to create a new issue and propose a patch.
History
Date User Action Args
2021-09-16 18:04:41iritkatrielsetmessages: + msg401976
2021-09-16 17:41:55ulopesetmessages: + msg401975
2021-05-22 16:41:50iritkatrielsetstatus: open -> closed
resolution: fixed
stage: patch review -> resolved
2021-05-12 22:55:29iritkatrielsetversions: + Python 3.11, - Python 3.10
2021-01-16 18:57:44iritkatrielsetkeywords: + patch
stage: patch review
pull_requests: + pull_request23054
2021-01-15 11:24:59iritkatrielsetmessages: + msg385108
versions: + Python 3.10, - Python 3.8
2021-01-15 11:09:31ulopesetmessages: + msg385106
2021-01-14 23:14:10iritkatrielsetmessages: + msg385089
2021-01-13 22:21:06iritkatrielsetmessages: + msg385056
2021-01-13 21:52:36ulopesetmessages: + msg385055
2021-01-13 14:57:29iritkatrielsetmessages: + msg385030
2021-01-13 14:53:49iritkatrielsetmessages: + msg385029
2021-01-13 14:51:09iritkatrielsetnosy: + iritkatriel
messages: + msg385028
2018-06-21 15:33:09fhackdroidsetmessages: + msg320191
2018-06-21 13:11:26r.david.murraysetversions: - Python 3.5, Python 3.6, Python 3.7
nosy: + r.david.murray

messages: + msg320176

type: enhancement
2018-06-17 08:47:05fhackdroidsetnosy: + fhackdroid
messages: + msg319809
2018-06-08 15:52:20ulopecreate