Skip to main content
Help us improve PyPI by participating in user testing. All experience levels needed!

Python dependency injection framework

Project description

Dependency injection the python way, the good way. Not a port of Guice or Spring.

Key features

  • Fast.
  • Thread-safe.
  • Simple to use.
  • Does not steal class constructors.
  • Does not try to manage your application object graph.
  • Transparently integrates into tests.
  • Supports Python 2.7 and Python 3.3+.



pip install inject


# Import the inject module.
import inject

# `inject.instance` requests dependencies from the injector.
def foo(bar):
    cache = inject.instance(Cache)'bar', bar)

# `inject.params` injects dependencies as keyword arguments or positional argument.
@inject.params(cache=Cache, user=CurrentUser)
def baz(foo, cache=None, user=None):'foo', foo, user)

# this can be called in different ways:
# with injected arguments

# with positional arguments
baz('foo', my_cache)

# with keyword arguments
baz('foo', my_cache, user=current_user)

# `inject.param` is deprecated, use `inject.params` instead.
@inject.param('cache', Cache)
def bar(foo, cache=None):'foo', foo)

# `inject.attr` creates properties (descriptors) which request dependencies on access.
class User(object):
    cache = inject.attr(Cache)

    def __init__(self, id): = id

    def save(self):'users', self)

    def load(cls, id):
        return cls.cache.load('users', id)

# Create an optional configuration.
def my_config(binder):
    binder.install(my_config2)  # Add bindings from another config.
    binder.bind(Cache, RedisCache('localhost:1234'))

# Configure a shared injector.

# Instantiate User as a normal class. Its `cache` dependency is injected when accessed.
user = User(10)

# Call the functions, the dependencies are automatically injected.

Usage with Django

Django can load some modules multiple times which can lead to InjectorException: Injector is already configured. You can use configure_once which is guaranteed to run only once when the injector is absent:

import inject


In tests use inject.clear_and_configure(callable) to create a new injector on setup, and optionally inject.clear() to clean up on tear down:

class MyTest(unittest.TestCase):
    def setUp(self):
        inject.clear_and_configure(lambda binder: binder
            .bind(Cache, Mock() \
            .bind(Validator, TestValidator())

    def tearDown(self):


After configuration the injector is thread-safe and can be safely reused by multiple threads.

Binding types

  • Instance bindings always return the same instance:

    redis = RedisCache(address='localhost:1234')
    def config(binder):
        binder.bind(Cache, redis)
  • Constructor bindings create a singleton on injection:

    def config(binder):
        # Creates a redis cache singleton on first injection.
        binder.bind_to_constructor(Cache, lambda: RedisCache(address='localhost:1234'))
  • Provider bindings call the provider on injection:

    def get_my_thread_local_cache():
    def config(binder):
        # Executes the provider on each injection.
        binder.bind_to_provider(Cache, get_my_thread_local_cache)
  • Runtime bindings automatically create singletons on injection, require no configuration. For example, only the Config class binding is present, other bindings are runtime:

    class Config(object):
    class Cache(object):
        config = inject.attr(Config)
    class Db(object):
        config = inject.attr(Config)
    class User(object):
        cache = inject.attr(Cache)
        db = inject.attr(Db)
        def load(cls, user_id):
            return cls.cache.load('users', user_id) or cls.db.load('users', user_id)
    inject.configure(lambda binder: binder.bind(Config, load_config_file()))
    user = User.load(10)


It is possible to use any hashable object as a binding key. For example:

import inject

inject.configure(lambda binder: \
    binder.bind('host', 'localhost') \
    binder.bind('port', 1234))

Why no scopes?

I’ve used Guice and Spring in Java for a lot of years, and I don’t like their scopes. python-inject by default creates objects as singletons. It does not need a prototype scope as in Spring or NO_SCOPE as in Guice because python-inject does not steal your class constructors. Create instances the way you like and then inject dependencies into them.

Other scopes such as a request scope or a session scope are fragile, introduce high coupling, and are difficult to test. In python-inject write custom providers which can be thread-local, request-local, etc.

For example, a thread-local current user provider:

import inject
import threading

# Given a user class.
class User(object):

# Create a thread-local current user storage.
_LOCAL = threading.local()

def get_current_user():
    return getattr(_LOCAL, 'user', None)

def set_current_user(user):
    _LOCAL.user = user

# Bind User to a custom provider.
inject.configure(lambda binder: binder.bind_to_provider(User, get_current_user))

# Inject the current user.
def foo(user):


Apache License 2.0

Project details

Release history Release notifications

This version
History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


History Node


Download files

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

Filename, size & hash SHA256 hash help File type Python version Upload date
Inject-3.3.2.tar.gz (10.1 kB) Copy SHA256 hash SHA256 Source None Sep 14, 2017

Supported by

Elastic Elastic Search Pingdom Pingdom Monitoring Google Google BigQuery Sentry Sentry Error logging CloudAMQP CloudAMQP RabbitMQ AWS AWS Cloud computing Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page