About
Dyject is a dependency injection module for Python. Unlike other enterprise libraries use for Inversion of Control (IoC) that have classes which look like AbstractSingletonProxyFactoryBean
, dyject is simple to understand and easy to use. It supports both Python 2.7 and Python 3, has no other dependencies and uses standard configuration files to define and wire objects.
Installation
The dyject module can be installed using easy_install
.
easy_install dyject
Alternatively, the latest version of dyject can be downloaded from the Python Package Index and installed using setup.py
tar xvfz dyject-0.7.2.tar.gz cd dyject-0.7.2 sudo python setup.py install
Configuration
The following is a sample configuration file:
[ConfigurationHandler] class = examples.classes.ConfigurationHandler system = 'linux' version = 1.0 supported_versions = [ 0.2 , 0.3, 0.5 , 0.8 , 1.0 ] default_config = class-ref\ClientConfig configs = class-ref\{ServiceConfig,ClientConfig,OtherConfig} [BaseConfig] class = examples.classes.BaseConfig config_type = 'BASE' arch = 'x86_64' [ServiceConfig] inherit = BaseConfig config_type = 'SERVICE' process = '/usr/bin/service -d' [ClientConfig] inherit = BaseConfig config_type = 'CLIENT' process = '/usr/bin/client -d' [OtherConfig] inherit = BaseConfig class = examples.classes.OtherConfig config_type = 'SPECIAL' process = 'cifs://remote_host/share1/other.bin' arch = 'arm'
In the above example, each configuration section corresponds to name for identifying an instance of a class. This name is what will be used within the Python code to retrieve an object. Within each section, there must be either a class
or inherit
attribute (or both) defined. class
must be a full path to a Python class. inherit
must specify the name of another configuration section. If no class
is defined, dyject will look up the class reference in inherit
to determine which class to use. It will continue up the inheritence tree defined in the configuration file until a class
attribute is found.
All other attributes are assigned as class members. The right side of the equal sign is interpreted based on standard Python typing . Therefore, in the ConfigurationHandler
example, system
will be set as a string, version
will be a float, supported_versions
will be set as a list and so fourth.
If a value starts with class-ref\
, the value given must be the name of another class as defined by its section heading. Braces can be used to define lists of objects. All objects set using class-ref
are singletons. Therefore, in the above example, the same ClientConfig
class will be set to both the default_config
property and the second element in the configs
list.
To recap, configuration elements are defined as follows:
[ClassIdentifier] | Name by which a class is identified and referenced to |
class/inherit = | Indicates the name of the module and class to use as the instance for the given object.
If no name is given, inherit must be defined and a section within the inheritence tree must define class . If both class and inherit are defined, the class within the section will be used to create the object and attributes will be assigned bottom up from the inheritence tree. Only the top class attribute will be used to define the object and other class attributes from parents will be ignored.
|
attribute = |
Class properties are assigned by attribute name. The value of the attribute determines the type using standard Python duck typing. class-ref\ is used to indicate that a class references by section name in the configuration file should be set as the given property. Multiple objects can be set using braces to indicate a list of objects.
|
Usage
With dependency injection, all the objects should have their dependencies set within the configuration file. This is sometimes referred to as wiring. Within a Python application, only one object should have to be called by name. This is sometimes known as the bootstrapping object. The following is an example of how to pull an instance of an object from a dyject configuration file:
#!/usr/bin/env python from dyject import Context if __name__ == '__main__': ctx = Context() ctx.load_config('example.config') client = ctx.get_class('ClientConfig') config = ctx.get_class('ConfigurationHandler') print('Client arch is {0}'.format(client.arch)) print('Configuration Handler config objects: {0}' .format(config.configs)) other_a = ctx.get_class('OtherConfig',prototype=True) other_b = ctx.get_class('OtherConfig',prototype=True) if id(other_a) != id(other_b): print('Other objects are distinct instances')
Notice that the prototype
argument can be set when calling get_class
. By default, get_class
will return a singleton object. By setting prototype
to True
, a new instance of the requested object is created that is not stored in dyject’s object cache.
Source Code
Both Source code and example for dyject can be found on Github.
dyject | The core dyject python module | |
dyject_examples | Source code for the examples listed on this page | |
dyject_web | The source code used by Middleman to generate this website |
Testing
Unit tests can be found within the source code. Simply run the script run_tests.py
and the following output should be generated. Although dyject has been tested with Python 2.7 and Python 3.2, running these tests should ensure that dyject runs sanely under older and newer version.
Limitations
Although the name class
is reserved and cannot be used as a class member name in Python, the name inherit
is not. Because dyject uses this term as a keyword in is configuration, it cannot currently set a class member named inherit
.
Currently dyject doesn’t support constructor arguments. Classes that have constructors that take in more attributes than self
without default values currently can not be handled using a dyject configuration file.
Inheritance within the configuration file does not necessarily correlate to inheritence within Python. It is possible to inherit from another configuration item which has a class that is not the current’s parent. Attributes will be assigned bottom up with all class
attributes being ignored except for the top one.
Issues
Please submit bugs to the project’s issue tracker on Github.
License
Dyject is licensed under the Apache 2.0 License.