Skip to main content

Runtime capability registry for optional Python dependencies

Project description

mypantry

PyPI version Tests codecov Documentation Python 3.11+ License: MIT Code style: ruff

mypantry is lightweight syntactic sugar (~300 lines, zero dependencies) for working with optional Python packages at runtime. It wraps importlib.metadata to check availability, import modules safely, and guard functions with decorators.

Works everywhere — development, installed packages, Docker containers. No configuration files needed.

The entire source is one file: _registry.py. Read it in 10 minutes.

Why mypantry?

Without mypantry With mypantry
try: import pkg + flag + if/raise pantry.has("pkg")
Repeated try/except blocks pantry.get("pkg")
Custom error handling per feature pantry["pkg"] raises with install instructions
Manual checks at every entry point @pantry("pkg1", "pkg2")
Move imports inside functions for circular deps pantry.lazy_import("my.module.Class")

Installation

pip install mypantry

Zero dependencies. Only Python standard library (importlib.metadata).

Recommended Pattern

import pantry

# Guard imports at the top of the file
if pantry.has("numpy"):
    import numpy as np

if pantry.has("pillow"):
    from PIL import Image

# Guard functions with the decorator
@pantry("numpy", "pillow")
def process(path):
    img = Image.open(path)
    return np.array(img)

This pattern gives you:

  • pipreqs sees the real imports and detects dependencies correctly
  • IDE autocompletion works because the imports are standard Python
  • pantry provides clean error messages if a dependency is missing

Quick Start

import pantry

# Check if a package is installed (metadata only, no import)
pantry.has("numpy")                    # True/False
pantry.has("numpy", "pandas")          # True only if ALL installed

# Import a module — raises RuntimeError with install instructions if missing
PIL = pantry["pillow"]

# Import safely — returns None (or a default) if missing
np = pantry.get("numpy")
redis = pantry.get("redis", None)

# Get version
pantry.version("numpy")                # "1.26.4" or None

# Guard a function
@pantry("numpy", "pandas")
def analyze(data):
    ...

# See what you've checked
print(pantry.report())
# Or check specific packages
print(pantry.report("numpy", "pandas", "pillow", "redis"))

Output of report():

pantry report
──────────────────────────────────────────────
package  module  version  ok
numpy    numpy   1.26.4   ✓
pandas   pandas  2.1.4    ✓
pillow   PIL     10.4.0   ✓
redis    redis   -        ✗
──────────────────────────────────────────────
available: 3/4

How It Works

import pantry creates a Pantry instance that probes packages on demand using importlib.metadata. No configuration files, no startup scanning.

  • has() — checks distribution metadata only (fast, no import)
  • get() / [] — imports the module lazily on first access, then caches it
  • Smart module name resolution: pillowPIL, scikit-learnsklearn, etc.

Works in any context: development, installed packages, Docker, notebooks, REPLs.

Lazy Import — Breaking Circular Dependencies

A separate feature for your own project modules: deferred imports that break circular dependency chains.

# myapp/module_a.py
import pantry

pantry.lazy_import("myapp.module_b.Helper")

class Service:
    def run(self):
        Helper = pantry["myapp.module_b.Helper"]  # import happens here
        return Helper()

This does not make external dependencies lazy. It is specifically for breaking circular imports between your own modules. Bridge toward PEP 690.

Lazy imports are separate from external dependencies. has(), get(), report() do not interact with them.

Testing with simulate_missing

def test_fallback():
    with pantry.simulate_missing("numpy"):
        assert pantry.has("numpy") is False
    assert pantry.has("numpy") is True

API Summary

External Dependencies

Syntax Description
pantry.has("pkg") True if installed (metadata check, no import)
pantry.has("p1", "p2") True if all are installed
pantry["pkg"] Import module; raise RuntimeError if missing
pantry.get("pkg") Import module; return None if missing
pantry.get("pkg", default) Import module; return default if missing
pantry.version("pkg") Installed version string, or None
@pantry("p1", "p2") Decorator; RuntimeError at call-time if missing
pantry.report(...) Formatted availability table

Lazy Import (own modules)

Syntax Description
pantry.lazy_import("a.b.C") Register for deferred import
pantry["a.b.C"] Resolve on first access (cached)

Testing

Syntax Description
with pantry.simulate_missing("pkg") Temporarily hide packages

Key Features

  1. Zero dependencies — pure Python standard library
  2. ~300 lines — one file, fully readable
  3. No configuration — no pyproject.toml scanning, works everywhere
  4. On-demand probingimport pantry is instant, no startup cost
  5. Smart resolution — pip names mapped to import names automatically
  6. Multiple access patterns — strict ([]), safe (.get()), check (.has())
  7. Decorator guards — fail at call-time with clear install instructions
  8. Lazy import — break circular dependencies in your own modules
  9. simulate_missing — context manager for testing fallback behavior
  10. Fully typed — PEP 561 py.typed marker included

Documentation

Full documentation: mypantry.readthedocs.io

Testing

pip install mypantry[dev]
pytest

Repository Structure

genro-pantry/
├── src/pantry/
│   ├── __init__.py       # Module-as-instance bootstrap (25 lines)
│   ├── _registry.py      # The entire API (303 lines)
│   └── py.typed          # PEP 561 marker
├── tests/                # 432 lines, 57 tests, 87% coverage
├── docs/
├── pyproject.toml
└── LICENSE

Project Status

  • Status: Beta
  • Python: 3.11, 3.12, 3.13
  • License: MIT

Contributing

Contributions and feedback welcome. Please open an issue first.

License

Copyright (c) 2025 Softwell S.r.l. — 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

mypantry-0.5.1.tar.gz (20.6 kB view details)

Uploaded Source

Built Distribution

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

mypantry-0.5.1-py3-none-any.whl (9.2 kB view details)

Uploaded Python 3

File details

Details for the file mypantry-0.5.1.tar.gz.

File metadata

  • Download URL: mypantry-0.5.1.tar.gz
  • Upload date:
  • Size: 20.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for mypantry-0.5.1.tar.gz
Algorithm Hash digest
SHA256 5dc9decb45982f0f1596ff99bf6dfaf20eda40a65f740c053a184df9bcd23bd8
MD5 8e0478def9d05d57072a3287204c4dca
BLAKE2b-256 93e6c62ebc7f1197d58ee3dae3152aed0017aacd882fb60e440b5797cde19e4a

See more details on using hashes here.

Provenance

The following attestation bundles were made for mypantry-0.5.1.tar.gz:

Publisher: publish.yml on genropy/genro-pantry

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mypantry-0.5.1-py3-none-any.whl.

File metadata

  • Download URL: mypantry-0.5.1-py3-none-any.whl
  • Upload date:
  • Size: 9.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for mypantry-0.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e717e72bc894c4ac501f7cb0aac6d9b5cef6a0f94e12ff42707c7fecaeade73b
MD5 10ca4b5f40c5324b1f59ea48360eff74
BLAKE2b-256 9b1a2a990e99bb043b9dbcd781a939ff2f96085ade1293c983e9ac8f8e46656c

See more details on using hashes here.

Provenance

The following attestation bundles were made for mypantry-0.5.1-py3-none-any.whl:

Publisher: publish.yml on genropy/genro-pantry

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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