progshot is a debugging tool that enables "offline-debugging" for your python program.
pip install progshot
To capture a continuous run and be able to offline debug it like a real debugger, use @trace
from progshot import trace
def swap(arr, i, j):
arr[i], arr[j] = arr[j], arr[i]
@trace
def bubble_sort(arr):
n = len(arr)
for i in range(n - 1):
for j in range(n - i - 1):
if arr[j] > arr[j + 1]:
swap(arr, j, j + 1)
@trace
will record every line executed in the decorated function and generated a out.pshot
file when the program exits. With psviewer
, you can enjoy a debugger-like environment offline, where you can not only go forward, but go backward as well.
Each capture is called a film, which contains all the frames the local variables in them.
Or you can use with
to capture statements
from progshot import shoot # As in shoot a movie
with shoot(depth=3):
# some code here
By default, @trace
is not recursive, but you can set the depth
of @trace
from progshot import trace
def swap(arr, i, j):
# Now this function will be recorded as well
arr[i], arr[j] = arr[j], arr[i]
@trace(depth=2)
def bubble_sort(arr):
n = len(arr)
for i in range(n - 1):
for j in range(n - i - 1):
if arr[j] > arr[j + 1]:
swap(arr, j, j + 1)
You can also manually dump to a file in your program
from progshot import dump
for i in range(3):
arr = [random.randint(0, 100) for _ in range(10)]
bubble_sort(arr)
dump(filename=f"sort_{i}.pshot")
By default, dump
will clear the current data after dumping, you can pass clear_data=False
as an argument to prevent that.
To view the report, you can use Web interface or CLI.
psview out.pshot
psview-cli out.pshot
Web interface also provides a terminal which behaves like the CLI.
The CLI interface is similar to pdb. You can use commands that have the similar meanings in pdb, except that you have a set of "backward debugging" commands.
psview commands
- p expression - print eval of expression
- pp expression - pretty print eval of expression with
objprint
- w(here) - show stack trace
- u(p) [count] - move the current frame count levels up (to older frame)
- d(own) [count] - move the current frame count levels down (to later frame)
- n(ext) - go to next line in current function if possible, otherwise next film
- b(ack) - go to previous line in current function if possible, otherwise previous film
- s(tep) - go to next film
- s(tep)b(ack) - go to previous film
- r(eturn) - go to the next film when the current function returns
- r(eturn)b(ack) - go to the previous film before the current function enters
- unt(il) [lineno] - go forward until the line with a number that's >= lineno is reached
- unt(il)b(ack) [lineno] - go backward until the line with a number that's <= lineno is reached
- g(oto) [bookmark] - goto bookmark film. bookmark can be film index or film name
- l(ist) [lineno] - show source code around lineno
- ll - show full source code of existing frame
You can also use capture
function to do a single capture.
from progshot import capture
def add(a, b):
capture()
return a + b
You can give a name(bookmark) for the capture to switch to the film quickly
Do not use space in name
from progshot import capture
def add(a, b):
capture(name="cap_in_add")
return a + b
There are some global configs that you can change through config
.
from progshot import config
# Change the default dump file name
config(filename="other.pshot")
# Do not auto-save when the program exits
config(save_at_exit=False)
# Change default trace depth to 10
config(depth=10)
Please send bug reports and feature requests through github issue tracker.
Copyright Tian Gao, Mengjia Zhao, 2021.
Distributed under the terms of the Apache 2.0 license.