Metadata-Version: 2.1
Name: injector
Version: 0.20.1
Summary: Injector - Python dependency injection framework, inspired by Guice
Home-page: https://github.com/alecthomas/injector
Download-URL: https://pypi.org/project/injector/
Author: Alec Thomas
Author-email: alec@swapoff.org
License: BSD
Keywords: Dependency Injection,DI,Dependency Injection framework,Inversion of Control,IoC,Inversion of Control container
Platform: any
Provides-Extra: dev
License-File: COPYING

Injector - Python dependency injection framework, inspired by Guice
===================================================================

[![image](https://github.com/alecthomas/injector/workflows/CI/badge.svg)](https://github.com/alecthomas/injector/actions?query=workflow%3ACI+branch%3Amaster)
[![Coverage Status](https://codecov.io/gh/alecthomas/injector/branch/master/graph/badge.svg)](https://codecov.io/gh/alecthomas/injector)

Introduction
------------

While dependency injection is easy to do in Python due to its support for keyword arguments, the ease with which objects can be mocked and its dynamic nature, a framework for assisting in this process can remove a lot of boiler-plate from larger applications. That's where Injector can help. It automatically and transitively provides dependencies for you. As an added benefit, Injector encourages nicely compartmentalised code through the use of :ref:`modules <module>`.

If you're not sure what dependency injection is or you'd like to learn more about it see:

* [The Clean Code Talks - Don't Look For Things! (a talk by Miško Hevery)](
  https://www.youtube.com/watch?v=RlfLCWKxHJ0)
* [Inversion of Control Containers and the Dependency Injection pattern (an article by Martin Fowler)](
  https://martinfowler.com/articles/injection.html)

The core values of Injector are:

* Simplicity - while being inspired by Guice, Injector does not slavishly replicate its API.
  Providing a Pythonic API trumps faithfulness. Additionally some features are omitted
  because supporting them would be cumbersome and introduce a little bit too much "magic"
  (member injection, method injection).

  Connected to this, Injector tries to be as nonintrusive as possible. For example while you may
  declare a class' constructor to expect some injectable parameters, the class' constructor
  remains a standard constructor – you may instantiate the class just the same manually, if you want.

* No global state – you can have as many [Injector](https://injector.readthedocs.io/en/latest/api.html#injector.Injector)
  instances as you like, each with a different configuration and each with different objects in different
  scopes. Code like this won't work for this very reason:

  ```python
    class MyClass:
        @inject
        def __init__(t: SomeType):
            # ...

    MyClass()
  ```

  This is simply because there's no global `Injector` to use. You need to be explicit and use
  [Injector.get](https://injector.readthedocs.io/en/latest/api.html#injector.Injector.get),
  [Injector.create_object](https://injector.readthedocs.io/en/latest/api.html#injector.Injector.create_object)
  or inject `MyClass` into the place that needs it.

* Cooperation with static type checking infrastructure – the API provides as much static type safety
  as possible and only breaks it where there's no other option. For example the
  [Injector.get](https://injector.readthedocs.io/en/latest/api.html#injector.Injector.get) method
  is typed such that `injector.get(SomeType)` is statically declared to return an instance of
  `SomeType`, therefore making it possible for tools such as [mypy](https://github.com/python/mypy) to
  type-check correctly the code using it.
  
* The client code only knows about dependency injection to the extent it needs – 
  [`inject`](https://injector.readthedocs.io/en/latest/api.html#injector.inject),
  [`Inject`](https://injector.readthedocs.io/en/latest/api.html#injector.Inject) and
  [`NoInject`](https://injector.readthedocs.io/en/latest/api.html#injector.NoInject) are simple markers
  that don't really do anything on their own and your code can run just fine without Injector
  orchestrating things.

### How to get Injector?

* GitHub (code repository, issues): https://github.com/alecthomas/injector

* PyPI (installable, stable distributions): https://pypi.org/project/injector/. You can install it using pip:

  ```bash
  pip install injector
  ```

* Documentation: https://injector.readthedocs.org
* Change log: https://injector.readthedocs.io/en/latest/changelog.html

Injector works with CPython 3.7+ and PyPy 3 implementing Python 3.7+.

A Quick Example
---------------


```python
>>> from injector import Injector, inject
>>> class Inner:
...     def __init__(self):
...         self.forty_two = 42
...
>>> class Outer:
...     @inject
...     def __init__(self, inner: Inner):
...         self.inner = inner
...
>>> injector = Injector()
>>> outer = injector.get(Outer)
>>> outer.inner.forty_two
42

```

Or with `dataclasses` if you like:

```python
from dataclasses import dataclass
from injector import Injector, inject
class Inner:
    def __init__(self):
        self.forty_two = 42

@inject
@dataclass
class Outer:
    inner: Inner

injector = Injector()
outer = injector.get(Outer)
print(outer.inner.forty_two)  # Prints 42
```


A Full Example
--------------

Here's a full example to give you a taste of how Injector works:


```python
>>> from injector import Module, provider, Injector, inject, singleton

```

We'll use an in-memory SQLite database for our example:


```python
>>> import sqlite3

```

And make up an imaginary `RequestHandler` class that uses the SQLite connection:


```python
>>> class RequestHandler:
...   @inject
...   def __init__(self, db: sqlite3.Connection):
...     self._db = db
...
...   def get(self):
...     cursor = self._db.cursor()
...     cursor.execute('SELECT key, value FROM data ORDER by key')
...     return cursor.fetchall()

```

Next, for the sake of the example, we'll create a configuration type:


```python
>>> class Configuration:
...     def __init__(self, connection_string):
...         self.connection_string = connection_string

```

Next, we bind the configuration to the injector, using a module:


```python
>>> def configure_for_testing(binder):
...     configuration = Configuration(':memory:')
...     binder.bind(Configuration, to=configuration, scope=singleton)

```

Next we create a module that initialises the DB. It depends on the configuration provided by the above module to create a new DB connection, then populates it with some dummy data, and provides a `Connection` object:


```python
>>> class DatabaseModule(Module):
...   @singleton
...   @provider
...   def provide_sqlite_connection(self, configuration: Configuration) -> sqlite3.Connection:
...     conn = sqlite3.connect(configuration.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

```

(Note how we have decoupled configuration from our database initialisation code.)

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`:


```python
>>> injector = Injector([configure_for_testing, DatabaseModule()])
>>> handler = injector.get(RequestHandler)
>>> tuple(map(str, handler.get()[0]))  # py3/py2 compatibility hack
('hello', 'world')

```

We can also verify that our `Configuration` and `SQLite` connections are indeed singletons within the Injector:


```python
>>> injector.get(Configuration) is injector.get(Configuration)
True
>>> injector.get(sqlite3.Connection) is injector.get(sqlite3.Connection)
True

```

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:

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.

Footnote
--------

This framework is similar to snake-guice, but aims for simplification.

&copy; Copyright 2010-2013 to Alec Thomas, under the BSD license
