Skip to main content

pystuck

pystuck.py is a utility for analyzing stuck python programs (or just hardcore debugging).

pystuck currently has two major features:

  1. print all running threads’ stack traces.

  2. remote inspection of modules and variables without interrupting the program.

in the debugged script: import pystuck; pystuck.run_server()

to invoke the client: invoke pystuck from the shell.

Dependancies

  • IPython

  • rpyc(3.2.3, may work with others)

Droduction Use

pystuck doesn’t consume resources when no client is connected to it.

all the run_server function does is spawn a thread that blocks on accept (waiting for clients to connect), so it can be used in production.

there are two drawbacks for using pystuck in production:

  1. meddling with variables and modules is not thread safe.

  2. a potential security breach - nothing prevents an unprivilidged user connect to a privlidged running python process and use the remote access to do practically anything.

Install

pip install pystuck

Examples

What is python doing?!

test.py is stuck, wouldn’t you just die to know where?

import pystuck; pystuck.run_server().

while True:
    with lock: # could block
         sock.recv(1024) # could block

running pystuck from the shell shows interesting stuff:

% pystuck
<_MainThread(MainThread, started -1215396160)>
  File "test.py", line 9, in <module>
    with lock: # could block

...

it’s stuck waiting for the lock!. it actually prints two more threads that are related to pystuck, ignore them.

Who’s got the lock?!

# it seldom happens that a thread doesn't release the lock or is stuck while holding it.
# we want to know which thread... bear with me now.
rlock.acquire()

invoking pystuck again:

% pystuck
<_MainThread(MainThread, started -1215396160)>
  File "test.py", line 9, in <module>
    with lock: # could block

<Thread(Thread-1, started -1219450000)>
  File "test.py", line 12, in <module>
    do_math()

<Thread(Thread-2, started -1219540000)>
  File "test.py", line 14, in <module>
    foo()
  File "test.py", line 20, in foo
    do_network()

use the 'modules' dictionary to access remote modules (like 'os', or '__main__')

In [0]: modules['sys']._current_frames() # gets a mapping between a thread to its frame (top of stack)
Out[0]: {-1215396160: <frame object at 0x8a07154>,
         -1219450000: <frame object at 0x8a29154>,
         -1219540000: <frame object at 0x8b39154>}

In [1]: _[-1215396160] # get the stuck thread's frame
Out[1]: <frame object at 0x8a07154>

In [2]: _.f_locals # local variables of the function
Out[2]: {'lock': <_RLock owner=-1219450000 count=1>}

In [3]: # our stuck thread is probably waiting for thread 1219450000 to finish do_math.. figures

What rpyc can do for you

rpyc is the library used to communicate python objects and procedure calls across processes. here are some of the things you can do to the already running server.

% pystuck

(stacks appear here...)

In [1]: modules['sys'].stdout = file("/tmp/log.txt", "w") # tunnel the script's stdout to log

In [2]: modules['__main__'].global_var = 100 # change and inspect variables in the __main__ module (the name of the script when invoked like this: python script.py)

In [3]: socket = modules['socket'].socket() # create a socket object opened by the server script!

Usage

usage: pystuck [-h] [--no-stacks] [--no-ipython] [host] [port]

pystuck.py is a utility for analyzing stuck python programs (or just hardcore debugging).

in order to debug a python program (hence, the debugee),
add this line anywhere at startup: import pystuck; pystuck.run_server().

this script is the client, once invoked it connects to the debuggee
and prints the debugee's threads stack traces (good for most cases).
in addition, it opens an ipython prompt with an rpyc connection that provides
access to the debuggee's modules (good for inspecting variables).
positional arguments:

  host          server address (default: 127.0.0.1)
  port          server port (default: 6666)

optional arguments:
  -h, --help    show this help message and exit
  --no-stacks   don't print the debugee's threads and stacks
  --no-ipython  don't open an ipython prompt for debugging

Metadata

Release files for pystuck 0.8.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pystuck 0.8.5
File Size Uploaded Provenance Actions
pystuck-0.8.5.tar.gz 5.6 kB Without provenance Details for pystuck-0.8.5.tar.gz

Release files / pystuck-0.8.5.tar.gz

Download URL pystuck-0.8.5.tar.gz
Size 5.6 kB
Tags Source
SHA-256 checksum
How to use checksums
f5bd1dad6db82d3065caa9e9596fbd22efbb9be66a9e31a5f541f36fbd55555c
BLAKE2b-256 checksum
How to use checksums
4a6ecb4730caaaf2ed64bf751f07c42854313d4787ff3c60122dc1a73e58bbb6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No

Security for pystuck 0.8.5

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page