Usage Guide
Checking Availability
Single Package
import pantry
if pantry.has("numpy"):
import numpy as np
has() checks distribution metadata only — it does not import the module.
This is fast and safe to call at module top level.
Multiple Packages
if pantry.has("numpy", "pandas"):
import numpy as np
import pandas as pd
Returns True only if all listed packages are installed.
Importing Modules
Strict Import — pantry[pkg]
Raises RuntimeError with install instructions if the package is missing:
PIL = pantry["pillow"]
If pillow is not installed:
RuntimeError: Package 'pillow' is not available. Install with: pip install pillow
Safe Import — pantry.get(pkg)
Returns None (or a custom default) if the package is missing:
np = pantry.get("numpy")
redis = pantry.get("redis", None)
Comparison
Pattern |
Missing package |
Use when |
|---|---|---|
|
Import skipped |
Guard at top of file |
|
|
Feature requires the package |
|
Returns |
You have a fallback |
Configuration-Driven Imports
When the package name comes from a configuration file or database, pantry loads it dynamically in one line:
import pantry
# The driver name comes from site configuration
driver_name = config.get("printer_driver") # e.g. "zebra-driver"
driver = pantry[driver_name] # import or RuntimeError
If the driver is not installed, the error message tells the user exactly
what to do: RuntimeError: Package 'zebra-driver' is not available. Install with: pip install zebra-driver
This is common in industrial/enterprise applications where different deployments have different hardware — printers, PLC controllers, barcode readers, scales — each with its own optional driver package:
import pantry
# Load all drivers specified in site config
drivers = {}
for device, pkg_name in site_config["devices"].items():
if pantry.has(pkg_name):
drivers[device] = pantry[pkg_name]
else:
log.warning(f"Driver {pkg_name} not installed for {device}")
Decorator
Guard functions so they fail at call-time with a clear message:
@pantry("numpy", "pandas")
def analyze(data):
import numpy as np
import pandas as pd
...
Or with the recommended pattern (imports at top level):
if pantry.has("numpy"):
import numpy as np
if pantry.has("pandas"):
import pandas as pd
@pantry("numpy", "pandas")
def analyze(data):
df = pd.DataFrame(data)
return np.mean(df.values)
The decorator preserves __name__, __doc__, and other function metadata.
Version Checking
ver = pantry.version("numpy") # "1.26.4" or None
Reporting
Check Specific Packages
print(pantry.report("numpy", "pandas", "pillow", "redis"))
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
Report All Queried Packages
# After using has(), get(), or []
print(pantry.report())
Shows all packages you have queried during the session.
Module Name Resolution
Pantry automatically resolves pip package names to their importable module names.
pip name |
import name |
How resolved |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
hyphen → underscore fallback |
Lazy Import — Breaking Circular Dependencies
A separate feature for your own project modules. Does not make external dependencies lazy.
The Problem
# myapp/module_a.py
from myapp.module_b import Helper # module_b imports module_a -> circular!
The Solution
# 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()
Lazy imports are accessed via pantry["dotted.path"] only. has(), get(),
and report() do not interact with them.
Bridge toward PEP 690.
Testing with simulate_missing
def test_fallback():
with pantry.simulate_missing("numpy"):
assert pantry.has("numpy") is False
assert pantry.get("numpy") is None
assert pantry.has("numpy") is True
Works with decorators too:
@pantry("numpy")
def compute():
...
def test_missing():
with pantry.simulate_missing("numpy"):
with pytest.raises(RuntimeError):
compute()
Exception-safe — original state is always restored.
Next Steps
API Reference — full API reference
Architecture — how it works internally