A testing harness for Python packages using uv's isolated environments
Project description
testudos
A testing harness for Python packages using uv's isolated environments.
Overview
Testudos simplifies running your Python package's test suite across multiple Python versions. It automatically determines which versions to test based on:
- Currently supported Python versions - Fetched from endoflife.date
- Your package's compatibility - Parsed from
requires-pythoninpyproject.toml
Only versions that are both still supported by Python and compatible with your package are tested.
Quick Start
# Install testudos
uv tool install testudos
# Run tests on all compatible Python versions
testudos run
# Run tests in parallel for speed
testudos run --parallel
# See which versions would be tested
testudos versions
# Preview commands without running (dry-run)
testudos run --dry-run
Installation
# Using uv (recommended)
uv tool install testudos
# Using pip
pip install testudos
Requirements
- Python 3.11+
- uv - Fast Python package installer
Usage
Running Tests
# Run tests on all compatible versions (sequential)
testudos run
# Run tests in parallel
testudos run --parallel
testudos run -P
# Limit parallel jobs
testudos run --parallel --jobs 4
testudos run -P -j 4
# Test specific Python versions
testudos run --python 3.11 --python 3.12
testudos run -p 3.11 -p 3.12
# Fail fast (stop on first failure) - default in sequential mode
testudos run --fail-fast
testudos run -f
# Continue on failures
testudos run --no-fail-fast
testudos run -F
# Verbose output
testudos run --verbose
testudos run -v
# Dry run - show what would be executed
testudos run --dry-run
testudos run -n
# Test a specific project directory
testudos run ./path/to/project
Test Coverage
Testudos can collect and aggregate test coverage data across all Python versions:
# Run tests with coverage collection
testudos run --coverage
# Run with coverage and generate HTML report
testudos run --coverage --coverage-report html
# Run with coverage threshold (fail if below 80%)
testudos run --coverage --coverage-fail-under 80
# Multiple report formats
testudos run --coverage --coverage-report html --coverage-report xml
# Available report formats: term, term-missing, html, xml, json, lcov
Coverage Commands
Manage coverage data manually:
# Combine coverage from multiple Python versions
testudos coverage combine
# Generate coverage report
testudos coverage report
testudos coverage report --format html
# Clean coverage data
testudos coverage clean
Installing Coverage Support
Coverage collection requires the coverage package:
# Install testudos with coverage support
pip install testudos[coverage]
# Or add coverage to your project dependencies
uv add coverage
Viewing Versions
# Show versions that would be tested
testudos versions
# Show versions for a specific project
testudos versions ./path/to/project
Configuration
Configure testudos in your pyproject.toml:
[project]
requires-python = ">=3.11"
[tool.testudos]
# Optional: Override auto-detected Python versions
python-versions = ["3.11", "3.12", "3.13"]
# Test command to run (default: "pytest")
test-command = "pytest"
# Additional arguments for test command
test-args = ["-v", "--tb=short"]
# Run tests in parallel by default
parallel = false
# Maximum parallel jobs (default: number of versions)
max-jobs = 4
# Coverage options
coverage = true # Enable coverage collection
coverage-combine = true # Combine coverage from all versions
coverage-report = ["html", "xml"] # Report formats to generate
coverage-fail-under = 80 # Fail if coverage below threshold
Configuration Validation
Testudos validates your configuration on load and will:
- Error on invalid types or values
- Warn on unknown configuration keys
How It Works
Testudos uses uv run --isolated to create ephemeral, isolated environments for each Python version. This ensures:
- Clean test environments - No leftover state between versions
- Fast execution - uv's caching makes environment creation nearly instant
- No version conflicts - Each version is completely isolated
Example Command
For Python 3.11, testudos runs:
uv run --isolated --python=3.11 pytest
Features
- Automatic version detection: Fetches supported versions from endoflife.date API
- Smart caching: 24-hour TTL cache for API responses with offline fallback
- Parallel execution: Run tests concurrently with live progress display
- Coverage aggregation: Collect and combine coverage data across all Python versions
- Dry-run mode: Preview commands without execution
- Configuration validation: Schema validation for
[tool.testudos]settings - Input validation: Safe command construction with validation
Documentation
- Architecture - Detailed design and component overview
- Implementation Roadmap - Development phases and tasks
- Future Enhancements - Planned features and issue descriptions
Development
# Clone the repository
git clone https://github.com/martinristovski/testudos.git
cd testudos
# Install dependencies
uv sync
# Run tests
uv run pytest
# Run testudos on itself
uv run testudos run
License
MIT
Project details
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 testudos-0.11.0.tar.gz.
File metadata
- Download URL: testudos-0.11.0.tar.gz
- Upload date:
- Size: 101.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e2ba6b0223d4446e347a0b7553d2fe194abf0969429f1fd89a0b5af49582d675
|
|
| MD5 |
e1be9ca1818f53aed52f87e8016cd16e
|
|
| BLAKE2b-256 |
952c771fe9d07baf79809d946fa2813870bd4c80732bdc6c943fc128343566da
|
Provenance
The following attestation bundles were made for testudos-0.11.0.tar.gz:
Publisher:
python-publish.yml on martinristovski/testudos
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
testudos-0.11.0.tar.gz -
Subject digest:
e2ba6b0223d4446e347a0b7553d2fe194abf0969429f1fd89a0b5af49582d675 - Sigstore transparency entry: 780649280
- Sigstore integration time:
-
Permalink:
martinristovski/testudos@a64b59fadc1b1a4ea76e4677ed10bbb0d05f9a6e -
Branch / Tag:
refs/tags/v0.11.0 - Owner: https://github.com/martinristovski
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@a64b59fadc1b1a4ea76e4677ed10bbb0d05f9a6e -
Trigger Event:
release
-
Statement type:
File details
Details for the file testudos-0.11.0-py3-none-any.whl.
File metadata
- Download URL: testudos-0.11.0-py3-none-any.whl
- Upload date:
- Size: 39.1 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 |
fb3b4d77b8a16ad9d6684ea1d168d60dfea16b483db7b0e5a92dc8bf9b04e1be
|
|
| MD5 |
38bf5080dd0a5112c04d56922dce14f1
|
|
| BLAKE2b-256 |
5ac05fcb153949c4371f2cb282e6d94fc041882171630a52766e155bb2421be9
|
Provenance
The following attestation bundles were made for testudos-0.11.0-py3-none-any.whl:
Publisher:
python-publish.yml on martinristovski/testudos
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
testudos-0.11.0-py3-none-any.whl -
Subject digest:
fb3b4d77b8a16ad9d6684ea1d168d60dfea16b483db7b0e5a92dc8bf9b04e1be - Sigstore transparency entry: 780649284
- Sigstore integration time:
-
Permalink:
martinristovski/testudos@a64b59fadc1b1a4ea76e4677ed10bbb0d05f9a6e -
Branch / Tag:
refs/tags/v0.11.0 - Owner: https://github.com/martinristovski
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@a64b59fadc1b1a4ea76e4677ed10bbb0d05f9a6e -
Trigger Event:
release
-
Statement type: