Data Race Detector for Free-Threading Python
Project description
threadcheck
Python data race detector for the free-threading (no-GIL) era. Detects concurrent access to shared mutable state in multi-threaded Python programs through static analysis and runtime instrumentation.
Problem
Python 3.14 (2026) introduces free-threading, removing the Global Interpreter Lock (GIL). This enables true parallel execution of multi-threaded code, but the ecosystem lacks debugging tools for concurrency bugs. Go has -race, C++ has ThreadSanitizer, Java has SpotBugs. Python has nothing comparable without recompiling the interpreter with Clang and TSan.
threadcheck is a pure-Python race detector that installs with pip and works out of the box.
Features
- Static analysis -- scans AST for shared mutable state (global, nonlocal, class attributes) and missing lock protection
- Runtime detection -- instruments code via AST transformation at import time; tracks memory accesses with vector clocks and detects happens-before violations
- Lock-aware suppression -- understands
threading.Lock,threading.RLock, andwith-based synchronization; raises confidence when locks are missing and suppresses warnings when they are present - Confidence scoring -- each warning tagged HIGH / MEDIUM / LOW based on thread context and lock coverage
- CLI tool -- single-command static scan or instrumented execution
- JSON and SARIF output -- suitable for CI/CD pipeline integration
Installation
pip install threadcheck
Requires Python 3.12+. Python 3.14+ is recommended for free-threading features.
Quick Start
Static Analysis
Scan a file or directory for potential race conditions without running any code:
threadcheck scan my_project/
Output:
[WARNING] [HIGH] [unsafe_global] my_project/counter.py:8:8
Global variable `counter` modified without lock in thread
Suggestion: use `threading.Lock()` to protect access
JSON output:
threadcheck scan my_project/ --json -o report.json
Runtime Detection
Execute a script with instrumentation to detect actual data races:
threadcheck run my_script.py
Output for a racing script:
Data races detected:
[!] `counter`
Thread-28928 (write) at my_script.py:8
Thread-9888 (write) at my_script.py:8
A script protected with locks reports:
No data races detected
CI Integration
pip install threadcheck
threadcheck scan src/ --json -o threadcheck_report.json
Commands
| Command | Description | Status |
|---|---|---|
scan <path> |
Static race analysis of file or directory | Stable |
run <script> |
Execute script with runtime race detection | Beta |
check-compat <path> |
Free-threading compatibility check | Planned |
Library Usage
from threadcheck import analyze_file, analyze_path
warnings = analyze_file("my_module.py")
warnings = analyze_path("src/")
for w in warnings:
print(f"{w.file}:{w.line} [{w.confidence.value}] {w.message}")
from threadcheck.dynamic.tracker import ThreadCheckTracker
from threadcheck.dynamic.transform import transform_and_compile
code = transform_and_compile(source, "script.py")
ThreadCheckTracker.start()
exec(code, {"_threadcheck_tracker": ThreadCheckTracker})
ThreadCheckTracker.stop()
print(ThreadCheckTracker.format_races())
ThreadCheckTracker.reset()
Architecture
Static Analysis Pipeline
- Parse source into AST
- Identify shared mutable state: globals, nonlocals, class attributes (
self.x), module-level mutable objects - Detect thread creation sites (
threading.Thread) - Cross-reference with lock usage (
with lock:,lock.acquire()) - Assign confidence: HIGH (thread target, no lock), MEDIUM (thread present, no lock), LOW (suspicious pattern, no thread context)
- Report findings with repair suggestions
Runtime Detection Pipeline
- Parse source into AST
- Identify shared variables per function scope
- Transform AST: inject
write_before(),lock_acquire(),lock_release()calls around shared variable accesses - Compile and execute transformed code under a tracker that maintains per-thread vector clocks
- On lock acquire, synchronize clocks (happens-before merge)
- After execution, scan access log for conflicting operations (concurrent writes or write-read pairs with no happens-before relationship)
- Report detected races with thread IDs and source locations
Project Structure
threadcheck/
├── pyproject.toml
├── src/
│ └── threadcheck/
│ ├── __init__.py
│ ├── __main__.py
│ ├── _version.py # single version source
│ ├── cli.py # argument parsing + dispatch
│ ├── static/
│ │ ├── analyzer.py # static analysis entry point
│ │ ├── visitors.py # AST visitors (global, nonlocal, class attr, shared mutable)
│ │ ├── lock_tracker.py # lock usage analysis
│ │ └── models.py # RaceWarning, Severity, Confidence models
│ ├── dynamic/
│ │ ├── __main__.py # run_script entry point
│ │ ├── transform.py # AST transformation engine
│ │ ├── tracker.py # runtime tracker with vector clocks
│ │ ├── clock.py # vector clock implementation
│ │ └── hook.py # sys.meta_path import hook
│ ├── reporting/
│ │ ├── formatter.py # terminal output formatting
│ │ └── types.py # type re-exports
│ └── pytest_plugin.py # pytest integration (planned)
├── tests/
│ ├── fixtures/ # sample code with known races
│ ├── test_static_analyzer.py
│ └── test_dynamic_detector.py
└── README.md / README_CN.md
Roadmap
| Phase | Feature | Status |
|---|---|---|
| 1 | CLI, static analysis (globals/nonlocals) | Done |
| 2 | Class attributes, lock suppression, confidence scoring | Done |
| 3 | AST import hook, runtime instrumentation, vector clocks | Done |
| 4 | Race report deduplication, enhanced happens-before analysis | Planned |
| 5 | SARIF output, JSON reporting | Planned |
| 6 | pytest plugin | Planned |
| 7 | Free-threading compatibility checker | Planned |
Limitations
- Static analysis may produce false positives (reports race that cannot occur at runtime) and false negatives (misses races that involve indirect sharing through aliases or containers)
- Runtime detection modifies the AST before execution; code that introspects its own source or frame objects may behave differently
- Runtime instrumentation incurs overhead (approximately 2-5x slowdown for typical code)
- Lock tracking supports
threading.Lock,threading.RLock, and standardwith-based patterns; other synchronization primitives (threading.Event,threading.Condition, third-party libraries) are not tracked
License
MIT
Contributing
Contributions are welcome. Please open an issue or submit a pull request on GitHub.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file threadcheck-0.0.1.1.tar.gz.
File metadata
- Download URL: threadcheck-0.0.1.1.tar.gz
- Upload date:
- Size: 34.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
514fe0f139e90eecd01a6fa8d8ca7faa7139aecd3c8b95746cd2a50ff40329a4
|
|
| MD5 |
d2189d2d22661e30d7592d15ea2f8ad9
|
|
| BLAKE2b-256 |
cb527987df92508f0686314dfe949d877cb5e263235486eea7e28623a50a1273
|
Provenance
The following attestation bundles were made for threadcheck-0.0.1.1.tar.gz:
Publisher:
release.yml on ChidcGithub/Threadcheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
threadcheck-0.0.1.1.tar.gz -
Subject digest:
514fe0f139e90eecd01a6fa8d8ca7faa7139aecd3c8b95746cd2a50ff40329a4 - Sigstore transparency entry: 1761312876
- Sigstore integration time:
-
Permalink:
ChidcGithub/Threadcheck@06264d0561b7053875a53797a29effc7aec6e413 -
Branch / Tag:
refs/tags/v0.0.1.1 - Owner: https://github.com/ChidcGithub
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@06264d0561b7053875a53797a29effc7aec6e413 -
Trigger Event:
push
-
Statement type:
File details
Details for the file threadcheck-0.0.1.1-py3-none-win_amd64.whl.
File metadata
- Download URL: threadcheck-0.0.1.1-py3-none-win_amd64.whl
- Upload date:
- Size: 26.1 kB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c7560e0bebb2eb79eda0adb5a65df9cff27cb453720570c2d8f45190ca1defd
|
|
| MD5 |
ceee193f8a9afca6fee666c138b98d50
|
|
| BLAKE2b-256 |
6ac60707a79079582740562bafa709940d98b6e54bf3504fde9aaebd8f748fb2
|
Provenance
The following attestation bundles were made for threadcheck-0.0.1.1-py3-none-win_amd64.whl:
Publisher:
release.yml on ChidcGithub/Threadcheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
threadcheck-0.0.1.1-py3-none-win_amd64.whl -
Subject digest:
1c7560e0bebb2eb79eda0adb5a65df9cff27cb453720570c2d8f45190ca1defd - Sigstore transparency entry: 1761313320
- Sigstore integration time:
-
Permalink:
ChidcGithub/Threadcheck@06264d0561b7053875a53797a29effc7aec6e413 -
Branch / Tag:
refs/tags/v0.0.1.1 - Owner: https://github.com/ChidcGithub
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@06264d0561b7053875a53797a29effc7aec6e413 -
Trigger Event:
push
-
Statement type:
File details
Details for the file threadcheck-0.0.1.1-py3-none-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: threadcheck-0.0.1.1-py3-none-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 26.0 kB
- Tags: Python 3, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8d455f37f53fc065abdc224763b531be5febe954567ca56d825f4a87d4706ca1
|
|
| MD5 |
02ac6f178786329c3846ea319460e804
|
|
| BLAKE2b-256 |
21cc2c0a81fed2918549750fa6e703bd2d4f2b329206e03ee0dc8f44d8e89f08
|
Provenance
The following attestation bundles were made for threadcheck-0.0.1.1-py3-none-manylinux_2_28_x86_64.whl:
Publisher:
release.yml on ChidcGithub/Threadcheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
threadcheck-0.0.1.1-py3-none-manylinux_2_28_x86_64.whl -
Subject digest:
8d455f37f53fc065abdc224763b531be5febe954567ca56d825f4a87d4706ca1 - Sigstore transparency entry: 1761313160
- Sigstore integration time:
-
Permalink:
ChidcGithub/Threadcheck@06264d0561b7053875a53797a29effc7aec6e413 -
Branch / Tag:
refs/tags/v0.0.1.1 - Owner: https://github.com/ChidcGithub
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@06264d0561b7053875a53797a29effc7aec6e413 -
Trigger Event:
push
-
Statement type:
File details
Details for the file threadcheck-0.0.1.1-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: threadcheck-0.0.1.1-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 26.0 kB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
567a339d3de0dc6d67a1a13b8ecc968abe9c1ad7542fed1a4377f5164c487658
|
|
| MD5 |
2078c3d6c676132d449f708b268995ab
|
|
| BLAKE2b-256 |
37072c47d4037d86ea06b545015f5797b8babd61166aa65a136058dd72ea93c9
|
Provenance
The following attestation bundles were made for threadcheck-0.0.1.1-py3-none-macosx_11_0_arm64.whl:
Publisher:
release.yml on ChidcGithub/Threadcheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
threadcheck-0.0.1.1-py3-none-macosx_11_0_arm64.whl -
Subject digest:
567a339d3de0dc6d67a1a13b8ecc968abe9c1ad7542fed1a4377f5164c487658 - Sigstore transparency entry: 1761313008
- Sigstore integration time:
-
Permalink:
ChidcGithub/Threadcheck@06264d0561b7053875a53797a29effc7aec6e413 -
Branch / Tag:
refs/tags/v0.0.1.1 - Owner: https://github.com/ChidcGithub
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@06264d0561b7053875a53797a29effc7aec6e413 -
Trigger Event:
push
-
Statement type: