pytest-cov-container 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ # SPDX-FileCopyrightText: 2026-present tomaDev <genins21@gmail.com>
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ __version__ = "0.0.1"
@@ -0,0 +1,36 @@
1
+ # SPDX-FileCopyrightText: 2026-present tomaDev <genins21@gmail.com>
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ import warnings
5
+ from typing import Any
6
+
7
+ _active_plugin: Any = None
8
+
9
+
10
+ def collect_container_coverage():
11
+ """Flush and collect coverage from running containers.
12
+
13
+ Call this before stopping containers to capture mid-session coverage.
14
+
15
+ Example::
16
+
17
+ from pytest_cov_container import collect_container_coverage
18
+
19
+
20
+ @pytest.fixture(scope="session")
21
+ def sam_api():
22
+ proc = start_sam(...)
23
+ yield SAM_URL
24
+ collect_container_coverage()
25
+ proc.terminate()
26
+ """
27
+ if _active_plugin is None:
28
+ warnings.warn(
29
+ "pytest-cov-container is not active. "
30
+ "Ensure pytest-cov is running (--cov) and "
31
+ "[tool.pytest-cov-container] is configured.",
32
+ UserWarning,
33
+ stacklevel=2,
34
+ )
35
+ return
36
+ _active_plugin.collect_from_running()
@@ -0,0 +1,47 @@
1
+ from dataclasses import dataclass, field
2
+ from pathlib import Path
3
+
4
+ import tomllib
5
+
6
+ from pytest_cov_container.models import DriverConfig
7
+
8
+
9
+ @dataclass
10
+ class PluginConfig:
11
+ image_pattern: str | None = None
12
+ label: str | None = None
13
+ language: str = "python"
14
+ enabled: bool = True
15
+ path_mapping: dict[str, str] = field(default_factory=dict)
16
+ driver_config: DriverConfig | None = None
17
+
18
+
19
+ def load_config(pyproject_path: Path) -> PluginConfig | None:
20
+ if not pyproject_path.exists():
21
+ return None
22
+
23
+ with open(pyproject_path, "rb") as f:
24
+ data = tomllib.load(f)
25
+
26
+ tool_config = data.get("tool", {}).get("pytest-cov-container")
27
+ if tool_config is None:
28
+ return None
29
+
30
+ path_mapping = {str(k): str(v) for k, v in tool_config.get("path_mapping", {}).items()}
31
+
32
+ plugin_config = PluginConfig(
33
+ image_pattern=tool_config.get("image_pattern"),
34
+ label=tool_config.get("label"),
35
+ language=tool_config.get("language", "python"),
36
+ enabled=tool_config.get("enabled", True),
37
+ path_mapping=path_mapping,
38
+ )
39
+
40
+ driver_section = tool_config.get(plugin_config.language, {})
41
+ plugin_config.driver_config = DriverConfig(
42
+ build_dir=driver_section.get("build_dir", ".aws-sam/build/ApiFunction"),
43
+ entrypoint=driver_section.get("entrypoint", "uvicorn app:app --host 0.0.0.0 --port 8080"),
44
+ path_mapping=path_mapping,
45
+ )
46
+
47
+ return plugin_config
@@ -0,0 +1,101 @@
1
+ import fnmatch
2
+ import io
3
+ import tarfile
4
+ import warnings
5
+ from pathlib import Path
6
+
7
+ import docker
8
+ import docker.errors
9
+
10
+ from pytest_cov_container.models import ContainerInfo
11
+
12
+ _SIGNAL_CMD = [
13
+ "sh",
14
+ "-c",
15
+ "for p in /proc/[0-9]*/cmdline; do "
16
+ "pid=$(echo $p | cut -d/ -f3); "
17
+ "if grep -q _cov_wrapper $p 2>/dev/null; "
18
+ "then kill -USR1 $pid; break; fi; done",
19
+ ]
20
+
21
+
22
+ class DockerBackend:
23
+ def __init__(self, client: docker.DockerClient | None = None):
24
+ self._client = client or docker.from_env()
25
+
26
+ def find_containers(
27
+ self,
28
+ image_pattern: str | None = None,
29
+ label: str | None = None,
30
+ ) -> list[ContainerInfo]:
31
+ filters: dict = {}
32
+ if label:
33
+ filters["label"] = label
34
+
35
+ containers = self._client.containers.list(all=True, filters=filters)
36
+
37
+ if image_pattern:
38
+ containers = [
39
+ c for c in containers if any(fnmatch.fnmatch(tag, image_pattern) for tag in (c.image.tags or []))
40
+ ]
41
+
42
+ return [self._to_info(c) for c in containers]
43
+
44
+ def send_signal(self, container_id: str) -> None:
45
+ try:
46
+ container = self._client.containers.get(container_id)
47
+ container.exec_run(_SIGNAL_CMD)
48
+ except docker.errors.APIError as exc:
49
+ warnings.warn(
50
+ f"Failed to send signal to container {container_id[:12]}: {exc}",
51
+ UserWarning,
52
+ stacklevel=2,
53
+ )
54
+
55
+ def extract_matching_files(
56
+ self,
57
+ container_id: str,
58
+ source_dir: str,
59
+ prefix: str,
60
+ dest: Path,
61
+ ) -> list[Path]:
62
+ try:
63
+ container = self._client.containers.get(container_id)
64
+ stream, _ = container.get_archive(source_dir)
65
+ except docker.errors.APIError as exc:
66
+ warnings.warn(
67
+ f"Failed to extract from container {container_id[:12]}: {exc}. Check that the container is accessible.",
68
+ UserWarning,
69
+ stacklevel=2,
70
+ )
71
+ return []
72
+
73
+ tar_bytes = b"".join(stream)
74
+ extracted: list[Path] = []
75
+ with tarfile.open(fileobj=io.BytesIO(tar_bytes)) as tar:
76
+ for member in tar.getmembers():
77
+ if not member.isfile():
78
+ continue
79
+ name = Path(member.name).name
80
+ if not name.startswith(prefix):
81
+ continue
82
+ member_file = tar.extractfile(member)
83
+ if member_file:
84
+ target = dest / name
85
+ target.write_bytes(member_file.read())
86
+ extracted.append(target)
87
+ return extracted
88
+
89
+ def inspect(self, container_id: str) -> dict:
90
+ container = self._client.containers.get(container_id)
91
+ return container.attrs
92
+
93
+ @staticmethod
94
+ def _to_info(container) -> ContainerInfo:
95
+ return ContainerInfo(
96
+ id=container.id,
97
+ name=container.name,
98
+ image=container.image.tags[0] if container.image.tags else "",
99
+ labels=container.labels,
100
+ status=container.status,
101
+ )
@@ -0,0 +1,21 @@
1
+ from importlib.metadata import entry_points
2
+
3
+ from pytest_cov_container.models import LanguageDriver
4
+
5
+
6
+ def discover_drivers() -> dict[str, LanguageDriver]:
7
+ eps = entry_points(group="pytest_cov_container.drivers")
8
+ drivers: dict[str, LanguageDriver] = {}
9
+ for ep in eps:
10
+ driver_class = ep.load()
11
+ drivers[ep.name] = driver_class()
12
+ return drivers
13
+
14
+
15
+ def get_driver(name: str) -> LanguageDriver:
16
+ found = discover_drivers()
17
+ if name not in found:
18
+ available = ", ".join(sorted(found.keys()))
19
+ msg = f"Unknown language driver '{name}'. Available: {available}"
20
+ raise ValueError(msg)
21
+ return found[name]
@@ -0,0 +1,105 @@
1
+ import stat
2
+ import time
3
+ import warnings
4
+ from pathlib import Path
5
+
6
+ from pytest_cov_container.models import ContainerInfo, DockerBackendProtocol, DriverConfig, InjectionResult
7
+
8
+ _COVERAGERC_TEMPLATE = """\
9
+ [run]
10
+ data_file = /tmp/.coverage.container
11
+ relative_files = true
12
+ parallel = true
13
+ sigterm = true
14
+ include =
15
+ *.py
16
+ omit =
17
+ _cov_wrapper.py
18
+ """
19
+
20
+ _COV_WRAPPER_TEMPLATE = """\
21
+ import os
22
+ import signal
23
+ import subprocess
24
+
25
+ import coverage
26
+
27
+ cov = coverage.Coverage(
28
+ config_file=os.environ.get("COVERAGE_PROCESS_START", "/var/task/.coveragerc")
29
+ )
30
+ cov.start()
31
+
32
+
33
+ def _flush(*_):
34
+ cov.save()
35
+
36
+
37
+ signal.signal(signal.SIGUSR1, _flush)
38
+
39
+ proc = subprocess.Popen(
40
+ ["sh", "-c", os.environ.get("CONTAINER_COV_ENTRYPOINT", "{entrypoint}")]
41
+ )
42
+ proc.wait()
43
+ cov.stop()
44
+ cov.save()
45
+ """
46
+
47
+ _RUN_SH_TEMPLATE = """\
48
+ #!/bin/bash
49
+ exec python /var/task/_cov_wrapper.py
50
+ """
51
+
52
+
53
+ class PythonDriver:
54
+ name: str = "python"
55
+
56
+ def inject(self, target_dir: Path, config: DriverConfig) -> InjectionResult:
57
+ files_written: list[Path] = []
58
+
59
+ # .coveragerc
60
+ coveragerc = target_dir / ".coveragerc"
61
+ coveragerc.write_text(_COVERAGERC_TEMPLATE)
62
+ files_written.append(coveragerc)
63
+
64
+ # _cov_wrapper.py
65
+ wrapper = target_dir / "_cov_wrapper.py"
66
+ wrapper.write_text(_COV_WRAPPER_TEMPLATE.format(entrypoint=config.entrypoint))
67
+ files_written.append(wrapper)
68
+
69
+ # run.sh
70
+ run_sh = target_dir / "run.sh"
71
+ run_sh.write_text(_RUN_SH_TEMPLATE)
72
+ run_sh.chmod(run_sh.stat().st_mode | stat.S_IEXEC)
73
+ files_written.append(run_sh)
74
+
75
+ return InjectionResult(
76
+ files_written=files_written,
77
+ env_vars={"COVERAGE_PROCESS_START": "/var/task/.coveragerc"},
78
+ )
79
+
80
+ def collect(
81
+ self,
82
+ docker_backend: DockerBackendProtocol,
83
+ container: ContainerInfo,
84
+ dest: Path,
85
+ config: DriverConfig, # noqa: ARG002
86
+ ) -> Path:
87
+ if container.status == "running":
88
+ docker_backend.send_signal(container.id)
89
+ time.sleep(1)
90
+
91
+ extracted = docker_backend.extract_matching_files(
92
+ container.id,
93
+ "/tmp", # noqa: S108
94
+ ".coverage.container",
95
+ dest,
96
+ )
97
+
98
+ if not extracted:
99
+ warnings.warn(
100
+ f"No coverage data found in container {container.name} ({container.id[:12]})",
101
+ UserWarning,
102
+ stacklevel=2,
103
+ )
104
+
105
+ return dest
@@ -0,0 +1,52 @@
1
+ from dataclasses import dataclass, field
2
+ from pathlib import Path
3
+ from typing import Protocol, runtime_checkable
4
+
5
+
6
+ @dataclass
7
+ class ContainerInfo:
8
+ id: str
9
+ name: str
10
+ image: str
11
+ labels: dict[str, str]
12
+ status: str
13
+
14
+
15
+ @dataclass
16
+ class DriverConfig:
17
+ build_dir: str
18
+ entrypoint: str
19
+ path_mapping: dict[str, str]
20
+
21
+
22
+ @dataclass
23
+ class InjectionResult:
24
+ files_written: list[Path]
25
+ env_vars: dict[str, str] = field(default_factory=dict)
26
+
27
+
28
+ class DockerBackendProtocol(Protocol):
29
+ def send_signal(self, container_id: str) -> None: ...
30
+
31
+ def extract_matching_files(
32
+ self,
33
+ container_id: str,
34
+ source_dir: str,
35
+ prefix: str,
36
+ dest: Path,
37
+ ) -> list[Path]: ...
38
+
39
+
40
+ @runtime_checkable
41
+ class LanguageDriver(Protocol):
42
+ name: str
43
+
44
+ def inject(self, target_dir: Path, config: DriverConfig) -> InjectionResult: ...
45
+
46
+ def collect(
47
+ self,
48
+ docker_backend: DockerBackendProtocol,
49
+ container: ContainerInfo,
50
+ dest: Path,
51
+ config: DriverConfig,
52
+ ) -> Path: ...
@@ -0,0 +1,117 @@
1
+ import subprocess
2
+ import sys
3
+ import tempfile
4
+ import warnings
5
+ from pathlib import Path
6
+
7
+ import pytest
8
+
9
+ import pytest_cov_container
10
+ from pytest_cov_container import config as config_module
11
+ from pytest_cov_container import drivers
12
+ from pytest_cov_container.docker_backend import DockerBackend
13
+ from pytest_cov_container.resolver import SamBuildResolver
14
+
15
+
16
+ class ContainerCovPlugin:
17
+ def __init__(self, plugin_config):
18
+ self.config = plugin_config
19
+ self.backend = DockerBackend()
20
+ self.driver = drivers.get_driver(plugin_config.language)
21
+ self.resolver = SamBuildResolver()
22
+ self.injection_result = None
23
+ self.coverage_dir = Path(tempfile.mkdtemp(prefix="cov_container_"))
24
+
25
+ def pytest_sessionstart(self, session):
26
+ target_dir = self.resolver.resolve_target_dir(
27
+ self.config.driver_config.build_dir,
28
+ session.config.rootpath,
29
+ )
30
+ if not target_dir.exists():
31
+ msg = f"Build directory {target_dir} does not exist. Run 'sam build' before running tests."
32
+ raise FileNotFoundError(msg)
33
+
34
+ self.injection_result = self.driver.inject(target_dir, self.config.driver_config)
35
+
36
+ @pytest.hookimpl(trylast=True)
37
+ def pytest_sessionfinish(self, session, exitstatus): # noqa: ARG002
38
+ containers = self.backend.find_containers(
39
+ image_pattern=self.config.image_pattern,
40
+ label=self.config.label,
41
+ )
42
+
43
+ if not containers:
44
+ warnings.warn(
45
+ "No matching containers found for coverage collection",
46
+ UserWarning,
47
+ stacklevel=2,
48
+ )
49
+
50
+ for container in containers:
51
+ self.driver.collect(self.backend, container, self.coverage_dir, self.config.driver_config)
52
+
53
+ self._combine_coverage(session.config.rootpath)
54
+
55
+ def collect_from_running(self):
56
+ containers = self.backend.find_containers(
57
+ image_pattern=self.config.image_pattern,
58
+ label=self.config.label,
59
+ )
60
+ running = [c for c in containers if c.status == "running"]
61
+ for container in running:
62
+ self.driver.collect(self.backend, container, self.coverage_dir, self.config.driver_config)
63
+
64
+ def _combine_coverage(self, root: Path):
65
+ rc_path = self.coverage_dir / ".coveragerc"
66
+ lines = ["[paths]"]
67
+ for i, (host_path, container_path) in enumerate(self.config.path_mapping.items()):
68
+ label = "source" if i == 0 else f"source{i}"
69
+ lines.append(f"{label} =")
70
+ lines.append(f" {host_path}")
71
+ lines.append(f" {container_path}")
72
+ rc_path.write_text("\n".join(lines) + "\n")
73
+
74
+ result = subprocess.run(
75
+ [sys.executable, "-m", "coverage", "combine", f"--rcfile={rc_path}", str(self.coverage_dir)],
76
+ check=False,
77
+ cwd=root,
78
+ capture_output=True,
79
+ text=True,
80
+ )
81
+ if result.returncode != 0:
82
+ warnings.warn(
83
+ f"coverage combine failed: {result.stderr}",
84
+ UserWarning,
85
+ stacklevel=2,
86
+ )
87
+
88
+
89
+ def pytest_addoption(parser):
90
+ group = parser.getgroup("cov-container")
91
+ group.addoption(
92
+ "--no-cov-container",
93
+ action="store_true",
94
+ default=False,
95
+ help="Disable container coverage collection",
96
+ )
97
+
98
+
99
+ @pytest.hookimpl(trylast=True)
100
+ def pytest_configure(config):
101
+ if config.getoption("--no-cov-container", default=False):
102
+ return
103
+
104
+ if not config.pluginmanager.hasplugin("pytest_cov"):
105
+ return
106
+
107
+ cov_sources = config.getoption("--cov", default=[])
108
+ if not cov_sources:
109
+ return
110
+
111
+ plugin_config = config_module.load_config(config.rootpath / "pyproject.toml")
112
+ if plugin_config is None or not plugin_config.enabled:
113
+ return
114
+
115
+ plugin = ContainerCovPlugin(plugin_config)
116
+ config.pluginmanager.register(plugin, "cov_container")
117
+ pytest_cov_container._active_plugin = plugin # noqa: SLF001
@@ -0,0 +1,9 @@
1
+ from pathlib import Path
2
+
3
+
4
+ class SamBuildResolver:
5
+ def resolve_target_dir(self, build_dir: str, project_root: Path) -> Path:
6
+ path = Path(build_dir)
7
+ if path.is_absolute():
8
+ return path
9
+ return project_root / path
@@ -0,0 +1,153 @@
1
+ Metadata-Version: 2.4
2
+ Name: pytest-cov-container
3
+ Version: 0.0.1
4
+ Summary: Pytest plugin to collect code coverage from applications running inside Docker containers
5
+ Project-URL: Documentation, https://github.com/tomaDev/pytest-cov-container#readme
6
+ Project-URL: Issues, https://github.com/tomaDev/pytest-cov-container/issues
7
+ Project-URL: Source, https://github.com/tomaDev/pytest-cov-container
8
+ Author-email: tomaDev <genins21@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE.txt
11
+ Keywords: containers,coverage,docker,pytest,testing
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Framework :: Pytest
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Programming Language :: Python :: 3.15
20
+ Classifier: Programming Language :: Python :: Implementation :: CPython
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: coverage>=7.2
23
+ Requires-Dist: docker>=6.0
24
+ Requires-Dist: pytest-cov>=4.0
25
+ Requires-Dist: pytest>=7.0
26
+ Description-Content-Type: text/markdown
27
+
28
+ # pytest-cov-container
29
+
30
+ [![PyPI - Version](https://img.shields.io/pypi/v/pytest-cov-container.svg)](https://pypi.org/project/pytest-cov-container)
31
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pytest-cov-container.svg)](https://pypi.org/project/pytest-cov-container)
32
+ [![security: bandit](https://img.shields.io/badge/security-bandit-yellow.svg)](https://github.com/PyCQA/bandit)
33
+
34
+ Collect code coverage from Python applications running inside Docker containers during integration tests. Works alongside [pytest-cov](https://github.com/pytest-dev/pytest-cov) to combine container coverage with your local test coverage.
35
+
36
+ Built for projects using [AWS SAM](https://docs.aws.amazon.com/serverless-application-model/) local testing, but works with any Docker-based test workflow.
37
+
38
+ ## How It Works
39
+
40
+ 1. **Before tests** — injects a coverage wrapper, `.coveragerc`, and `run.sh` into your SAM build directory
41
+ 2. **During tests** — your containerized app runs under `coverage` via the injected wrapper
42
+ 3. **After tests** — extracts `.coverage.*` files from containers and runs `coverage combine` to merge them with local results
43
+
44
+ ## Installation
45
+
46
+ ```console
47
+ pip install pytest-cov-container
48
+ ```
49
+
50
+ Requires Python 3.11+.
51
+
52
+ ## Configuration
53
+
54
+ Add to your `pyproject.toml`:
55
+
56
+ ```toml
57
+ [tool.pytest-cov-container]
58
+ image_pattern = "samcli/lambda*"
59
+ label = "pytest-cov-container"
60
+
61
+ [tool.pytest-cov-container.path_mapping]
62
+ "src/api" = "/var/task"
63
+
64
+ [tool.pytest-cov-container.python]
65
+ build_dir = ".aws-sam/build/ApiFunction"
66
+ entrypoint = "uvicorn app:app --host 0.0.0.0 --port 8080"
67
+ ```
68
+
69
+ | Key | Description |
70
+ |-----|-------------|
71
+ | `image_pattern` | Glob pattern to match container image tags |
72
+ | `label` | Docker label to filter containers |
73
+ | `path_mapping` | Maps host source paths to container paths (used by `coverage combine`) |
74
+ | `build_dir` | SAM build output directory where coverage files are injected |
75
+ | `entrypoint` | The command your app runs inside the container |
76
+ | `language` | Language driver to use (default: `"python"`) |
77
+ | `enabled` | Set to `false` to disable (default: `true`) |
78
+
79
+ ## Usage
80
+
81
+ Run your tests with `--cov` as usual:
82
+
83
+ ```console
84
+ pytest --cov=src/api tests/
85
+ ```
86
+
87
+ The plugin activates automatically when:
88
+ - pytest-cov is active (`--cov` flag present)
89
+ - `[tool.pytest-cov-container]` is configured in `pyproject.toml`
90
+
91
+ Disable it for a run with:
92
+
93
+ ```console
94
+ pytest --cov=src/api --no-cov-container tests/
95
+ ```
96
+
97
+ ### Mid-Session Collection
98
+
99
+ If you need to collect coverage before containers stop (e.g., in a session-scoped fixture teardown), use the public API:
100
+
101
+ ```python
102
+ from pytest_cov_container import collect_container_coverage
103
+
104
+ @pytest.fixture(scope="session")
105
+ def sam_api():
106
+ proc = start_sam(...)
107
+ yield SAM_URL
108
+ collect_container_coverage()
109
+ proc.terminate()
110
+ ```
111
+
112
+ This sends `SIGUSR1` to running containers to flush coverage data, then extracts the files.
113
+
114
+ ## Pluggable Drivers
115
+
116
+ Language support is pluggable via entry points. The built-in Python driver handles:
117
+
118
+ - Writing `.coveragerc` with `parallel = true` and `sigterm = true`
119
+ - Writing `_cov_wrapper.py` that starts coverage, handles `SIGTERM`/`SIGUSR1`, and runs your entrypoint
120
+ - Writing `run.sh` to bootstrap the wrapper
121
+ - Extracting `.coverage.*` files from `/tmp` in containers
122
+
123
+ To add a driver for another language, register an entry point:
124
+
125
+ ```toml
126
+ [project.entry-points."pytest_cov_container.drivers"]
127
+ node = "my_package.drivers.node:NodeDriver"
128
+ ```
129
+
130
+ Drivers must implement the `LanguageDriver` protocol from `pytest_cov_container.models`.
131
+
132
+ ## Development
133
+
134
+ ```console
135
+ # Run tests
136
+ hatch test
137
+
138
+ # Run across all Python versions
139
+ hatch test --all
140
+
141
+ # Format and lint
142
+ hatch fmt
143
+
144
+ # Type check
145
+ hatch run types:check
146
+
147
+ # Security scan
148
+ hatch run security:scan
149
+ ```
150
+
151
+ ## License
152
+
153
+ `pytest-cov-container` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
@@ -0,0 +1,14 @@
1
+ pytest_cov_container/__about__.py,sha256=_6K3JaHxGrAAf9QWvnngLAyAqs-pyQ0h7aHu_B2jDHE,123
2
+ pytest_cov_container/__init__.py,sha256=g82W1-dF7joWQG0ONmDBj2Hm3LnCLcz97HBDXZF4dJA,963
3
+ pytest_cov_container/config.py,sha256=O3gHl4ByejjgEB-nLzR-nH8sl_MpjvMZcvMXBB6ZS6o,1440
4
+ pytest_cov_container/docker_backend.py,sha256=06i9AZqrjK6IXraNihsXEbW4J34nN12D9aaJ9MlfQWc,3148
5
+ pytest_cov_container/models.py,sha256=liAkE_T9Dze7EuMWlhsHcwpTtACteuVh1SPXpYl5ptQ,1060
6
+ pytest_cov_container/plugin.py,sha256=ej1PidjQGEPrlH_fLiC-GasxMTa2uNH8Ly9MAz48w4A,4017
7
+ pytest_cov_container/resolver.py,sha256=nCg4e9UvmpphQ_CHicFt_dGpTMwSYPq5YPZ8Yve-y0s,250
8
+ pytest_cov_container/drivers/__init__.py,sha256=t6JmIvT7HfPrjzqbmOHY7tX9ihcTjDLBQ1eJifBzzsw,656
9
+ pytest_cov_container/drivers/python.py,sha256=U7XWmjnw2D3-LvuttJhl6dZGbtflqpwYY4mZbk06BHE,2484
10
+ pytest_cov_container-0.0.1.dist-info/METADATA,sha256=EuLk-us-f9Z-udCRC1N7-2tyxWL_ohg6wqiKm9H-xbI,5056
11
+ pytest_cov_container-0.0.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
12
+ pytest_cov_container-0.0.1.dist-info/entry_points.txt,sha256=S1jaxS9MKAETKV2JLzIAnvRwUFQsJXnJ5B0f0yKS4ao,145
13
+ pytest_cov_container-0.0.1.dist-info/licenses/LICENSE.txt,sha256=l-uCyVpFGHkbtBjhxUN2NnLtz05SMD9Np92H7SubkqE,1093
14
+ pytest_cov_container-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,5 @@
1
+ [pytest11]
2
+ cov_container = pytest_cov_container.plugin
3
+
4
+ [pytest_cov_container.drivers]
5
+ python = pytest_cov_container.drivers.python:PythonDriver
@@ -0,0 +1,18 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026-present tomaDev <genins21@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6
+ associated documentation files (the "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
9
+ following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all copies or substantial
12
+ portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
15
+ LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
16
+ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
18
+ USE OR OTHER DEALINGS IN THE SOFTWARE.