Pythonic Dependency Injection (Py-Di) Documentation Release 0.1.1
Total Page:16
File Type:pdf, Size:1020Kb
Pythonic Dependency Injection (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 tables 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: • Google Guice (Java) • 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) • Unity (.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.