Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support local variables (tb.tb_frame.f_locals) #41

Closed
iankharris opened this issue May 28, 2019 · 11 comments
Closed

Support local variables (tb.tb_frame.f_locals) #41

iankharris opened this issue May 28, 2019 · 11 comments

Comments

@iankharris
Copy link

iankharris commented May 28, 2019

I'm sure there's a reason for this, but it seems that Traceback objects don't copy the f_locals dictionary from the traceback frame. It would be nice to have this so that, for instance, reraised exceptions can be better introspected in an IDE.

For instance:

>>> def func():
...     a = 1
...    print locals()
...     try:
...         raise ValueError()
...     except Exception as e:
...         exc_type, exc_value, exc_tb = sys.exc_info()
>>> exc_tb.tb_frame.f_locals
{'a': 1, e: ValueError()}
>>> tb = Traceback(exc_tb)
>>> tb.as_traceback().tb_frame.f_locals
{}

There might be something obvious here that I'm missing?

@ionelmc
Copy link
Owner

ionelmc commented May 29, 2019

f_locals are a can of worms basically, we'd have lots of corner cases like reference loops, unpickleable stuff, overblown pickle result size and such. I'm not saying no but I'm not a fan of the idea either - the primary goal of this library is to be able to display a traceback using stdlib modules (like traceback) from a different process.

@iankharris
Copy link
Author

I understand. An easy solution might be to just assign all unpickleable or otherwise problematic f_locals to None and be done with it. I'm not sure how we can detect reference loops but I haven't done much work on code like this.

@ionelmc
Copy link
Owner

ionelmc commented May 30, 2019

Btw, previous code and discussion on exactly what you asked: #6 - I guess you could try it and report if it works in your IDE?

@purplesyringa
Copy link
Contributor

@ionelmc Do you think we could do the following: we could only pickle simple objects (say, primitives and list/dict) to avoid space issues.

The problem is: gevent uses tblib to save exceptions when they are thrown in another thread to get rid of locals, but pytest uses f_locals for traceback. This means that gevent and pytest don't work together. Pickling primitives shouldn't increase refcount or affect performance badly but will make debugging waaay easier.

@ionelmc
Copy link
Owner

ionelmc commented Aug 26, 2021

The changes in #59 made by @marcoffee seem fine to me (some tests needed tho, and some docs explaining that repr will be used, and it can create side-effects). Would those changes solve this/the problems on gevent? I'm not a big gevent user.

@davegravy
Copy link

davegravy commented Feb 5, 2023

Am trying to use tblib to pickle tracebacks in an application with spotty internet so I can later send to Sentry via sentry_sdk.capture_exception() in a retry process. Currently all the local variables are absent and this feature would be really handy even if it only supported primitives.

Is there anything other than docs and tests blocking #59?

@ionelmc
Copy link
Owner

ionelmc commented Jun 22, 2023

So what I would like to have here is some sort of parametrization in tblib.pickle_support's pickle_traceback and install. I think that ideally install would take an option/hook to customize the locals attribute (so the user decides what ends up in locals via his hook). Then I won't need to have a hack in tblib for some pytest thingy or whatever.

Eg:

from tblib import pickling_support

def pytest_locals(frame):
    if "__tracebackhide__" in frame.f_locals:
        return {"__tracebackhide__": bool(frame.f_locals["__tracebackhide__"])}
    else:
        return {}

pickling_support.install(pickle_locals=pytest_locals)

@hoodmane
Copy link

Yes, this proposal sounds great.

@ionelmc
Copy link
Owner

ionelmc commented Oct 21, 2023

For your use-case it's going to be roughly like:

from tblib import pickling_support

def get_pytest_locals(frame):
    if frame.f_locals.get("__tracebackhide__"):
        return {"__tracebackhide__": True}
    else:
        return {}

pickling_support.install(get_locals=get_pytest_locals)

@hoodmane
Copy link

Thanks!

@ionelmc
Copy link
Owner

ionelmc commented Oct 22, 2023

Released 3.0

hoodmane added a commit to pyodide/pytest-pyodide that referenced this issue Nov 17, 2023
tblib 3.0 has been released with support for pickling frame locals:
ionelmc/python-tblib#41
With this PR, pytes-pyodide attempts to use the new tblib 3.0 feature, with a
fallback for older Pyodide versions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants