pytest-casewise-package-install
A pytest plugin that emulates per-test package installation by generating deterministic stub packages. It lets you validate version-sensitive logic without reaching remote indexes or polluting the interpreter that runs your suite.
Why it helps
- declare package requirements directly on tests through the
with_packagesdecorator or marker exposed via the packagepytest_casewise_package_install.markers - bundle dependency requirements with parametrized data using helpers from
pytest_casewise_package_install.parametrize_support - run short-lived package environments backed by
LocalMultiPackageManager, which caches stub directories under~/.local_package_cache - rely on
package_envanddynamic_packagesfixtures for context-managed or ad hoc installs defined inpytest_casewise_package_install.fixtures - keep the runner clean because
PackageInstallPluginbacks up and restoressys.path,PYTHONPATH, and imported modules for every test case
The manager creates lightweight modules (such as
requests,numpy,pandas,click,transformers, and more) that expose__version__and minimal APIs. The process is offline-only; no realpip installoccurs.
Installation
pip install pytest-casewise-package-install
The plugin auto-registers through the casewise-package-install entry point. Importing the package or enabling it in pytest.ini is not required.
Quick start
decorator usage
from pytest_casewise_package_install import with_packages
@with_packages({"requests": "2.31.0"})
def test_requests_stub():
import requests
assert requests.__version__ == "2.31.0"
marker usage
import pytest
@pytest.mark.with_packages(packages={"requests": "2.28.0"})
def test_requests_marker():
import requests
assert requests.__version__ == "2.28.0"
multiple packages
from pytest_casewise_package_install import with_packages
@with_packages({"certifi": "2023.7.22", "urllib3": "2.0.4"})
def test_http_stack_versions():
import certifi
import urllib3
assert certifi.__version__ == "2023.7.22"
assert urllib3.__version__ == "2.0.4"
Parametrized tests
Use param_with_packages to pair fixtures with dependency declarations. Each parameter set receives its own with_packages mark so the plugin can discover requirements.
import pytest
from pytest_casewise_package_install import param_with_packages
@pytest.mark.parametrize(
"version, packages",
[
param_with_packages("2.31.0", packages={"requests": "2.31.0"}, id="requests-2-31"),
param_with_packages("2.28.0", packages={"requests": "2.28.0"}, id="requests-2-28"),
param_with_packages("2.25.0", packages={"requests": "2.25.0"}, id="requests-2-25"),
],
)
def test_requests_matrix(version, packages):
import requests
assert requests.__version__ == version
For larger tables reuse create_model_test_params:
from pytest_casewise_package_install import create_model_test_params
MODEL_CONFIGS = [
{
"model_name": "bert-base-uncased",
"packages": {"transformers": "4.30.0", "torch": "2.0.0"},
"id": "bert-transformers-4-30",
},
{
"model_name": "gpt2",
"packages": {"transformers": "4.35.0", "torch": "2.1.0"},
"id": "gpt2-transformers-4-35",
},
]
@pytest.mark.parametrize("model_name, packages", create_model_test_params(MODEL_CONFIGS))
def test_models(model_name, packages):
import transformers
assert transformers.__version__ in {"4.30.0", "4.35.0"}
Fixtures
package_env: returns a manager that applies package sets inside a context manager and restores the environment afterward.dynamic_packages: installs stub packages in-place without automatic teardown when you need to perform multiple assertions after importing modules.
from pytest_casewise_package_install import package_env
def test_numpy_context(package_env):
with package_env.use_packages({"numpy": "1.24.0"}):
import numpy as np
assert np.__version__ == "1.24.0"
Stub catalog and cache
LocalMultiPackageManager maintains two directories:
~/.local_package_cache/<package>_<version>holds generated stubs and metadata about schema version and requested version~/.local_package_cache/active/<package>mirrors the selected version and is prepended tosys.path
Stub generators ship for:
- requests
- numpy
- pandas
- pyyaml (available through the
yamlalias) - click
- transformers
- pillow (available through
PIL) - tokenizers
- librosa
- six
- certifi
- urllib3
Any other package receives a minimal stub with only __version__ defined. If a stub cannot be generated successfully the plugin marks the status as FAILED and the test exits with pytest.fail.
Configuration
Override defaults in pytest.ini or setup.cfg:
[pytest]
casewise_cache_dir = ~/.my_casewise_cache
casewise_auto_cleanup = true
casewise_install_timeout = 300
casewise_verbose = true
Environment variables take precedence over config files:
export CASEWISE_CACHE_DIR=~/.my_casewise_cache
export CASEWISE_AUTO_CLEANUP=true
export CASEWISE_INSTALL_TIMEOUT=300
export CASEWISE_VERBOSE=true
The PluginConfig loader (see pytest_casewise_package_install.config) normalizes these values and falls back to ~/.local_package_cache, automatic cleanup, a 300 second timeout placeholder, and non-verbose logging.
Test lifecycle
PackageInstallPlugin (in pytest_casewise_package_install.plugin) wires into pytest hooks:
- discover requested packages from parametrized values, markers, or decorator metadata
- back up
PYTHONPATH,sys.path, and the set of already imported modules before altering the environment - ensure required stubs are ready through
LocalMultiPackageManager - prepend stub directories to
sys.path, refresh imported modules, and expose environment variables so child processes inherit the setup - run the test body
- restore the previous interpreter state and remove modules imported during the test
If the package requirement is not a dictionary the plugin raises pytest.fail with a descriptive message to avoid silent misconfiguration.
Examples
Browse the examples/ directory for working scenarios:
test_basic_usage.py: decorator versus marker usage, multi-package sets, cache reusetest_parametrize_simple.py: matrix parametrization, helper usage, shared cachingtest_fixture_usage.py:package_envanddynamic_packagesin practicetest_advanced_usage.py: class-based tests and mixing strategies
Run them locally:
cd examples
pytest -v -s
Development
git clone https://github.com/yourusername/pytest-casewise-package-install
cd pytest-casewise-package-install
pip install -e .
pytest tests -v
License
MIT License
Metadata
Release files for pytest-casewise-package-install 0.3.0
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 | |
|---|---|---|---|---|
| pytest_casewise_package_install-0.3.0.tar.gz | 41.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pytest_casewise_package_install-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 59.4 kB
Release files / pytest_casewise_package_install-0.3.0.tar.gz
| Download URL | pytest_casewise_package_install-0.3.0.tar.gz |
|---|---|
| Size | 41.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
42c4e7d7a4e5b4441d16dc47ef6db01f48503d976b3b24fd4c82f30741c480f5
|
|
BLAKE2b-256 checksum How to use checksums |
64ba567500e0a35fe6f20c8825d1f08642d8eee63d5e0d66ce95b5ce5d7eb0f1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / pytest_casewise_package_install-0.3.0-py3-none-any.whl
| Download URL | pytest_casewise_package_install-0.3.0-py3-none-any.whl |
|---|---|
| Size | 18.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d869cc33492ec02f9a6377290f2a4a6147f9ddb10d220474d680b55c4059c590
|
|
BLAKE2b-256 checksum How to use checksums |
060fdf328e8f2564f6773226b7feab21ce0019c9544b67dc582eac79237f9140
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Security for pytest-casewise-package-install 0.3.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A 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.