2012-11-16 14:38:57 +00:00
|
|
|
# Injector - Python dependency injection framework, inspired by Guice
|
2010-11-29 04:54:33 +00:00
|
|
|
|
2012-11-16 16:40:54 +00:00
|
|
|
[![image](https://secure.travis-ci.org/alecthomas/injector.png?branch=master)](https://travis-ci.org/alecthomas/injector/builds)
|
2012-11-13 20:37:40 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
## Introduction
|
2010-11-29 04:54:33 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
|
|
|
|
Dependency injection as a formal pattern is less useful in Python than
|
|
|
|
in other languages, primarily due to its support for keyword arguments,
|
|
|
|
the ease with which objects can be mocked, and its dynamic nature.
|
2010-11-25 14:04:56 +00:00
|
|
|
|
2010-11-27 00:19:16 +00:00
|
|
|
That said, a framework for assisting in this process can remove a lot of
|
2012-11-16 14:38:57 +00:00
|
|
|
boiler-plate from larger applications. That's where Injector can help.
|
|
|
|
It automatically and transitively provides keyword arguments with their
|
|
|
|
values. As an added benefit, Injector encourages nicely
|
|
|
|
compartmentalised code through the use of `Module` s.
|
2010-11-27 00:19:16 +00:00
|
|
|
|
2010-11-25 14:33:37 +00:00
|
|
|
While being inspired by Guice, it does not slavishly replicate its API.
|
2010-11-25 22:27:18 +00:00
|
|
|
Providing a Pythonic API trumps faithfulness.
|
2010-11-25 14:33:37 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
## A Full Example
|
|
|
|
|
|
|
|
Here's a full example to give you a taste of how Injector works:
|
2012-02-23 16:28:33 +00:00
|
|
|
|
|
|
|
>>> from injector import Module, Key, provides, Injector, inject, singleton
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
We'll use an in-memory SQLite database for our example:
|
2012-02-23 16:28:33 +00:00
|
|
|
|
|
|
|
>>> import sqlite3
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
And make up an imaginary RequestHandler class that uses the SQLite
|
|
|
|
connection:
|
2012-02-23 16:28:33 +00:00
|
|
|
|
|
|
|
>>> class RequestHandler(object):
|
|
|
|
... @inject(db=sqlite3.Connection)
|
|
|
|
... def __init__(self, db):
|
|
|
|
... self._db = db
|
|
|
|
... def get(self):
|
|
|
|
... cursor = self._db.cursor()
|
|
|
|
... cursor.execute('SELECT key, value FROM data ORDER by key')
|
|
|
|
... return cursor.fetchall()
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
Next, for the sake of the example, we'll create a "configuration"
|
|
|
|
annotated type:
|
2012-02-23 16:28:33 +00:00
|
|
|
|
|
|
|
>>> Configuration = Key('configuration')
|
|
|
|
>>> class ConfigurationForTestingModule(Module):
|
|
|
|
... def configure(self, binder):
|
|
|
|
... binder.bind(Configuration, to={'db_connection_string': ':memory:'},
|
|
|
|
... scope=singleton)
|
|
|
|
|
|
|
|
Next we create our database module that initialises the DB based on the
|
2012-11-16 14:38:57 +00:00
|
|
|
configuration provided by the above module, populates it with some dummy
|
|
|
|
data, and provides a Connection object:
|
2012-02-23 16:28:33 +00:00
|
|
|
|
|
|
|
>>> class DatabaseModule(Module):
|
|
|
|
... @singleton
|
|
|
|
... @provides(sqlite3.Connection)
|
|
|
|
... @inject(configuration=Configuration)
|
|
|
|
... def provide_sqlite_connection(self, configuration):
|
|
|
|
... conn = sqlite3.connect(configuration['db_connection_string'])
|
|
|
|
... cursor = conn.cursor()
|
|
|
|
... cursor.execute('CREATE TABLE IF NOT EXISTS data (key PRIMARY KEY, value)')
|
|
|
|
... cursor.execute('INSERT OR REPLACE INTO data VALUES ("hello", "world")')
|
|
|
|
... return conn
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
(Note how we have decoupled configuration from our database
|
|
|
|
initialisation code.)
|
2012-02-23 16:28:33 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
Finally, we initialise an Injector and use it to instantiate a
|
|
|
|
RequestHandler instance. This first transitively constructs a
|
|
|
|
sqlite3.Connection object, and the Configuration dictionary that it in
|
|
|
|
turn requires, then instantiates our RequestHandler:
|
2012-02-23 16:28:33 +00:00
|
|
|
|
|
|
|
>>> injector = Injector([ConfigurationForTestingModule(), DatabaseModule()])
|
|
|
|
>>> handler = injector.get(RequestHandler)
|
2012-07-11 18:23:58 +00:00
|
|
|
>>> tuple(map(str, handler.get()[0])) # py3/py2 compatibility hack
|
|
|
|
('hello', 'world')
|
2012-02-23 16:28:33 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
We can also veryify that our Configuration and SQLite connections are
|
|
|
|
indeed singletons within the Injector:
|
2012-02-23 16:28:33 +00:00
|
|
|
|
|
|
|
>>> injector.get(Configuration) is injector.get(Configuration)
|
|
|
|
True
|
|
|
|
>>> injector.get(sqlite3.Connection) is injector.get(sqlite3.Connection)
|
|
|
|
True
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
You're probably thinking something like: "this is a large amount of work
|
|
|
|
just to give me a database connection", and you are correct; dependency
|
|
|
|
injection is typically not that useful for smaller projects. It comes
|
|
|
|
into its own on large projects where the up-front effort pays for itself
|
|
|
|
in two ways:
|
2012-02-23 16:28:33 +00:00
|
|
|
|
2012-11-16 15:20:13 +00:00
|
|
|
1. Forces decoupling. In our example, this is illustrated by
|
|
|
|
decoupling our configuration and database configuration.
|
|
|
|
2. After a type is configured, it can be injected anywhere with no
|
|
|
|
additional effort. Simply @inject and it appears. We don't really
|
|
|
|
illustrate that here, but you can imagine adding an arbitrary
|
|
|
|
number of RequestHandler subclasses, all of which will
|
|
|
|
automatically have a DB connection provided.
|
2012-02-23 16:28:33 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
## Terminology
|
|
|
|
|
|
|
|
At its heart, Injector is simply a dictionary for mapping types to
|
|
|
|
things that create instances of those types. This could be as simple as:
|
2010-11-27 13:27:26 +00:00
|
|
|
|
|
|
|
{str: 'an instance of a string'}
|
|
|
|
|
|
|
|
For those new to dependency-injection and/or Guice, though, some of the
|
|
|
|
terminology used may not be obvious.
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
### Provider
|
|
|
|
|
2010-11-27 13:27:26 +00:00
|
|
|
A means of providing an instance of a type. Built-in providers include
|
2012-11-16 14:38:57 +00:00
|
|
|
`ClassProvider` (creates a new instance from a class),
|
|
|
|
`InstanceProvider` (returns an existing instance directly) and
|
|
|
|
`CallableProvider` (provides an instance by calling a function).
|
2010-11-27 00:19:16 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
### Scope
|
2010-11-25 14:33:37 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
By default, providers are executed each time an instance is required.
|
|
|
|
Scopes allow this behaviour to be customised. For example,
|
|
|
|
`SingletonScope` (typically used through the class decorator
|
|
|
|
`singleton`), can be used to always provide the same instance of a
|
|
|
|
class.
|
2010-11-25 22:27:18 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
Other examples of where scopes might be a threading scope, where
|
|
|
|
instances are provided per-thread, or a request scope, where instances
|
|
|
|
are provided per-HTTP-request.
|
2010-11-25 14:33:37 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
The default scope is `NoScope`.
|
2010-11-25 14:33:37 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
### Binding Key
|
|
|
|
|
|
|
|
A binding key uniquely identifies a provider of a type. It is
|
|
|
|
effectively a tuple of `(type, annotation)` where `type` is the type to
|
|
|
|
be provided and `annotation` is additional, optional, uniquely
|
|
|
|
identifying information for the type.
|
|
|
|
|
|
|
|
For example, the following are all unique binding keys for `str`:
|
2010-11-25 14:33:37 +00:00
|
|
|
|
2010-11-27 13:27:26 +00:00
|
|
|
(str, 'name')
|
|
|
|
(str, 'description')
|
2010-11-25 14:33:37 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
For a generic type such as `str`, annotations are very useful for unique
|
2010-11-27 13:27:26 +00:00
|
|
|
identification.
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
As an *alternative* convenience to using annotations, `Key` may be used
|
|
|
|
to create unique types as necessary:
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2012-02-23 16:28:33 +00:00
|
|
|
>>> from injector import Key
|
2010-11-27 13:27:26 +00:00
|
|
|
>>> Name = Key('name')
|
|
|
|
>>> Description = Key('description')
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
Which may then be used as binding keys, without annotations, as they
|
|
|
|
already uniquely identify a particular provider:
|
2010-11-27 13:27:26 +00:00
|
|
|
|
|
|
|
(Name, None)
|
|
|
|
(Description, None)
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
Though of course, annotations may still be used with these types, like
|
|
|
|
any other type.
|
|
|
|
|
|
|
|
### Annotation
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
An annotation is additional unique information about a type to avoid
|
|
|
|
binding key collisions. It creates a new unique binding key for an
|
|
|
|
existing type.
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
### Binding
|
|
|
|
|
|
|
|
A binding is the mapping of a unique binding key to a corresponding
|
|
|
|
provider. For example:
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2012-02-23 16:28:33 +00:00
|
|
|
>>> from injector import InstanceProvider
|
2010-11-27 13:27:26 +00:00
|
|
|
>>> bindings = {
|
|
|
|
... (Name, None): InstanceProvider('Sherlock'),
|
2012-11-13 17:50:46 +00:00
|
|
|
... (Description, None): InstanceProvider('A man of astounding insight'),
|
2010-11-27 13:27:26 +00:00
|
|
|
... }
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
### Binder
|
|
|
|
|
|
|
|
The `Binder` is simply a convenient wrapper around the dictionary that
|
|
|
|
maps types to providers. It provides methods that make declaring
|
|
|
|
bindings easier.
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
### Module
|
|
|
|
|
|
|
|
A `Module` configures bindings. It provides methods that simplify the
|
|
|
|
process of binding a key to a provider. For example the above bindings
|
|
|
|
would be created with:
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2012-02-23 16:28:33 +00:00
|
|
|
>>> from injector import Module
|
2010-11-27 13:27:26 +00:00
|
|
|
>>> class MyModule(Module):
|
|
|
|
... def configure(self, binder):
|
|
|
|
... binder.bind(Name, to='Sherlock')
|
|
|
|
... binder.bind(Description, to='A man of astounding insight')
|
|
|
|
|
|
|
|
For more complex instance construction, methods decorated with
|
2012-11-16 14:38:57 +00:00
|
|
|
`@provides` will be called to resolve binding keys:
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2012-02-23 16:28:33 +00:00
|
|
|
>>> from injector import provides
|
2010-11-27 13:27:26 +00:00
|
|
|
>>> class MyModule(Module):
|
|
|
|
... def configure(self, binder):
|
|
|
|
... binder.bind(Name, to='Sherlock')
|
|
|
|
...
|
|
|
|
... @provides(Description)
|
|
|
|
... def describe(self):
|
|
|
|
... return 'A man of astounding insight (at %s)' % time.time()
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
### Injection
|
|
|
|
|
|
|
|
Injection is the process of providing an instance of a type, to a method
|
|
|
|
that uses that instance. It is achieved with the `inject` decorator.
|
|
|
|
Keyword arguments to inject define which arguments in its decorated
|
|
|
|
method should be injected, and with what.
|
2010-11-27 13:27:26 +00:00
|
|
|
|
|
|
|
Here is an example of injection on a module provider method, and on the
|
2012-11-16 14:38:57 +00:00
|
|
|
constructor of a normal class:
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2012-02-23 16:28:33 +00:00
|
|
|
>>> from injector import inject
|
2010-11-27 13:27:26 +00:00
|
|
|
>>> class User(object):
|
|
|
|
... @inject(name=Name, description=Description)
|
|
|
|
... def __init__(self, name, description):
|
|
|
|
... self.name = name
|
|
|
|
... self.description = description
|
|
|
|
|
|
|
|
>>> class UserModule(Module):
|
|
|
|
... def configure(self, binder):
|
|
|
|
... binder.bind(User)
|
|
|
|
|
|
|
|
>>> class UserAttributeModule(Module):
|
|
|
|
... def configure(self, binder):
|
|
|
|
... binder.bind(Name, to='Sherlock')
|
|
|
|
...
|
|
|
|
... @provides(Description)
|
|
|
|
... @inject(name=Name)
|
|
|
|
... def describe(self, name):
|
|
|
|
... return '%s is a man of astounding insight' % name
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
### Injector
|
|
|
|
|
|
|
|
The `Injector` brings everything together. It takes a list of `Module`
|
|
|
|
s, and configures them with a binder, effectively creating a dependency
|
|
|
|
graph:
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2012-02-23 16:28:33 +00:00
|
|
|
>>> from injector import Injector
|
2010-11-27 13:27:26 +00:00
|
|
|
>>> injector = Injector([UserModule(), UserAttributeModule()])
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
You can also pass classes instead of instances to `Injector`, it will
|
|
|
|
instantiate them for you:
|
2012-11-04 01:46:33 +00:00
|
|
|
|
|
|
|
>>> injector = Injector([UserModule, UserAttributeModule])
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
The injector can then be used to acquire instances of a type, either
|
|
|
|
directly:
|
2010-11-27 13:27:26 +00:00
|
|
|
|
|
|
|
>>> injector.get(Name)
|
|
|
|
'Sherlock'
|
|
|
|
>>> injector.get(Description)
|
|
|
|
'Sherlock is a man of astounding insight'
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
Or transitively:
|
2010-11-27 13:27:26 +00:00
|
|
|
|
|
|
|
>>> user = injector.get(User)
|
|
|
|
>>> isinstance(user, User)
|
|
|
|
True
|
|
|
|
>>> user.name
|
|
|
|
'Sherlock'
|
|
|
|
>>> user.description
|
|
|
|
'Sherlock is a man of astounding insight'
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
## Scopes
|
|
|
|
|
|
|
|
### Singletons
|
2012-02-23 22:07:59 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
Singletons are declared by binding them in the SingletonScope. This can
|
|
|
|
be done in three ways:
|
2012-02-23 22:07:59 +00:00
|
|
|
|
2012-11-16 15:20:13 +00:00
|
|
|
1. Decorating the class with `@singleton`.
|
|
|
|
2. Decorating a `@provides(X)` decorated Module method with `@singleton`.
|
|
|
|
3. Explicitly calling `binder.bind(X, scope=singleton)`.
|
2012-02-23 22:07:59 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
A (redunant) example showing all three methods:
|
2012-02-23 22:07:59 +00:00
|
|
|
|
|
|
|
>>> @singleton
|
|
|
|
... class Thing(object): pass
|
|
|
|
>>> class ThingModule(Module):
|
|
|
|
... def configure(self, binder):
|
|
|
|
... binder.bind(Thing, scope=singleton)
|
|
|
|
... @singleton
|
|
|
|
... @provides(Thing)
|
|
|
|
... def provide_thing(self):
|
|
|
|
... return Thing()
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
### Implementing new Scopes
|
2012-02-23 22:07:59 +00:00
|
|
|
|
2010-11-27 13:27:26 +00:00
|
|
|
In the above description of scopes, we glossed over a lot of detail. In
|
2010-11-29 04:54:33 +00:00
|
|
|
particular, how one would go about implementing our own scopes.
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
Basically, there are two steps. First, subclass `Scope` and implement
|
|
|
|
`Scope.get`:
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2012-02-23 16:28:33 +00:00
|
|
|
>>> from injector import Scope
|
2010-11-29 04:54:33 +00:00
|
|
|
>>> class CustomScope(Scope):
|
|
|
|
... def get(self, key, provider):
|
|
|
|
... return provider
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
Then create a global instance of `ScopeDecorator` to allow classes to be
|
|
|
|
easily annotated with your scope:
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2012-02-23 16:28:33 +00:00
|
|
|
>>> from injector import ScopeDecorator
|
2010-11-29 04:54:33 +00:00
|
|
|
>>> customscope = ScopeDecorator(CustomScope)
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2010-11-29 04:54:33 +00:00
|
|
|
This can be used like so:
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2012-11-16 15:20:13 +00:00
|
|
|
>>> @customscope
|
|
|
|
... class MyClass(object):
|
|
|
|
... pass
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
Scopes are bound in modules with the `Binder.bind_scope` method:
|
2010-11-29 04:54:33 +00:00
|
|
|
|
|
|
|
>>> class MyModule(Module):
|
2010-11-27 13:27:26 +00:00
|
|
|
... def configure(self, binder):
|
2010-11-29 04:54:33 +00:00
|
|
|
... binder.bind_scope(CustomScope)
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
Scopes can be retrieved from the injector, as with any other instance.
|
|
|
|
They are singletons across the life of the injector:
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2010-11-29 04:54:33 +00:00
|
|
|
>>> injector = Injector([MyModule()])
|
|
|
|
>>> injector.get(CustomScope) is injector.get(CustomScope)
|
|
|
|
True
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
For scopes with a transient lifetime, such as those tied to HTTP
|
|
|
|
requests, the usual solution is to use a thread or greenlet-local cache
|
|
|
|
inside the scope. The scope is "entered" in some low-level code by
|
|
|
|
calling a method on the scope instance that creates this cache. Once the
|
|
|
|
request is complete, the scope is "left" and the cache cleared.
|
2010-11-27 13:27:26 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
## Tests
|
2012-11-03 22:57:49 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
When you use unit test framework such as `unittest2` or `nose` you can
|
|
|
|
also profit from `injector`. However, manually creating injectors and
|
|
|
|
test classes can be quite annoying. There is, however, `with_injector`
|
|
|
|
method decorator which has parameters just as `Injector` construtor and
|
|
|
|
installs configured injector into class instance on the time of method
|
|
|
|
call:
|
2012-11-03 22:57:49 +00:00
|
|
|
|
|
|
|
>>> from injector import Module, with_injector
|
|
|
|
>>> class UsernameModule(Module):
|
|
|
|
... def configure(self, binder):
|
|
|
|
... binder.bind(str, 'Maria')
|
|
|
|
...
|
|
|
|
>>> class TestSomethingClass(object):
|
|
|
|
... @with_injector(UsernameModule())
|
|
|
|
... def setup(self):
|
|
|
|
... pass
|
|
|
|
...
|
2012-11-04 01:46:33 +00:00
|
|
|
... @inject(username=str)
|
2012-11-03 22:57:49 +00:00
|
|
|
... def test_username(self, username):
|
|
|
|
... assert (username == 'Maria')
|
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
*Each* method call re-initializes `Injector` - if you want to you can
|
|
|
|
also put `@with_injector` decorator on class constructor.
|
|
|
|
|
|
|
|
After such call all `inject`-decorated methods will work just as you'd
|
|
|
|
expect them to work.
|
2012-11-03 22:57:49 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
## Footnote
|
2012-11-03 22:57:49 +00:00
|
|
|
|
2010-11-27 00:19:16 +00:00
|
|
|
This framework is similar to snake-guice, but aims for simplification.
|
2010-11-25 14:33:37 +00:00
|
|
|
|
2012-11-16 14:38:57 +00:00
|
|
|
*Injector is © 2010 by Alec Thomas available under the Modified BSD License.*
|
2010-11-25 14:33:37 +00:00
|
|
|
|