Pythonic (py-di) Documentation Release 0.1.1

Lukas Buenger

September 19, 2013

CONTENTS

1 Contents 3 1.1 Quick start...... 3 1.2 Guide...... 3 1.3 Background...... 4 1.4 Package documentation...... 9

2 Indices and 17

Python Module Index 19

i ii Pythonic Dependency Injection (py-di) Documentation, Release 0.1.1

Python-DI is a small Dependency Injection library for Python with a limited set of features, exposing an overall explicit API and developed with simplicity and source code readability in mind. The sources can be found on GitHub, the docs are on Read The Docs. Feel free to open a ticket for any kind of request, report, feedback or question.

Note: If you are not familiar with the Dependency Injection (DI) pattern, I strongly advise you to read: • This article by Martin Fowler. • On Wikipedia: About Dependency Injection (DI) and Inversion of Control (IoC).

Warning: This project is still in alpha status. There are going to be breaking changes and test coverage is not even anywhere near a reasonable ratio.

CONTENTS 1 Pythonic Dependency Injection (py-di) Documentation, Release 0.1.1

2 CONTENTS CHAPTER ONE

CONTENTS

1.1 Quick start

1. Install Python-DI in your Python 3.3+ environment: pip install python-di

2. Write your application: class Inner(object): def __init__(self, version:’latest’): self.version= version

class Outer(object): def __init__(self, inner): self.inner= inner

3. Create an injector: from di.injectors import Injector

injector= Injector()

4. Register your dependencies: injector.bind(’version’).annotated_with(’latest’).to_value(’ 1.1.1’) injector.bind(’inner’).to_class(Inner).as_prototype()

5. Create provided objects: my_outer= injector.inject_into(Outer) print(my_outer.inner.version) # 1.1.1

Please refer to the !!!Guide!!! for further information on quite everything about Python-DI.

1.2 Guide

1.2.1 Getting started

This section gives more in-depth information on each aspect of Python-DI.

3 Pythonic Dependency Injection (py-di) Documentation, Release 0.1.1

Requirements

• Python (3.3) • nose (1.3, to run the tests) • Sphinx (1.1.3, to build the docs)

Installation

You can install Python-DI from the Package Index: pip install python-di

Alternatively you could clone the latest release from GitHub and install the package manually using Setuptools: git clone https://github.com/lukasbuenger/python-di/releases/tag/0.1.1 cd python-di # python3 or python python setup.py install

Create your Injector

The Injector is the main entry point for your application. So we create one: injector= Injector()

There are

1.3 Background

1.3.1 Motivation

Note: The following write up reflects my personal opinion. I don’t (and hopefully never will) consider any of it as the only truth. Opinions are supposed to change and the more there are, the better an eventual consensus will be. It also contains traces of humor.

Dependency Injection: A State of the (Python) Union address

Ever since I first came in touch with the Dependency Injection pattern and the ideas behind the IoC concept I’ve been using and exploring DI helpers, frameworks and tools for almost any language that I’ve worked or just messed around with. DI immediately stroke and still strikes me as a pattern so simple, yet very genius and powerful. You find mature and well maintained DI implementations for probably every popular language out there. Just to name a few established ones: • Guice () • Spring (originally for Java but lots of ports) • AngularJS (Javascript, also by Google) • Symfony (PHP)

4 Chapter 1. Contents Pythonic Dependency Injection (py-di) Documentation, Release 0.1.1

• Zend Framework (PHP) • SwiftSuspenders (ActionScript) • (.NET) In the Python world however, until very recently at least, there has never been a real main contender. There are some 40 odd packages on PyPi addressing DI, but most of them are abandoned. This is probably due to the fact, that there are a couple of more pythonic ways to achieve decoupling than an injector framework. I would even dare to say that good Python code implements simple Dependency Injection anyway. However, until very recently because last spring (2013) Pinject got released. It’s owned by (what a surprise) Google, though not (yet) an official product and as you’d expect it from a company that is heavily promoting and supporting the DI pattern, it’s a bomb! So ...

There is Pinject: Why reinventing the wheel?

There are few but very valid points against using Dependency Injection in general (and this one is even the most valid of them all). They mostly address one of the following issues: • Every DI framework has a performance footprint depending on the framework implementation and the conven- tions (many configuration files, classes etc.) it enforces. • Usually, Dependency Injection increases complexity as one has to keep track of all the DI processes on top of those your app ships with anyway. • Code depends on a Dependency Injection framework. While the first point is obviously not circumventable (even with heavy optimizing you’ll have some sort of footprint), the other two can be minimized if not even avoided. From my experience I came to believe that the complexity increase is more a psychological issue. Of course, the ideas behind the Dependency Injection pattern may not be the easiest to wrap your head around and the advantages of using DI in your apps are not that super-obvious. But still I think that the nature of the major frameworks is what keeps some people thinking of Dependency Injection as a possible complexity beast. Often over-engineered, with docs full of cryptic design decisions, offering thousands of boilerplate-heavy ways to con- figure your bindings, eventually even forcing you into previously unknown config markup territories (XML, YAML etc.); at first sight frameworks like Spring et al seem to merely complicate things than actually enhancing your devel- opment process. This leads to common resentments like “This feels so powerful and kind of just right but wow, real engineer stuff, probably two much overhead for my not THAT large-scale app.” which in turn leads a developer to drop DI with the sour feeling of not yet being ready for the world of the real bad-ass programmers which in in turn can lead to the death by heart attack of another developer, who had to pick up the pieces (which of course implement no DI at all) after the first developer decided to leave the Machine, become a public school teacher and never ever try to be bad-ass again. DI frameworks by Google like AngularJS, Guice and aforementioned Pinject all follow a very concise philosophy and address these issues to a certain extent. But they all force you in doing things a certain (at times very specific) way. One could argue that Google ways of things are designed by some of the most skilled people in the business and I couldn’t agree more, but still I wouldn’t want to be restricted to them. And I felt somehow restricted with Pinject, especially with its conventions on binding specifications. Simple things became too complicated too quickly. The API documentation is quite good, but you must find some joy in reading mostly uncommented, tightly formatted code if you want to explore the inner workings of Pinject, which I consider essential in order to understand and therefore safely use any library of choice. The implicit binding inspection feature even scared me a bit. I had a sudden vision of me banging my head over duplicate constructor argument names within some implicitly declared binding that a co-worker pushed before going

1.3. Background 5 Pythonic Dependency Injection (py-di) Documentation, Release 0.1.1

to the very same bar I intended to visit before I ran into the bug in the first place. Such things don’t happen at Google, I guess. But to be fair, Pinject’s error handling and raising is exceptionally explicit and therefore brilliant. So this is never a con, it just doesn’t feel right to me. And last but not least, Pinject is not (yet) compatible with Python 3 and therefore misses out on some new features, that come in very handy when dealing with DI.

Conclusion

To me Pinject is an inspiring piece of software, because it introduces a set of best practices and utilities that for the first time feels really pythonic, is quite accessible and as far as I can tell bullet-proof by any means. By far the best DI library in the Python world. This library is heavily inspired by Pinject itself. Still, it does enforce some conventions that I consider out of (pragmatism vs. concept) balance and it does promote non- explicit binding specifications by providing implicit bindings, which suggests out-of-the-box magic. In my opinion, these points add significantly to the complexity. And I would call low complexity the main priority of Python-DI.

1.3.2 Core Concepts

Here’s a short description of what Python-DI tries to be:

Important: A Dependency Injection container for Python ... • ... with a limited set of features. • ... exposing an overall explicit API. • ... implemented with focus on simplicity and source code readability. • ... that, although enforcing and promoting a few very basic rules, can be non-invasively used with any existing code base.

A limited feature set

In order to be as simple as possible, Python-DI offers the following limited feature set: • Binding string values, so called binding keys, to either: – a static value, e.g. version 1.1. – a function within singleton or prototype scope. – a class constructor within singleton or prototype scope. • Annotating binding keys in order to provide the ability to declare dependency on different objects for the same binding key. • Injecting dependencies into any function (unbound or bound) or class constructor unless they are built-in.

Exposing an overall explicit API

The client API mainly consists of only three methods: bind() Register an item with a binding key. inject_into() Inject all declared dependencies into a function (unbound or bound) or class constructor.

6 Chapter 1. Contents Pythonic Dependency Injection (py-di) Documentation, Release 0.1.1 get_instance() Return an object according to the rules of the binding found for a passed binding key. The API raises exceptions that are explicit and verbose, both in class name and message content. Read more about this in the !!!Injector!!! section.

Focus on simplicity and source code readability

Python provides us with some extremely powerful tools like Metaprogramming or the Python Decorators that literally cry for being used to implement DI and decoupling, but in order to keep it simple, I avoided using anything except standard OOP techniques. So you won’t have to fully understand any of those (or other) techniques to fully understand Python-DI. The only not so common concept that Python-DI uses, are Function Annotations. This feature allows to annotate function values with any expression: def foo(an_integer:int, a_string:’Bar’) pass

Python itself does nothing with Function Annotations, but it provides tools like the inspect library for retrieving and analyzing them. In Python-DI, Function Annotations are used to declare dependency on annotations and optionally even binding keys.

Note: As you may have noticed, this last paragraph exposed another possible simplicity leak. For more on that, visit the !!!Terminology!!! section.

As far as source code readability is concerned, it may be a matter of taste, but I consider well organised and commented OO code the most painless to read in almost any case.

A few very basic rules

The only domain where Python-DI enforces convention is the Function Signature of functions or constructors that you want to provide with their dependencies. • There will be an injection attempt for any explicit positional or keyword argument in the inspected sig- nature. Given the following code example:

def foo(positional, keyword_or_positional=None, *args, keyword_only, **kwargs): # do something

An injection attempt is made for the arguments positional, keyword_or_positional and keyword_only. If a binding can’t be found, the injection fails. • Declaring dependency on annotated bindings enforces the use of string-typed Function Annotations. Given a factory bound to ‘widget’ annotated with ‘text’. You’d declare dependency on that factory like so: def need_text_widget(widget:’text’): # do something with widget

• Declaring dependency on annotations AND bindings enforces the use of string-typed Function Annotations using the pattern ANNOTATION_FORMAT Given:

1.3. Background 7 Pythonic Dependency Injection (py-di) Documentation, Release 0.1.1

– a factory bound to ‘widget’ annotated with ‘text’. – a factory bound to ‘widget’ annotated with ‘int’. – an injector. You’d declare dependency on those factories like this: def need_text_widget(text_widget:’widget:text’, int_widget:’widget:int’): # do something with widget

In this case the argument names are ignored and only the Function Annotations get analyzed. Read more about this in the !!!Declaring dependencies!!! section. Last but not least, Python-DI “enforces” the use of Python 3.3 or higher.

1.3.3 Terminology

When reading about Dependency Injection, you’ll always come across the same few terms, but the names of common components within different frameworks do not always relate to their counterparts from theory. At some point you may interact with an Injector object that injects, whereelse you deal with an ObjectGraph that provides. This can get a little confusing, so here’s a list of terms you’ll find in this documentation and what they mean within the context of Python-DI. To inject vs. To provide Python-DI provides an injectee with everything it declared as dependency by injecting the dependencies into the injectee. It injects the dependencies only after it provided them with possible dependen- cies too. To bind We actually bind an identifier to a provider. By doing so we tell our injector to use this very provider whenever an injection request for this identifier occurs. To annotate or Annotation This can mean two different things: • Annotating a binding lets you have different objects injected for the same identifier. On the other side, you can annotate to a dependency declaration to request the object bound to the annotated identifier. • Python-DI uses Function Annotations to declare and identify dependencies. Within this documentation the binding annotations are referred to as annotations and the Python feature is referred to as Function Annotations. Singleton The term Singleton in Python-DI does NOT refer to the Singleton pattern, but describes an object that gets instantiated when first requested and reused for all further requests. Although it might not seem obvious at first sight, there is a huge difference between the two. Here you’ll find some further discussion on that topic. Prototype In Python-DI this term describes an item (constructor, function etc.), that acts as blueprint for creating objects of a certain type. Whenever an injection of a Prototype is requested, the blueprint is used to create a new object which in turn will get used to respond to the request. Scope The term Scope describes how and when a requested object gets created and returned. If bound as singleton, an object in question will be created upon first request and reused for all further requests. If bound as prototype, every request will be responded to with a new creation result. A bound item is either in singleton scope or in prototype scope. Injection Describes the process of inspecting a given item’s (class, function) dependencies and providing it with what it needs, following the rules defined during binding. Injection request Describes the process of providing an object with one declared dependency. The injection process consists of injection requests for each dependency.

8 Chapter 1. Contents Pythonic Dependency Injection (py-di) Documentation, Release 0.1.1

Provider The entity (or set of objects) that controls the creation of an object, the scope it should live in and how it should get returned. Injector An entity that “contains” the information for injecting dependencies ( Definition on Wikipedia). Within Python-DI, represented by the Injector class. Additionally, in Python-DI the Injector exposes the main API. Binding An object that connects an identifier with a provider and delegating given injection requests to the latter. Factory In this documentation we refer to factories as bound or unbound functions that are not class constructors and return a newly created object. This is a very good explanation of the Factory pattern in Python. DI container A synonym to !!!Injector!!!

1.4 Package documentation

1.4.1 di.injectors class di.injectors.Injector(settings=None, **lazy_settings) Bases: builtins.object The Injector is responsible for binding and injecting dependencies. Parameters • settings (_Settings) – An optional explicit settings object. • lazy_settings (dict) – Will get passed to a Settings object if no explicit settings got passed. bind(name) Starts a binding chain. Parameters name (str) – The name of the binding Return type Binding get_instance(key, *args, **kwargs) Returns an item provided by the provider that is bound to key. Parameters key (str) – The binding key. Raises BindingNotFound Raises AnnotationNotFound Return type • inject_into(item, *args, **kwargs) Injects dependencies into a given item function or class constructor. The following parameters will get processed: •Arguments that can be passed as either positional or keyword (inspect.Parameter.POSITIONAL_OR_KEYWORD) •Positional only arguments (inspect.Parameter.POSITIONAL_ONLY) Additionally, overloaded positional and keyword arguments will get merged into the injection parameters (inspect.Parameter.VAR_POSITIONAL and inspect.Parameter.VAR_POSITIONAL re- spectively)

1.4. Package documentation 9 Pythonic Dependency Injection (py-di) Documentation, Release 0.1.1

Note: Overloaded positionals (*args) and keywords (**kwargs) won’t get passed to nested depen- dencies.

Parameters item (type, types.FunctionType) – A function or class Raises UnknownParameterKind Raises InjectionFailed Return type •

1.4.2 di.providers class di.providers.ClassScopeProxy(injector, item=None) Bases: di.providers.ScopeProxy A proxy exposing an interface for scope selection when binding classes (types).

Note: At the moment, ClassScopeProxy is just a plain subclass of ScopeProxy with no specific imple- mentations whatsoever. It exists mostly for semantic reasons and with eventual future changes in mind. class di.providers.FactoryProvider(injector, item=None) Bases: di.providers.Provider A factory provider. Returns a function that is decorated with injection. class di.providers.FunctionScopeProxy(injector, item=None) Bases: di.providers.ScopeProxy A proxy exposing an interface for scope selection when binding function.

Note: At the moment, FunctionScopeProxy is just a plain subclass of ScopeProxy with no specific implementations whatsoever. It exists mostly for semantic reasons and with eventual future changes in mind. class di.providers.MainProviderProxy(injector, item=None) Bases: di.providers.ProviderProxy The main entry point for a binding chain. Exposes factory methods to determine the item type provider. to_class(item) Sets a ClassScopeProxy object as inner provider that later on should determine the scope the provider should work in. Parameters item (type) – The item you want to provide for the current binding chain. Return type ClassScopeProxy to_function(item) Sets a FunctionScopeProxy object as inner provider that later on should determine the scope the provider should work in. Parameters item (types.FunctionType) – The item you want to provide for the current binding chain. Return type FunctionScopeProxy

10 Chapter 1. Contents Pythonic Dependency Injection (py-di) Documentation, Release 0.1.1

to_value(item) Sets a ValueProvider object as inner provider. Parameters item – The item you want to provide for the current binding chain. Return type ValueProvider class di.providers.PrototypeProvider(injector, item=None) Bases: di.providers.Provider A prototype provider. Returns a newly provided item for each request. provide(*args, **kwargs) Returns a newly created object. Return type • class di.providers.Provider(injector, item=None) Bases: builtins.object Basic interface for providers. Providers (or chain of providers) defines the way a binding gets injected upon request. Parameters • injector (Injector) – The injector hosting the DI. • item – The item you want to provide. provide(*args, **kwargs) Provides an object according to configuration and implementation. Raises NotImplemented Return type • class di.providers.ProviderProxy(injector, item=None) Bases: di.providers.Provider A proxy class that delegates provide request to an inner provider object. The ProviderProxy object should expose an interface to set its inner providers. provide(*args, **kwargs) Delegates the call to its inner provider. Raises ProviderProxyError Return type • class di.providers.ScopeProxy(injector, item=None) Bases: di.providers.ProviderProxy A proxy exposing an interface for scope selection. as_factory() Sets a FactoryProvider object as inner provider. Parameters item – The item you want to provide for the current binding chain. Return type FactoryProvider

1.4. Package documentation 11 Pythonic Dependency Injection (py-di) Documentation, Release 0.1.1

as_prototype() Sets a PrototypeProvider as inner provider. as_singleton() Sets a SingletonProvider as inner provider. class di.providers.SingletonProvider(injector, item=None) Bases: di.providers.Provider A singleton provider. The item gets provided and when first requested and then cached for further requests. provide(*args, **kwargs) Please note that no args or kwargs will get passed to the injection process, as it would be a huge liability to create a singleton with eventual runtime variables. Return type • class di.providers.ValueProvider(injector, item=None) Bases: di.providers.Provider A value provider. Returns a static value.

1.4.3 di.binding_specs

class di.binding_specs.BindingSpec Bases: builtins.object Basic interface for binding specifications. configure(bind) Should implement binding instructions, e.g.: bind(’widget’).annotated_with(’text’).to_class(TextWidget).as_prototype()

Parameters bind (types.FunctionType) – A reference to the bind() method of the re- lated context.

1.4.4 di.bindings

di.bindings.ANNOTATION_FORMAT = ‘{name}:{annotation}’ Standard binding key format for name/annotation pairs class di.bindings.Binding(binding_map, name, provider) Bases: builtins.object Handles binding annotations by providing an extended binding key to the passed injector in case of an annotation request. If not, delegating attributes to provider. In both cases the actual key -> value assignment happens within the Binding. Parameters • binding_map (StrictDict) – A binding map. • name (str) – The name of the binding that eventually will get annotated. • provider (Provider) – A provider object that you want to bind to a name/annotation pair. Raises BindingAlreadyExists

12 Chapter 1. Contents Pythonic Dependency Injection (py-di) Documentation, Release 0.1.1

annotated_with(annotation) Extends the binding key with annotation and delegates further provider instructions to (returns) the wrapped provider. Adds Parameters annotation (str) – An annotation name. Return type Provider di.bindings.from_key(key) Returns a tuple (name, annotation) for a key formatted as defined in ANNOTATION_FORMAT, e.g. (’widget’, ’text’) for key ’widget:text’. Parameters key (str) – A key Return type tuple di.bindings.from_param(param, use_argument_names=True) Returns a tuple (name, annotation) for a inspect.Parameter object. Parameters • param (inspect.Parameter) – A parameter object • use_argument_names (bool) – Whether to use the parameter object name or rely on anno- tation only. Raises InvalidAnnotation Raises MissingDependencyDeclaration Return type tuple di.bindings.get_key(name, annotation=None) Returns the binding key for a name/annotation pair, formatted with ANNOTATION_FORMAT, e.g. ’widget:text’. Parameters • name (str) – A binding name • annotation (str) – A binding annotation Return type str

1.4.5 di.settings class di.settings.Settings(**kwargs) Bases: builtins.object A simple settings container. Should hold all values needed for configuring an Injector. As for now, there are only two: Parameters • binding_specs (list of BindingSpec) – A list of binding specs that should be processed. • use_argument_names (bool) – Whether dependency declaration works with annotations only or with argument name / annotation pairs. binding_specs Returns A list of BindingSpec objects that should be processed. use_argument_names

1.4. Package documentation 13 Pythonic Dependency Injection (py-di) Documentation, Release 0.1.1

Returns bool, whether dependency declaration works with annotations only or with argument name / annotation pairs.

1.4.6 di.exceptions exception di.exceptions.AnnotationAlreadyExists Bases: di.exceptions.DIError Gets raised when a binding with a given annotation already exists. exception di.exceptions.AnnotationNotFound Bases: di.exceptions.DIError Gets raised when a binding does exist, but not the annotation in question. exception di.exceptions.BindingAlreadyExists Bases: di.exceptions.DIError Gets raised when a a binding tries to map to a key that already exists. exception di.exceptions.BindingNotFound Bases: di.exceptions.DIError Gets raised when a dependency declaration has no related binding. exception di.exceptions.DIError Bases: builtins.Exception Base error class exception di.exceptions.InjectionFailed Bases: di.exceptions.DIError Error during injection. Catches and formats any other error that occurs during injection. exception di.exceptions.InvalidAnnotation Bases: di.exceptions.DIError Use strings to annotate arguments used with PythonDI. You’ll probably see a lot of these errors if you don’t. exception di.exceptions.InvalidBindingType Bases: di.exceptions.DIError Gets raised when the passed item to bind mismatches the expected data type. exception di.exceptions.MissingDependencyDeclaration Bases: di.exceptions.DIError Gets raised when any positional or keyword only argument of an injectee lacks an annotated dependency decla- ration. This error only occurs if use_arg_names is False. exception di.exceptions.NoInnerProviderFound Bases: di.exceptions.DIError Raised when a ProviderProxy without an inner provider has to provide(). exception di.exceptions.UnknownParameterKind Bases: di.exceptions.DIError This one is only for security reasons. It eventually gets raised when an unknown inspect.Parameter.kind is detected during injection. However, the current implementation of inject_into() handles all possible cases known until Python 3.3.

14 Chapter 1. Contents Pythonic Dependency Injection (py-di) Documentation, Release 0.1.1

1.4.7 di.types class di.types.StrictDict(seq=None, **kwargs) A simple dict subclass. It prevents overriding existing keys and lazy deletion. __setitem__(key, value) Sets a key/value pair. Raises a KeyError if the key already exist. Parameters • key – The key • value – The value Raises KeyError __delitem__(key) Removes a key/value pair based upon the given key. Raises a KeyError if the key doesn’t exist. Parameters key – The key Raises KeyError

1.4. Package documentation 15 Pythonic Dependency Injection (py-di) Documentation, Release 0.1.1

16 Chapter 1. Contents CHAPTER TWO

INDICES AND TABLES

• genindex • modindex • search

17 Pythonic Dependency Injection (py-di) Documentation, Release 0.1.1

18 Chapter 2. Indices and tables PYTHON MODULE INDEX

d di.binding_specs, 12 di.bindings, 12 di.exceptions, 14 di.injectors,9 di.providers, 10 di.settings, 13

19