This release is a pre-release and may not be stable for production use.
voci
A concurrent test runner for async Python. voci runs your whole suite in one process on one
event loop, with every test as a concurrent asyncio task. For a suite that spends its time
waiting — on a database, on an ASGI app, on a network stub — that turns wall-clock time from the
sum of your tests into roughly the slowest one, with a single connection pool and a single set of
imports behind it.
Tests declare what they need as parameter defaults, the way FastAPI routes do. There is no
conftest.py and no name-based lookup: a fixture is a function you import, so "go to definition"
works, renames are safe, and a typo is an ImportError at collection rather than a mystery at run
time. When a fixture is a side effect no test needs the value of, a module — or a package, for
everything under it — declares it once: voci.use(reset_cache), the way a FastAPI router
declares dependencies for every route on it. Assertions keep the introspection you already know —
voci vendors pytest's assertion rewriter, so assert a == b still prints a real diff.
# tests/fixtures.py
from typing import Annotated
import voci
from voci import Depends
@voci.fixture(scope="session")
async def engine() -> AsyncIterator[AsyncEngine]:
engine = create_async_engine("sqlite+aiosqlite:///./test.db")
yield engine
await engine.dispose()
@voci.fixture()
async def session(engine: Annotated[AsyncEngine, Depends(engine)]) -> AsyncIterator[AsyncSession]:
"""A transaction per test, always rolled back — so tests share one engine safely."""
async with engine.connect() as conn:
transaction = await conn.begin()
yield AsyncSession(bind=conn)
await transaction.rollback()
# tests/test_users.py
from typing import Annotated
from voci import Depends
from tests.fixtures import session
async def test_create_user(db: Annotated[AsyncSession, Depends(session)]) -> None:
db.add(User(email="alice@example.com"))
await db.flush()
assert await db.scalar(select(func.count()).select_from(User)) == 1
$ voci
PASS tests/test_users.py 10 tests Σ 3.84s
PASS tests/test_orders.py 12 tests Σ 7.29s
22 tests · 22 passed · 1.14s wall (9.8x concurrency)
The last line is the one to watch: the multiplier shows what running concurrently bought you over
the sum of every test's own duration (each file's Σ, above).
Install
You only need Python 3.13+ - voci has no dependencies.
uv add voci --dev # or: pip install voci
Configure
Everything is optional. voci reads [tool.voci] from the nearest pyproject.toml:
[tool.voci]
testpaths = ["tests"]
concurrency = 16 # tests in flight at once
timeout = 60 # per-test budget, in seconds
env = { ENVIRONMENT = "test" }
CLI flags win over config, which wins over the defaults:
voci # run everything
voci tests/test_users.py # run one file
voci tests/test_users.py::test_create[admin] # run one test, or one of its cases
voci -k "users and not slow" # select by substring of the test id
voci -m "smoke" # select by @voci.tag
voci --serial # exactly serial — the first debugging step
voci -x # stop at the first failure, cancelling what is in flight
voci --durations 10 # the slowest tests, to tune --concurrency by
voci --loop-watchdog 10 # how long the loop may block before voci names the call
voci --collect-only # print the ids that would run, and stop
voci -s # live, id-prefixed output instead of captured
Where to go next
- examples/ — three worked suites: a FastAPI + async SQLAlchemy CRUD API, a pure-async library, and shared resources under concurrency. Each is runnable.
- ROADMAP.md — what isn't built yet, and what's next.
- plans/rationale.md — why voci is shaped the way it is. Read this before changing anything central to it.
Status
Pre-release, ahead of v0.1. The core is real and exercised by voci's own suite and the three examples: discovery, dependency injection with four scopes and inverted teardown, concurrent execution with per-test timeouts, capture, assertion introspection, selection, and the reporter. Fair scheduling around shared resources, the migration tool, and machine-readable reports are still ahead — see ROADMAP.md. The public API is not frozen yet.
Trade-offs
voci is not pytest and does not aim to be compatible with it. There is no plugin ecosystem and no hook system — dependency injection is the extension point. Adopting it means rewriting your fixture wiring, and it requires Python 3.13+. Linux and macOS are supported; Windows is best-effort.
unittest.mock keeps working, at a price: mock.patch writes to a module or class, which every
concurrently running test would see, so voci runs a patch-decorated test alone and reports what
that cost. A patch opened inside a test body fails that test unless it is marked @voci.solo.
examples/02-async-library walks the alternatives.
Metadata
Release files for voci 0.1.0rc4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| voci-0.1.0rc4.tar.gz | 1.0 MB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| voci-0.1.0rc4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.3 MB
Release files / voci-0.1.0rc4.tar.gz
| Download URL | voci-0.1.0rc4.tar.gz |
|---|---|
| Size | 1.0 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e3532b3d47b8d2073ea8f8edcd705f50bc7bb61da8c4490df8344e368b0e4dad
|
|
BLAKE2b-256 checksum How to use checksums |
c8e90c76f872a729076536856d0ef0a628f6ad3c9916316003bf731049ae70de
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency logRelease files / voci-0.1.0rc4-py3-none-any.whl
| Download URL | voci-0.1.0rc4-py3-none-any.whl |
|---|---|
| Size | 261.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b8641cc0628e729389130c08ada19e928530070f09d4024bc6a0498689033d56
|
|
BLAKE2b-256 checksum How to use checksums |
f42766ddb777f6642db18954dac128767683f945a6b424426de9cd6343e57228
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency logSecurity for voci 0.1.0rc4
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for voci-0.1.0rc4.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency logA 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.