Check if your Python project dependencies are ready for free-threaded Python (3.13t/3.14t)
Project description
🧵 ftready
Is your project ready for free-threaded Python? Find out in one command.
Quickstart · Features · Usage · Tracking Progress · CI Integration · Python API · PyPI
PEP 703 introduced a free-threaded build of CPython (3.13t) that disables the GIL — unlocking true multi-core parallelism. PEP 779 made free-threading a supported (no longer experimental) feature in CPython 3.14. But your project is only as ready as its least-compatible dependency. See the free-threading guide for background.
ftready scans your dependency tree, queries PyPI for cp313t/cp314t wheels, cross-references ft-checker.com test results, and tells you exactly where you stand:
Package Compatibility Details
─────────────────────────────────────────────────────
numpy ✅ Success free-threaded wheel found
pandas ✅ Success free-threaded wheel found
scipy ✅ Success free-threaded wheel found
scikit-learn ✅ Success free-threaded wheel found
matplotlib ✅ Success free-threaded wheel found
pillow ✅ Success free-threaded wheel found
opencv-python ⬜ Not tested
tqdm 🐍 Pure Python likely compatible (no C extensions)
loguru 🐍 Pure Python likely compatible (no C extensions)
─────────────────────────────────────────────────────
✅ 6 passed · ❌ 0 failed · ⬜ 1 unknown · 🐍 2 pure
⚡ Quickstart
pip install ftready # or: uv tool install ftready
ftready
That's it. Reads your pyproject.toml and prints a compatibility report.
🎯 Features
- PyPI-first detection — queries
cp313t/cp314twheel tags in parallel for every dependency - Real test results — enriches with ft-checker.com data covering ~1000 top packages
- Pure-Python detection — flags packages with no C extensions as likely compatible
- Every input format —
pyproject.toml(PEP 621 + Poetry),requirements.txt,uv.lock,poetry.lock,pdm.lock - Full dependency tree —
--all-depsscans transitive deps via lock files with pinned-version accuracy - Progress tracking —
ftready diffcompares two JSON reports to show what changed over time - Multiple outputs — rich tables, plain text, JSON, or CSV
- CI-ready — configurable exit codes (
--fail-on) to gate or report without blocking - Offline-safe —
--no-ftcheckerand--no-pypiflags for controlled environments
📖 Usage
Input sources
ftready # pyproject.toml in current dir
ftready --pyproject path/to/pyproject.toml # specific pyproject.toml
ftready --requirements requirements.txt # requirements file
ftready --include-dev # include dev dependencies
ftready --all-deps # full tree (auto-detects lock file)
ftready --all-deps --lock path/to/uv.lock # specific lock file
Output formats
ftready # rich table (default)
ftready --plain # plain-text table
ftready --format json # JSON (for scripting)
ftready --format csv # CSV
ftready --output report.txt # write to file
ftready -v # verbose progress to stderr
Data source control
ftready --no-ftchecker # PyPI only (skip ft-checker.com)
ftready --no-pypi # ft-checker only (skip PyPI)
ftready --no-cache # force fresh ft-checker scrape
ftready --cache-ttl 48 # cache ft-checker data for 48 hours
ftready --cache-file /tmp/ft_cache.json # custom cache location
Exit codes
| Code | Meaning |
|---|---|
0 |
No blocking issues (configurable via --fail-on) |
1 |
At least one dependency has a Failed status |
2 |
Configuration error (missing input file, etc.) |
ftready --fail-on=never # always exit 0 (report only)
ftready --fail-on=unknown # exit 1 on Failed OR Unknown
ftready --fail-on=failed # exit 1 on Failed only (default)
📈 Tracking Progress
Save JSON reports over time and use ftready diff to see what changed:
# Save a baseline
ftready --format json --output baseline.json
# ... weeks later, check again
ftready --format json --output current.json
# Compare the two
ftready diff baseline.json current.json
ftready diff: 2025-01-15T12:00:00+00:00 → 2025-03-01T12:00:00+00:00
3.13t ready: 5 → 8 (+3)
3.14t ready: 2 → 5 (+3)
✅ Improved (3):
scipy: 3.13t: Not tested → Success
pillow: 3.13t: Not tested → Success, 3.14t: Not tested → Success
matplotlib: 3.14t: Not tested → Success
The diff also supports --format json for machine-readable output.
🔄 How It Works
pyproject.toml ─┐
requirements.txt ┼──▶ Parse ──▶ PyPI API ──▶ ft-checker.com ──▶ Report
uv.lock ────────┘ │ (parallel) (optional) │
│ │
└── pinned versions ──▶ exact endpoint ▼
table/json/csv
- Parse — reads your dependency file and extracts package names (+ pinned versions from lock files)
- Query PyPI — hits
/pypi/{pkg}/json(or/pypi/{pkg}/{version}/jsonfor pinned versions) in parallel - Detect pure-Python — flags packages with only
py3-none-anywheels and no C extensions - Enrich — optionally fetches ft-checker.com test results (cached 24h)
- Report — renders results as a styled table, JSON, or CSV with configurable exit codes
About "Pure Python": A package is flagged as 🐍 when all its published wheels use the
py3-none-anytag — meaning no compiled C extensions. These packages typically work on free-threaded Python without changes. However, packages usingctypes,cffi, or calling native code at runtime may still have issues despite being "pure." Treat this as a strong signal, not a guarantee.
🏗 CI Integration
Report without blocking:
- name: Check free-threaded compatibility
run: |
pip install ftready
ftready --plain --fail-on=never -v
Gate on failures:
- name: Check free-threaded compatibility
run: |
pip install ftready
ftready --plain -v
JSON artifact for downstream processing:
- name: Check free-threaded compatibility
run: |
pip install ftready
ftready --format json --output ft-report.json --fail-on=never
- uses: actions/upload-artifact@v4
with:
name: ft-compat-report
path: ft-report.json
Track progress across CI runs by downloading a previous artifact and diffing:
- name: Compare with baseline
run: |
pip install ftready
ftready --format json --output current.json --fail-on=never
ftready diff baseline.json current.json || true
🐍 Python API
ftready exports a public API for programmatic use:
from ftready import (
load_dependencies,
fetch_ftchecker_db,
build_results,
generate_report,
diff_reports,
format_diff,
)
from pathlib import Path
# Load deps from pyproject.toml
deps = load_dependencies(Path("pyproject.toml"))
# Query data sources
ft_db = fetch_ftchecker_db(Path(".ft_cache.json"))
results = build_results(deps, ft_db)
# Render as JSON
report = generate_report(results, include_dev=False, output_format="json")
# Compare two JSON reports
summary = diff_reports("old_report.json", "new_report.json")
print(format_diff(summary))
📊 Data Sources
| Source | Role | Coverage |
|---|---|---|
| PyPI JSON API | Primary — cp313t/cp314t wheel tag detection |
Every package on PyPI |
| PyPI version endpoint | Primary — pinned-version checking from lock files | Every package on PyPI |
| ft-checker.com | Enrichment — actual test results | ~1000 top packages |
When both sources report on a package, ft-checker.com takes priority — a package may ship free-threaded wheels but still fail tests.
Related resources
- py-free-threading.github.io — official porting guide, C-API status, and ecosystem tracker
- hugovk.dev/free-threaded-wheels — auto-updated dashboard of the top 360 packages with free-threaded wheels
⚠️ Limitations
- Latest release only (without lock files): When no lock file is used, ftready checks the latest PyPI release. If you're pinned to an older version, use
--all-depswith a lock file for exact version checks. - Wheel tags ≠ runtime compatibility: A package shipping
cp313twheels doesn't guarantee it works correctly under free-threaded Python — it only means the maintainer built wheels for that target. ft-checker.com test results provide stronger evidence. - ft-checker.com coverage: The enrichment source covers ~1000 popular packages. Niche packages may only have PyPI wheel tag data.
- No transitive blocker tracing: The tool reports status per-package but doesn't show dependency chains (e.g., "X depends on Y which depends on Z, and Z is the blocker").
📄 License
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 Distribution
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 ftready-1.0.0.tar.gz.
File metadata
- Download URL: ftready-1.0.0.tar.gz
- Upload date:
- Size: 95.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bee4ea37e949a93ad075676521b02aaf478821c5c2a907c002f7c4d406c4dc04
|
|
| MD5 |
7f0bde542d1c660cc14eecfb98767a70
|
|
| BLAKE2b-256 |
ea9940b70cc2d2cac8adcbce732a8171e05b15cd8a63000c0bed7b4c900220f2
|
Provenance
The following attestation bundles were made for ftready-1.0.0.tar.gz:
Publisher:
publish.yml on dxvidparham/ftready
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ftready-1.0.0.tar.gz -
Subject digest:
bee4ea37e949a93ad075676521b02aaf478821c5c2a907c002f7c4d406c4dc04 - Sigstore transparency entry: 1243299722
- Sigstore integration time:
-
Permalink:
dxvidparham/ftready@90f91187a04d5f57d41de0e8a5eaf5fc4d6786e6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/dxvidparham
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@90f91187a04d5f57d41de0e8a5eaf5fc4d6786e6 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file ftready-1.0.0-py3-none-any.whl.
File metadata
- Download URL: ftready-1.0.0-py3-none-any.whl
- Upload date:
- Size: 24.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e62f027c375ea039982dccaf5fa680fb66411569bba6502e4d47de967f672d74
|
|
| MD5 |
1a2af3989a3119253c26f5a8b72dec43
|
|
| BLAKE2b-256 |
f8cd920e4318378a07316990b278e672f854b432e4bc792f08c66073d51e9381
|
Provenance
The following attestation bundles were made for ftready-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on dxvidparham/ftready
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ftready-1.0.0-py3-none-any.whl -
Subject digest:
e62f027c375ea039982dccaf5fa680fb66411569bba6502e4d47de967f672d74 - Sigstore transparency entry: 1243299727
- Sigstore integration time:
-
Permalink:
dxvidparham/ftready@90f91187a04d5f57d41de0e8a5eaf5fc4d6786e6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/dxvidparham
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@90f91187a04d5f57d41de0e8a5eaf5fc4d6786e6 -
Trigger Event:
workflow_dispatch
-
Statement type: