Skip to content
Merged
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
1 change: 1 addition & 0 deletions packages/python-ta/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Added `LSPReporter`, a new reporter that outputs lint diagnostics in LSP 3.17-compliant JSON format.
- Added suggested fixes for pascal and uppercase names in `invalid_name_checker.py`
- Updated `invalid_name_checker.py` to include a suggested fix for invalid names in checks using snake_case format.
- Updated `SnapshotTracer` to convert snapshots to JSON with `snapshot_to_json` and pass JSON data to the webstepper template instead of SVG.

### 💫 New checkers

Expand Down
6 changes: 2 additions & 4 deletions packages/python-ta/docs/debug/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -453,14 +453,12 @@ The following section will focus on tracing the Python memory model. This featur
from python_ta.debug import SnapshotTracer


def func_multi_line(output_directory: str = None) -> None:
def func_multi_line() -> None:
"""
Function for testing SnapshotTracer
"""
with SnapshotTracer(
output_directory=output_directory,
include_frames=("func_multi_line",),
exclude_vars=("output_directory",),
memory_viz_args=MEMORY_VIZ_ARGS,
):
num = 123
Expand All @@ -473,7 +471,7 @@ if __name__ == '__main__':
func_multi_line()
```

When this function runs, the variables within `func_multi_line` are captured, and memory models are outputted to `output_directory` for each line of code. For the expected output, refer to the snapshots in `tests/test_debug/snapshot_tracer_testing_snapshots/func_multi_line`.
When this function runs, MemoryViz snapshots are captured and stored internally in the `SnapshotTracer`. If `webstepper=True`, the snapshots are rendered into an HTML visualization. For the expected output, refer to the snapshots in `tests/test_debug/snapshot_tracer_testing_snapshots/func_multi_line`.

### API

Expand Down
43 changes: 11 additions & 32 deletions packages/python-ta/src/python_ta/debug/snapshot_tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
import base64
import copy
import inspect
import json
import logging
import os
import socket
import sys
import warnings
from pathlib import Path
from typing import TYPE_CHECKING, Any, Optional

from jinja2 import Environment, FileSystemLoader

from ..util.servers.one_shot_server import open_html_in_browser
from .id_tracker import IDTracker
from .snapshot import snapshot
from .snapshot import snapshot, snapshot_to_json

if TYPE_CHECKING:
import types
Expand All @@ -26,16 +26,14 @@ class SnapshotTracer:
A class used for snapshot-based debugging to visualize program memory at each line in the calling function.

Instance attributes:
output_directory: The directory where the memory model diagrams will be saved. Defaults to the current directory.
webstepper: Opens the web-based visualizer.
_snapshots: A list of dictionaries that maps the code line number and the snapshot number.
_snapshot_args: A dictionary of keyword arguments to pass to the `snapshot` function.
_first_line: Line number of the first line in the `with` block.
"""

output_directory: Optional[str]
webstepper: bool
_snapshots: list[dict[int, int]]
_snapshots: list[dict[str, Any]]
_snapshot_args: dict[str, Any]
_first_line: int

Expand All @@ -48,26 +46,20 @@ def __init__(
"""Initialize a context manager for snapshot-based debugging.

Args:
output_directory: The directory to save the snapshots, defaulting to the current directory.
**Note**: Use this argument instead of the `--output` flag in `memory_viz_args` to specify the output directory.
The directory will be created if it does not exist.
output_directory: This argument is deprecated; previously used for file-based outputs.
webstepper: Opens a MemoryViz Webstepper webpage to interactively visualize the resulting memory diagrams.
**kwargs: All other keyword arguments are passed to `python.debug.snapshot`. Refer to the `snapshot` function for more details.
"""
if sys.version_info < (3, 10, 0):
logging.warning("You need Python 3.10 or later to use SnapshotTracer.")
if any("--output" in arg for arg in kwargs.get("memory_viz_args", [])):
raise ValueError(
"Use the output_directory parameter to specify a different output path."
)
if output_directory is not None:
warnings.warn("The output_directory argument is deprecated.", DeprecationWarning)
self._snapshots = []
self._snapshot_args = kwargs
self._snapshot_args["memory_viz_args"] = copy.deepcopy(kwargs.get("memory_viz_args", []))
self._snapshot_args["exclude_frames"] = copy.deepcopy(kwargs.get("exclude_frames", []))
self._snapshot_args["exclude_frames"].append("_trace_func")
self.output_directory = os.path.abspath(output_directory if output_directory else ".")
self.id_tracker = IDTracker()
Path(self.output_directory).mkdir(parents=True, exist_ok=True)

self.webstepper = webstepper
self._first_line = float("inf")
Expand All @@ -77,28 +69,15 @@ def _trace_func(self, frame: types.FrameType, event: str, _arg: Any) -> None:
if self._first_line == float("inf"):
self._first_line = frame.f_lineno
if event == "line":
filename = os.path.join(
self.output_directory,
f"snapshot-{len(self._snapshots)}.svg",
)
self._snapshot_args["memory_viz_args"].extend(["--output", filename])

snapshot(
snapshot_output = snapshot(
id_tracker=self.id_tracker,
save=True,
**self._snapshot_args,
)

self._add_svg_to_map(filename, frame.f_lineno)

def _add_svg_to_map(self, filename: str, line: int) -> None:
"""Add the SVG in filename to self._snapshots"""
with open(filename) as svg_file:
svg_content = svg_file.read()
json_data = snapshot_to_json(snapshot_output, id_tracker=self.id_tracker)
self._snapshots.append(
{
"lineNumber": line,
"svg": svg_content,
"lineNumber": frame.f_lineno,
"memoryVizInput": json_data,
}
)

Expand Down Expand Up @@ -144,7 +123,7 @@ def _build_self_contained_html(self, func_frame: types.FrameType) -> bytes:

rendered_html = template.render(
code_text=self._get_code(func_frame),
svg_array=self._snapshots,
memory_viz_data=self._snapshots,
bundle_content=bundle_content,
)

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 19 additions & 19 deletions packages/python-ta/src/python_ta/debug/webstepper/index.bundle.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<div id="root"></div>
<script>
window.codeText = `{{ code_text }}`;
window.svgArray = {{ svg_array | tojson }};
window.memoryVizData = {{ memory_viz_data | tojson }};
</script>
<script>
// Two hacks here:
Expand Down
Loading