punit.parallelism#

Parallelism for pUnit.

pUnit provides three mechanisms for controlling test execution parallelism: the ``–parallelism [THREADS]`` CLI flag, the ``@parallel`` decorator, and the ``@sequential`` decorator.

CLI flag#

The --parallelism [THREADS] option enables multi-threaded parallel execution. Each worker thread runs its own asyncio event loop for isolation.

  • --parallelism (bare flag, no number) runs tests using cpu_count // 2 workers.

  • --parallelism N runs tests using N workers.

  • No flag means tests run sequentially, one after another.

When parallel mode is active the runner processes each test file in four batches: parallel facts, parallel theories, sequential facts, then sequential theories.

@parallel decorator#

Marking a fact or theory with @parallel triggers auto-enable mode. When pUnit scans the test package and finds at least one @parallel decorated test (and no --parallelism flag was given on the CLI):

  • Parallel execution is enabled automatically with cpu_count // 2 workers.

  • Only @parallel-marked tests run in the concurrent batch.

  • All other tests are treated as sequential (as if they carried @sequential).

Applying @parallel to a class marks every method of that class.

@parallel is a no-op when --parallelism is specified on the CLI; the CLI flag takes full priority.

@sequential decorator#

Marking a fact or theory with @sequential forces it to run as a sequential test after all parralel tests for that file have completed. This lets you co-run certain tests one-at-a-time inside an otherwise parallel test suite (for example, tests that share mutable state).

When no @parallel decorator is present and --parallelism is not used on the CLI, all tests run sequentially (the default).

Examples#

Enable parallel mode for the entire test package via CLI:

punit tests/ --parallelism 4

Run only selected tests in parallel – no CLI flag needed:

from punit import fact, parallel

@fact
@parallel
def test_network_io():
    ...

@fact
def test_database_setup():
    # Runs sequentially alongside the parallel tests
    ...

Mix parallel and sequential tests inside a parallel suite:

from punit import fact, parallel, sequential

@parallel
class SlowTests:
    @fact
    def test_heavy_computation(self):
        ...

    @fact
    @sequential
    def test_shared_file_handle(self):
        # Runs one-at-a-time after all other SlowTests finish
        ...
class ThreadPool(parallelism)#

Bases: object

Manages a pool of worker threads, each with its own asyncio event loop.

The pool follows the context-manager protocol: __enter__ starts the pool, __exit__ waits for all in-flight work to finish and shuts down the workers.

Parameters#

parallelismint

Maximum number of tasks to run in parallel. This also equals the number of worker threads. Must be at least 1.

dispatch(coro)#

Dispatch coro to be executed on a worker thread.

Returns a _TaskInfo that can be checked with wait().

Return type:

_TaskInfo

wait(task, timeout=None)#

Block until task’s coroutine has completed.

Return type:

None

parallel(target)#

Mark a test function, method, or class for parallel execution.

When pUnit detects any @parallel-decorated tests in a test package at runtime, it automatically triggers (multi-threaded) parallel execution for the entire test run – no --parallelism CLI flag is required.

When @parallel triggers auto-enable:

  • Only tests marked with @parallel run in the parallel batch

  • Tests not marked with @parallel are treated as sequential (as if decorated with @sequential)

  • The thread pool is initialized with cpu_count // 2 workers

  • This auto-enable is superseded by --parallelism on the CLI

When --parallelism is specified on the CLI, this decorator has no effect; the existing --parallelism behavior takes priority and all non-@sequential tests run in parallel as before.

When applied to a class, all methods of the class are automatically marked as parallel.

The decorator returns the original target unchanged; it installs the __punit_parallel marker attribute on the unwrapped function (or the class object itself for class targets).

Parameters#

targetCallable | type

The test function, method, or class to mark.

Returns#

Callable

The original, undecorated target.

Example#

from punit import fact, parallel

@fact
@parallel
def my_parallel_test():
    assert True

class ParallelTestCase:
    @fact
    @parallel
    def test_one(self):
        assert True

    @fact
    def test_sequential(self):
        # Runs sequentially (not in parallel batch)
        assert True
rtype:

Callable[..., Any]

sequential(target)#

Mark a test function, method, or class for sequential execution.

When pUnit is started with --parallelism THREADS (where THREADS > 1), tests decorated without @sequential run in parallel up to N at any given point. @sequential tests are not added to the concurrent dispatch queue – after all peers at the same scope finish concurrently, the sequential tests run one after another in their definition order.

When applied to a class, all methods of the class are automatically marked as sequential.

The decorator returns the original target unchanged; it installs the __punit_sequential marker attribute on the unwrapped function (or the class object itself for class targets).

Return type:

Callable[..., Any]

Parameters#

targetCallable | type

The test function, method, or class to mark.

Returns#

Callable

The original, undecorated target.

Example#

from punit import fact, sequential

@fact
@sequential
def my_test():
    assert True

class MyTestCase:
    @fact
    @sequential
    def test_one(self):
        assert True

# Apply @sequential to an entire class -- all methods run sequentially
@sequential
class SequentialTestCase:
    @fact
    def test_one(self):
        assert True

    @fact
    def test_two(self):
        assert True