Skip to content

Commit

Permalink
Add snapshot feature to python_ta.debug (#983)
Browse files Browse the repository at this point in the history
  • Loading branch information
umututku03 authored Dec 2, 2023
1 parent 12deaad commit 9586a85
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

- Added new configuration option `use-pyta-error-messages` to let users choose whether PythonTA should overwrite pylint's error messages.
- Both PlainReporter and ColorReporter emphasize specific code chunks by using overline characters under any part that is highlighted as ERROR.
- Added snapshot function for deriving a list of dictionaries containing local variables from relevant functions and/or stack frames.
- Added new configuration option `allow-pylint-comments` to let users choose whether PythonTA should allow comments beginning with pylint: or not.
- `AccumulationTable` can now track variables initialized within the `for` loop. Prior, only variables initialized before the `for` loop could be tracked.
- `AccumulationTable` now stores deep copies of objects rather than shallow copies, thus fixing issues that come up in case of mutation during loop.
Expand Down
22 changes: 22 additions & 0 deletions python_ta/debug/snapshot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""
Use the 'inspect' module to extract local variables from
multiple stack frames. Useful for dynamic debugging.
"""
import inspect


def snapshot():
"""Capture a snapshot of local variables from the current and outer stack frames
where the 'snapshot' function is called. Returns a list of dictionaries,
each mapping function names to their respective local variables.
Excludes the global module context.
"""
local_vars = []
frame = inspect.currentframe().f_back

while frame:
if frame.f_code.co_name != "<module>":
local_vars.append({frame.f_code.co_name: frame.f_locals})
frame = frame.f_back

return local_vars
81 changes: 81 additions & 0 deletions tests/test_debug/test_accumulation_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import pytest

from python_ta.debug import AccumulationTable
from python_ta.debug.snapshot import snapshot


def test_one_accumulator() -> None:
Expand Down Expand Up @@ -396,3 +397,83 @@ def test_uninitialized_loop_accumulators() -> None:
with AccumulationTable(["i"]) as table:
for number in [10, 20, 30, 40, 50, 60]:
_ = number


# The functions below are for snapshot() testing purposes ONLY
def func1() -> list:
"""
Function for snapshot() testing.
"""
test_var1a = "David is cool!"
test_var2a = "Students Developing Software"
return snapshot()


def func2() -> list:
"""
Function for snapshot() testing.
"""
test_var1b = {"SDS_coolest_project": "PyTA"}
test_var2b = ("Aina", "Merrick", "Varun", "Utku")
return func1()


def func3() -> list:
"""
Function for snapshot() testing.
"""
test_var1c = []
for i in range(5):
test_var1c.append(i)

return func2()


def test_snapshot_one_level() -> None:
"""
Examines whether the snapshot() function accurately captures
the local variables of a singular function call,
devoid of any nested levels.
"""
local_vars = func1()

assert {
"func1": {"test_var2a": "Students Developing Software", "test_var1a": "David is cool!"}
} == local_vars[0]


def test_snapshot_two_levels() -> None:
"""
Evaluates the precision of the snapshot() function in capturing
local variables during a two-level nested function call.
"""
local_vars = func2()

assert {
"func1": {"test_var2a": "Students Developing Software", "test_var1a": "David is cool!"}
} == local_vars[0]
assert {
"func2": {
"test_var1b": {"SDS_coolest_project": "PyTA"},
"test_var2b": ("Aina", "Merrick", "Varun", "Utku"),
}
} == local_vars[1]


def test_snapshot_three_levels() -> None:
"""
Evaluates the precision of the snapshot() function in capturing
local variables during a three-level nested function call.
"""
local_vars = func3()

assert {
"func1": {"test_var2a": "Students Developing Software", "test_var1a": "David is cool!"}
} == local_vars[0]
assert {
"func2": {
"test_var1b": {"SDS_coolest_project": "PyTA"},
"test_var2b": ("Aina", "Merrick", "Varun", "Utku"),
}
} == local_vars[1]
assert {"func3": {"i": 4, "test_var1c": [0, 1, 2, 3, 4]}} == local_vars[2]

0 comments on commit 9586a85

Please sign in to comment.