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

fix(er): include nonlocals in snapshots [backport 2.19] #11902

Open
wants to merge 1 commit into
base: 2.19
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions ddtrace/debugging/_safety.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from inspect import CO_VARARGS
from inspect import CO_VARKEYWORDS
from itertools import chain
from types import FrameType
from typing import Any
from typing import Dict
Expand All @@ -23,11 +24,11 @@ def get_args(frame: FrameType) -> Iterator[Tuple[str, Any]]:

def get_locals(frame: FrameType) -> Iterator[Tuple[str, Any]]:
code = frame.f_code
_locals = frame.f_locals
nargs = code.co_argcount + bool(code.co_flags & CO_VARARGS) + bool(code.co_flags & CO_VARKEYWORDS)
names = code.co_varnames[nargs:]
values = (frame.f_locals.get(name) for name in names)

return zip(names, values)
return (
(name, _locals.get(name)) for name in chain(code.co_varnames[nargs:], code.co_freevars, code.co_cellvars)
) # include freevars and cellvars


def get_globals(frame: FrameType) -> Iterator[Tuple[str, Any]]:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
fixes:
- |
exception replay: include missing nonlocal variables in snapshot log messages.
20 changes: 20 additions & 0 deletions tests/debugging/exception/test_replay.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,3 +294,23 @@ def c(foo=42):
self.assert_span_count(6)
# no new snapshots
assert len(uploader.collector.queue) == 3

def test_debugger_exception_in_closure(self):
def b():
with self.trace("b"):
nonloc = 4

def a(v):
if nonloc:
raise ValueError("hello", v)

a(nonloc)

with exception_replay() as uploader:
with with_rate_limiter(RateLimiter(limit_rate=1, raise_on_exceed=False)):
with pytest.raises(ValueError):
b()

assert all(
s.line_capture["locals"]["nonloc"] == {"type": "int", "value": "4"} for s in uploader.collector.queue
)
5 changes: 4 additions & 1 deletion tests/debugging/test_safety.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ def assert_args(args):
assert set(dict(_safety.get_args(inspect.currentframe().f_back)).keys()) == args

def assert_locals(_locals):
assert set(dict(_safety.get_locals(inspect.currentframe().f_back)).keys()) == _locals
assert set(dict(_safety.get_locals(inspect.currentframe().f_back)).keys()) == _locals | {
"assert_args",
"assert_locals",
}

def assert_globals(_globals):
assert set(dict(_safety.get_globals(inspect.currentframe().f_back)).keys()) == _globals
Expand Down
Loading