Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

voci

PyPI CI Coverage Python 3.13 | 3.14 MIT License uv Ruff Pyrefly

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 is what running concurrently bought you over the sum of every test's own duration (each file's Σ, above).

Install

Python 3.13+. The core package has no dependencies.

uv pip install voci          # 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 load-bearing.

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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

voci-0.1.0rc3.tar.gz (983.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

voci-0.1.0rc3-py3-none-any.whl (259.4 kB view details)

Uploaded Python 3

File details

Details for the file voci-0.1.0rc3.tar.gz.

File metadata

  • Download URL: voci-0.1.0rc3.tar.gz
  • Upload date:
  • Size: 983.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for voci-0.1.0rc3.tar.gz
Algorithm Hash digest
SHA256 0de0f972f066e2b9c87faa41edfa163cd5a631695a5d6289f831c398bccf4ecd
MD5 aeaf1caa4feb9f31a1cd0998372e09c8
BLAKE2b-256 5e974219571f3b95dcd9958b18189fa0375047ae9d3b6726adc3fdb388c85e12

See more details on using hashes here.

Provenance

The following attestation bundles were made for voci-0.1.0rc3.tar.gz:

Publisher: release.yml on Ddedalus/voci

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file voci-0.1.0rc3-py3-none-any.whl.

File metadata

  • Download URL: voci-0.1.0rc3-py3-none-any.whl
  • Upload date:
  • Size: 259.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for voci-0.1.0rc3-py3-none-any.whl
Algorithm Hash digest
SHA256 3ce22291771be8e4958d827bd676f5cee1dc073a43f86eafe4ebdc5b05b0849f
MD5 b629a766a445366482506942423eda00
BLAKE2b-256 4ffef9185bd5cd8d5d070632795d021470a4957fc2bfe569c4132e516698ea6c

See more details on using hashes here.

Provenance

The following attestation bundles were made for voci-0.1.0rc3-py3-none-any.whl:

Publisher: release.yml on Ddedalus/voci

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.0rc3 This release

2 files

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