A pytest plugin for test case-level dynamic dependency management
Project description
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pytest_casewise_package_install-0.3.0.tar.gz.
File metadata
- Download URL: pytest_casewise_package_install-0.3.0.tar.gz
- Upload date:
- Size: 41.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
42c4e7d7a4e5b4441d16dc47ef6db01f48503d976b3b24fd4c82f30741c480f5
|
|
| MD5 |
c2824b981b64219c3db4d27d24388753
|
|
| BLAKE2b-256 |
64ba567500e0a35fe6f20c8825d1f08642d8eee63d5e0d66ce95b5ce5d7eb0f1
|
File details
Details for the file pytest_casewise_package_install-0.3.0-py3-none-any.whl.
File metadata
- Download URL: pytest_casewise_package_install-0.3.0-py3-none-any.whl
- Upload date:
- Size: 18.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d869cc33492ec02f9a6377290f2a4a6147f9ddb10d220474d680b55c4059c590
|
|
| MD5 |
3c12fb22141864d8376740dd2571a80c
|
|
| BLAKE2b-256 |
060fdf328e8f2564f6773226b7feab21ce0019c9544b67dc582eac79237f9140
|