Skip to main content

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_packages decorator or marker exposed via the package pytest_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_env and dynamic_packages fixtures for context-managed or ad hoc installs defined in pytest_casewise_package_install.fixtures
  • keep the runner clean because PackageInstallPlugin backs up and restores sys.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 real pip install occurs.

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 to sys.path

Stub generators ship for:

  • requests
  • numpy
  • pandas
  • pyyaml (available through the yaml alias)
  • 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:

  1. discover requested packages from parametrized values, markers, or decorator metadata
  2. back up PYTHONPATH, sys.path, and the set of already imported modules before altering the environment
  3. ensure required stubs are ready through LocalMultiPackageManager
  4. prepend stub directories to sys.path, refresh imported modules, and expose environment variables so child processes inherit the setup
  5. run the test body
  6. 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 reuse
  • test_parametrize_simple.py: matrix parametrization, helper usage, shared caching
  • test_fixture_usage.py: package_env and dynamic_packages in practice
  • test_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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytest_casewise_package_install-0.3.0.tar.gz (41.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pytest_casewise_package_install-0.3.0-py3-none-any.whl (18.2 kB view details)

Uploaded Python 3

File details

Details for the file pytest_casewise_package_install-0.3.0.tar.gz.

File metadata

File hashes

Hashes for pytest_casewise_package_install-0.3.0.tar.gz
Algorithm Hash digest
SHA256 42c4e7d7a4e5b4441d16dc47ef6db01f48503d976b3b24fd4c82f30741c480f5
MD5 c2824b981b64219c3db4d27d24388753
BLAKE2b-256 64ba567500e0a35fe6f20c8825d1f08642d8eee63d5e0d66ce95b5ce5d7eb0f1

See more details on using hashes here.

File details

Details for the file pytest_casewise_package_install-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pytest_casewise_package_install-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d869cc33492ec02f9a6377290f2a4a6147f9ddb10d220474d680b55c4059c590
MD5 3c12fb22141864d8376740dd2571a80c
BLAKE2b-256 060fdf328e8f2564f6773226b7feab21ce0019c9544b67dc582eac79237f9140

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page