Skip to main content

InjectQ

PyPI version Python versions License Coverage

InjectQ is a modern, lightweight Python dependency injection library focused on clarity, type-safety, and seamless framework integration.

Documentation

Full documentation is hosted at Documentation and the repository docs/ contains the source.

Key features

  • Simplicity-first dict-like API for quick starts
  • Flexible decorator- and type-based injection (@inject, Inject[T])
  • Type-friendly: designed to work with static type checkers
  • Built-in integrations for frameworks (FastAPI, Taskiq) as optional extras
  • Factory and async factory support
  • 🆕 Hybrid factory methods combining DI with manual arguments (invoke(), ainvoke())
  • Scope management and testing utilities
  • Supports Python 3.10 through 3.15, including Python 3.15 lazy imports (PEP 810)

Prefer the exported global InjectQ.get_instance() container in examples and application code. It uses the active context container when present, otherwise falls back to a global singleton.

from injectq import InjectQ, inject, singleton

container = InjectQ.get_instance()

# Basic value binding
container[str] = "Hello, World!"

@singleton
class UserService:
    def __init__(self, message: str):
        self.message = message

    def greet(self) -> str:
        return f"Service says: {self.message}"

@inject
def main(service: UserService) -> None:
    print(service.greet())

if __name__ == "__main__":
    main()  # Prints: Service says: Hello, World!

Notes:

  • Use container[...] for simple bindings and values.
  • Use @inject and Inject[T] for function/class injection.

Enhanced Features

Nullable Dependencies

InjectQ supports binding None values for optional dependencies using the allow_none parameter:

from injectq import InjectQ

container = InjectQ()

# Optional service - can be None
class EmailService:
    def send_email(self, to: str, message: str) -> str:
        return f"Email sent to {to}: {message}"

class NotificationService:
    def __init__(self, email_service: EmailService | None = None):
        self.email_service = email_service

    def notify(self, message: str) -> str:
        if self.email_service:
            return self.email_service.send_email("user", message)
        return f"Basic notification: {message}"

# Bind None for optional dependency
container.bind(EmailService, None, allow_none=True)
container.bind(NotificationService, NotificationService)

service = container.get(NotificationService)
print(service.notify("Hello"))  # Prints: Basic notification: Hello

Abstract Class Validation

InjectQ automatically prevents binding abstract classes and raises a BindingError during binding (not at resolution time):

from abc import ABC, abstractmethod
from injectq import InjectQ
from injectq.utils.exceptions import BindingError

class PaymentProcessor(ABC):  # Abstract class
    @abstractmethod
    def process_payment(self, amount: float) -> str:
        pass

class CreditCardProcessor(PaymentProcessor):  # Concrete implementation
    def process_payment(self, amount: float) -> str:
        return f"Processing ${amount} via credit card"

container = InjectQ()

# This will raise BindingError immediately
try:
    container.bind(PaymentProcessor, PaymentProcessor)  # Error!
except BindingError:
    print("Cannot bind abstract class")

# This works fine
container.bind(PaymentProcessor, CreditCardProcessor)  # OK

See examples/enhanced_features_demo.py for a complete demonstration.

🆕 Hybrid Factory Methods

The new invoke() and ainvoke() methods combine dependency injection with manual arguments:

from injectq import InjectQ

container = InjectQ()
container.bind(Database, Database)
container.bind(Cache, Cache)

# Factory that needs both DI dependencies and runtime arguments
def create_user_service(db: Database, cache: Cache, user_id: str):
    return UserService(db, cache, user_id)

container.bind_factory("user_service", create_user_service)

# ❌ Old way - verbose
db = container[Database]
cache = container[Cache]
service = container.call_factory("user_service", db, cache, "user123")

# ✅ New way - automatic DI + manual args
service = container.invoke("user_service", user_id="user123")
# Database and Cache auto-injected, only provide user_id!

# Also works with async
service = await container.ainvoke("async_service", batch_size=100)

When to use invoke():

  • Factory needs some DI dependencies + some runtime arguments
  • You want cleaner code without manual resolution
  • Mix configuration from container with user input

See examples/factory_api_showcase.py and docs/injection-patterns/factory-methods.md for details.

Installation

Install from PyPI:

pip install injectq

Optional framework integrations (install only what you need):

pip install injectq[fastapi]   # FastAPI integration (optional)
pip install injectq[taskiq]    # Taskiq integration (optional)

Supported Python versions

InjectQ supports Python 3.10 and above, and is tested on 3.10 through 3.15.

Python 3.15 lazy imports

InjectQ 0.5.0+ works with Python 3.15 explicit lazy imports (PEP 810). Your imports and decorators stay the same. Lazy imports pay off for your own slow or rarely used modules (here, reports). You can turn them on in three ways:

# 1. Per module: list modules to import lazily. Ignored on Python 3.10-3.14.
__lazy_modules__ = ["reports"]
import reports

# 2. Per import: the `lazy` keyword. Python 3.15+ only (SyntaxError before).
lazy import reports
# 3. Whole process
PYTHON_LAZY_IMPORTS=all python app.py   # or: python -X lazy_imports=all app.py

One thing to check: @singleton, @transient, @scoped, @register_as and top-level container.bind(...) register services when their module is imported. With lazy imports, a module you import only for that registration (import services) is never loaded, so resolving its services fails with DependencyNotFoundError. Load such modules eagerly before resolving, for example:

import importlib

importlib.import_module("services")  # always runs immediately

See Python 3.15 Lazy Imports for the other fixes and examples/python315_lazy_imports/ for a runnable demo, and test your whole application before turning on global lazy mode in production.

Where to look next

  • docs/getting-started/installation.md — installation and verification
  • docs/getting-started/lazy-imports.md — Python 3.15 lazy imports (runnable example)
  • docs/injection-patterns/dict-interface.md — dict-like API
  • docs/injection-patterns/inject-decorator.md — @inject usage
  • docs/injection-patterns/factory-methods.md — factory patterns (DI, parameterized, hybrid)
  • docs/integrations/ — integration guides for FastAPI and Taskiq

License

MIT — see the LICENSE file.

Run tests with coverage

Activate the project's virtualenv and run pytest (coverage threshold is configured to 73%):

source .venv/bin/activate
python -m pytest

Coverage reports are written to htmlcov/ and coverage.xml.

Performance Benchmarks

InjectQ includes comprehensive performance benchmarks to ensure production-ready performance:

# Run all benchmarks
pytest tests/test_benchmarks.py --benchmark-only

# Run with verbose statistics
pytest tests/test_benchmarks.py --benchmark-only --benchmark-verbose

# Save results for comparison
pytest tests/test_benchmarks.py --benchmark-only --benchmark-autosave

Performance Highlights

  • Ultra-fast operations: Basic operations (bind, get, has) execute in 270-780 nanoseconds
  • Efficient resolution: Dependency resolution completes in ~1 microsecond
  • Excellent scalability: Handles 1,000+ operations with sub-millisecond performance
  • Thread-safe: Concurrent access with minimal overhead (~24 μs)
  • Production-ready: Web request simulation (10 services) completes in 142 microseconds

📊 See BENCHMARK_REPORT.md for detailed analysis and BENCHMARK_QUICK_GUIDE.md for usage guide.

Metadata

Release files for injectq 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for injectq 0.5.0
File Size Uploaded Provenance
injectq-0.5.0.tar.gz 2.3 MB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for injectq 0.5.0
File Interpreter ABI Platform
injectq-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 2.3 MB

Release files / injectq-0.5.0.tar.gz

Download URL injectq-0.5.0.tar.gz
Size 2.3 MB
Tags Source
SHA-256 checksum
How to use checksums
39a151c0a76fc15b6a80ab45a0c9264be1a04b10043289844e5e48e93ecd0f79
BLAKE2b-256 checksum
How to use checksums
6f546df21482f6a35b4d51fb6ef0eac303683997dfbb00702924461df94797e7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.15.0

Release files / injectq-0.5.0-py3-none-any.whl

Download URL injectq-0.5.0-py3-none-any.whl
Size 76.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
05a0f23950cff623b49651e9d98de63b9ad6b2a703d3474020a5dc5b05cc16b6
BLAKE2b-256 checksum
How to use checksums
96436ccbcd851571f8f36ed8ed629747cabbe32f6052482faf6127ae6b026fca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.15.0

Security for injectq 0.5.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.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page