Skip to main content

pytest-cov-container

PyPI - Version PyPI - Python Version security: bandit

Collect code coverage from Python applications running inside Docker containers during integration tests. Works alongside pytest-cov to combine container coverage with your local test coverage.

You name your framework and the plugin infers the rest. Supported today: AWS SAM (sam local).

How It Works

  1. Before tests — for every function it measures, the plugin injects into the function's build dir a .coveragerc, a pure-Python copy of coverage, a small bootstrap module and a .pth file that imports it. The Lambda Python runtime processes .pth files in the task root before it imports your handler, so coverage starts before your code loads. Your build and your entrypoint stay untouched.
  2. During tests — each pytest process (each xdist worker) runs a small HTTP sink. container_env() hands the containers its URL. The bootstrap pushes the process's coverage data to it after every handler call (sam local invoke removes its container right after the invoke, so there is no later chance) and, for a long-running server, when the plugin sends SIGUSR1.
  3. After tests — each push becomes a suffix file of pytest-cov's data file, with container paths mapped back to your sources. pytest-cov's own combine merges them with the host data, so its report and --cov-fail-under include container coverage.

Installation

pip install pytest-cov-container

Requires Python 3.11+. For framework = "aws-sam": SAM CLI 1.165+ (the first release that labels its Lambda containers).

Configuration

Add to your pyproject.toml:

[tool.pytest-cov-container]
framework = "aws-sam"

framework is required. It selects a preset that works out everything below from your project; an explicit key always wins.

For framework = "aws-sam" the preset reads template.yaml:

What Inferred as
functions every zip Python function with a local CodeUri, whatever builds it (pip, python-uv, makefile), or the functions list
build dir from sam build's own template (.aws-sam/build/<dir>; functions that share code share one), else .aws-sam/build/<logical id>
sources the function's CodeUri at /var/task, and each local layer's ContentUri where sam build put its files under /opt, whatever built it (python3.x, makefile, none); before a build, by its BuildMethod (Globals and function Layers)
label sam.cli.container.type=lambda (set by SAM CLI 1.165+ on every Lambda container)
mount_prefix the build root: only this checkout's containers
Key Description
framework Preset that infers the defaults (supported: aws-sam)
functions aws-sam: logical ids to measure (default: every eligible function)
template aws-sam: SAM template, relative to rootdir (default: template.yaml)
build_dir aws-sam: the SAM build root (default: .aws-sam/build)
worker_env Ownership: only containers whose env holds <worker_env>=<worker id> (gw0, gw1, … under xdist, else main)
label Docker label filter, key or key=value; "" switches the filter off
mount_prefix Ownership: only containers that bind-mount a directory under <rootdir>/<mount_prefix>/; "" switches the check off. Tells concurrent checkouts apart, which image and labels cannot: they are the same in every checkout.
image_pattern Glob pattern, or list of patterns, matched against the image the container was created from (then its tags)
branch Branch coverage in the containers. Default: the host's setting, which it must match to combine.
sink_host Address containers use to reach the sink (default: detected, see below)
sink_bind Address the sink listens on (default: detected, see below)
docker_network The docker network the containers join, for that detection (default: bridge)
required Fail when a process that flushed containers received no coverage (default: false; also --cov-container-required)
enabled Set to false to disable (default: true)

Template declarations

sam local --env-vars only overrides variables the template declares, so declare the bootstrap's two variables (and your worker_env, if any) empty in Globals. Empty, the injected bootstrap does nothing, so production is unaffected:

Globals:
  Function:
    Environment:
      Variables:
        COVERAGE_PROCESS_START: ""
        COV_CONTAINER_SINK: ""

A process that the Lambda runtime does not start, such as a web server behind the Lambda Web Adapter (run.sh), must process the task root's .pth files itself, as the runtime would:

import os
import site

if task_root := os.environ.get("LAMBDA_TASK_ROOT"):
    site.addsitedir(task_root)

Usage

Run your tests with --cov as usual:

pytest --cov=src tests/

The plugin activates automatically when pytest-cov is active (--cov) and [tool.pytest-cov-container] is configured. Disable it for a run with --no-cov-container.

Pass container_env() into the containers, for example in sam local's env file:

import pytest_cov_container

env_vars = {"Parameters": pytest_cov_container.container_env(project_root)}
# write env_vars to env.json, then: sam local start-api --env-vars env.json

Long-running servers

A handler pushes after every call by itself. A long-running server (a web app behind the Lambda Web Adapter) pushes when asked: call collect_container_coverage() before stopping its containers.

from pytest_cov_container import collect_container_coverage


@pytest.fixture(scope="session")
def sam_api():
    proc = start_sam(...)
    yield SAM_URL
    collect_container_coverage()
    proc.terminate()

It signals the running containers this process owns and waits for their pushes; containers still running when the tests end are flushed automatically before pytest-cov reports.

Reaching the Sink

The plugin asks the Docker engine where containers can reach the host:

  • Docker Desktop, OrbStack, or any engine on a macOS/Windows host — the sink listens on 127.0.0.1 and containers use host.docker.internal, which these engines forward to the host's loopback.
  • A Linux engine (CI runners) — the sink listens on the gateway of the containers' network (docker0, typically 172.17.0.1) and containers use that IP, so no --add-host is needed and nothing outside Docker can connect.

Set docker_network when the containers join another network (sam local --docker-network), or sink_bind / sink_host for anything detection gets wrong (rootless Docker, a remote engine). Either wins over detection. The token in the sink's URL keeps anything but this session's containers from writing data.

pytest-xdist and Concurrent Checkouts

Every process that runs tests has its own sink, and its containers push to it, so each worker receives exactly its own containers' data. Under xdist the controller injects before any worker starts, and pytest-cov's controller combines everything once.

Ownership still matters for flushing long-running servers and for cleanup: image and label say what a container is, not whose it is. Set worker_env and declare it in the template; the preset's mount_prefix tells checkouts apart. owned_containers(rootpath, all_workers=False) lists the owned containers for other uses, such as a reaper that removes leaked containers without touching a concurrent session's.

Development

# Install the pre-commit hooks (once per clone): checks, ty, bandit and
# `hatch test` run on every commit
prek install

# Run tests
hatch test

# Run the e2e suite: real `sam local` containers (needs Docker and SAM CLI)
hatch test -m e2e -n 0

# Run across all Python versions
hatch test --all

# Format and lint
hatch fmt

# Type check
hatch run types:check

# Security scan
hatch run security:scan

# Cut a release: test every Python and run the e2e suite, then bump + commit + tag + push in one shot. Pass a version
# literal or a hatch segment (`patch`, `minor`, `major`, `rc`, etc.).
# Omit the arg to bump the minor version.
hatch run release           # 0.2.0 → 0.3.0
hatch run release patch     # 0.2.0 → 0.2.1
hatch run release 0.3.5     # explicit

License

pytest-cov-container is distributed under the terms of the MIT license.

Release files for pytest-cov-container 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-cov-container 0.6.0
File Size Uploaded
pytest_cov_container-0.6.0.tar.gz 118.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-cov-container 0.6.0
File Interpreter ABI Platform
pytest_cov_container-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 149.4 kB

Release files / pytest_cov_container-0.6.0.tar.gz

Download URL pytest_cov_container-0.6.0.tar.gz
Size 118.7 kB
Tags Source
SHA-256 checksum
How to use checksums
6208a7bd755183de1b9056c708a1d21cafb7e841a8c77644d1abd52304dfd58c
BLAKE2b-256 checksum
How to use checksums
a06cc8efc0fee0c6be59c71575cc3ba5cfcf111926855b87dbcd9af3cd80b0b9
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 25, 2026.

Transparency log

Release files / pytest_cov_container-0.6.0-py3-none-any.whl

Download URL pytest_cov_container-0.6.0-py3-none-any.whl
Size 30.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
69db20acabd452b778c52e4a58aae0fc98dd4d5f9f64ed9438c911da2091eba0
BLAKE2b-256 checksum
How to use checksums
3062c470f8e8446c348f6b85551cda9c4c5646e3a80fe5dd46b1f415beecab6e
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 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.0.1

2 release 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