Chapter 2. The IoC container

Inversion Of Control (IoC), also known as dependency injection is more of an architectural concept than a simple coding pattern.

The idea is to decouple classes that depend on each other from inheriting other dependencies, and instead link them only at the interfacing level. This requires some sort of 3rd party software module to instantiate the concrete objects and "inject" them into the class that needs to call them.

In Spring, there are certain classes whose instances form the backbone of your application and that are managed by the Spring IoC container. While Spring Java calls them beans, Spring Python and Spring for .NET call them objects. An object is simply a class instance that was instantiated, assembled and otherwise managed by a Spring IoC container instead of directly by your code; other than that, there is nothing special about a object. It is in all other respects one of probably many objects in your application. These objects, and the dependencies between them, are reflected in the configuration meta-data used by a container.

The following diagram demonstrates a key Spring concept: building useful services on top of simple objects, configured through a container's set of blueprints, provides powerful services that are easier to maintain.

This chapter provides the basics of Spring Python's IoC container by using examples with explanations. If you are familiar with Spring Java, then you may notice many similarities. Also, this document points out key differences. It shows how to define the objects, read them into a container, and then fetch the objects into your code.

2.1. External dependencies

XML-based IoC configuration formats use ElementTree which is a part of Python's stantard library in Python 2.5 and newer. If you use Python 2.4 you can download ElementTree from here. YamlConfig requires installation of PyYAML which may be found here. No additional dependencies needs be installed if you choose PythonConfig.

2.2. Container

A container is an object you create in your code that receives the definitions for objects from various sources. Your code goes to the container to request the object, and the container then does everything it needs to create an instance of that.

Depending on the scope of the object definition, the container may create a new instance right there on the spot, or it may fetch a reference to a singleton instance that was created previously. If this is the first time a singleton-scoped object is requested, is created, stored, and then returned to you. For a prototype-scoped object, EVERY TIME you request an object, a new instance is created and NOT stored in the singleton cache.

Containers depend on various object factories to do the heavy lifting of construction, and then itself will set any additional properties. There is also the possibility of additional behavior outside of object creation, which can be defined by extending the ObjectContainer class.

The reason it is called a container is the idea that you are going to a central place to get your top level object. While it is also possible to get all your other objects, the core concept of dependency injection is that below your top-most object, all the other dependencies have been injected and thus not require container access. That is what we mean when we say most of your code does NOT have to be Spring Python-aware.

[Note]Present vs. Future Object Containers

Pay special note that there is no fixed requirement that a container actually be in a certain location. While the current solution is memory based, meaning your objects will be lost when your application shuts down, there is always the possibility of implementing some type of distributed, persistent object container. For example, it is within the realm of possibilities to implement a container that utilizes a back-end database to "contain" things or utilizes some distributed memory cache spread between nodes.

2.2.1. ObjectContainer vs. ApplicationContext

The name of the container is ObjectContainer. Its job is to pull in object meta-data from various sources, and then call on related object factories to create the objects. In fact, this container is capable of receiving object definitions from multiple sources, each of differing types such as XML, YAML, python code, and other future formats.

The following block of code shows an example of creating an object container, and then pulling an object out of the container.

from springpython.context import ApplicationContext
from springpython.config import XMLConfig

container = ApplicationContext(XMLConfig("app-context.xml"))
service = container.get_object("sampleService")

The first thing you may notice is the fact that ApplicationContext was used instead of ObjectContainer. ApplicationContext is a subclass of ObjectContainer, and is typically used because it also performs additional pre- and post-creational logic.

For example, any object that implements ApplicationContextAware will have an additional app_context attribute added, populated with a copy of the ApplicationContext. If your object's class extends ObjectPostProcessor and defines a post_process_after_initialization, the ApplicationContext will run that method against every instance of that object.

If your singleton objects hold references to some external resources, e.g. connections to a resource manager of some sort, you may also want to subclass springpython.context.DisposableObject to have a means for the resources to get released. Any singleton subclassing springpython.context.DisposableObject may define a destroy method which is guaranteed to be executed on ApplicationContext shutdown. An alternative to creating a destroy method is to define the destroy_method attribute of an object which should be a name of the custom method to be invoked on ApplicationContext shutdown. If an object defines both destroy and destroy_method then the former will take precedence. It is an error to extend springpython.context.DisposableObject without providing either destory or destroy_method. If this occurs, an error will be written to Spring Python logs when the container shuts down.

2.2.2. Scope of Objects / Lazy Initialization

Another key duty of the container is to also manage the scope of objects. This means at what time that objects are created, where the instances are stored, how long before they are destroyed, and whether or not to create them when the container is first started up.

Currently, two scopes are supported: SINGLETON and PROTOTYPE. A singleton-scoped object is cached in the container until application shutdown. A prototype-scoped object is never stored, thus requiring the object factory to create a new instance every time the object is requested from the container.

The default policy for the container is to make everything SINGLETON and also eagerly fetch all objects when the container is first created. The scope for each object can be individually overriden. Also, the initialization of each object can be shifted to "lazy", whereby the object is not created until the first time it is fetched or referenced by another object.

2.3. Configuration

Spring Python support different formats for defining objects. This project first began using the format defined by PyContainer, a now inactive project. The structure has been captured into an XSD spec. This format is primarily to support legacy apps that have already been built with Spring Python from its inception. There is no current priority to extend this format any further. Any new schema developments will be happening with XMLConfig and YamlConfig.

In the spirit of Spring JavaConfig and a blog posting by Rod Johnson, another format has been defined. By extending PythonConfig and using the @Object python decorator, objects may be defined with pure python code in a centralized class.

Due to limitations in the format of PyContainer, another schema has been developed called XMLConfig that more closely models the original Spring Java version. It has support for referenced objects in many more places than PyContainer could handle, inner objects as well, various collections (lists, sets, frozen sets, tuples, dictionaries, and java-style props), and values.

Spring Python also has a YAML-based parser called YamlConfig.

Spring Python is ultimately about choice, which is why developers may extend the Config class to define their own object definition scanner. By plugging an instance of their scanner into ApplicationContext, definitions can result in instantiated objects.

You may be wondering, amidst all these choices, which one to pick? Here are some suggestions based on your current solution space:

  • New projects are encouraged to pick either PythonConfig, XMLConfig, or YamlConfig, based on your preference for pure python code, XML, or YAML.

  • Projects migrating from Spring Java can use SpringJavaConfig to ease transition, with a long term goal of migrating to XMLConfig, and perhaps finally PythonConfig.

  • Apps already developed with Spring Python can use PyContainerConfig to keep running, but it is highly suggested you work towards XMLConfig.

  • Projects currently using XMLConfig should be pretty easy to migrate to PythonConfig, since it is basically a one-to-one translation. The pure python configuration may turn out much more compact, especially if you are using lists, sets, dictionaries, and props.

    It should also be relatively easy to migrate an XMLConfig-based configuration to YamlConfig. YAML tends to be more compact than XML, and some prefer not having to deal with the angle-bracket tax.

2.3.1. XMLConfig - Spring Python's native XML format

XMLConfig is a class that scans object definitions stored in the new XML format defined for Spring Python. It looks very similar to Spring Java's 2.5 XSD spec, with some small changes.

The following is a simple definition of objects. Later sections will show other options you have for wiring things together.

<?xml version="1.0" encoding="UTF-8"?>
<objects xmlns="http://www.springframework.org/springpython/schema/objects/1.1"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/springpython/schema/objects/1.1
       		http://springpython.webfactional.com/schema/context/spring-python-context-1.1.xsd">
       		
	<object id="MovieLister" class="springpythontest.support.testSupportClasses.MovieLister" scope="prototype">
		<property name="finder" ref="MovieFinder"/>
		<property name="description"><ref object="SingletonString"/></property> 
	</object>
	
	<object id="MovieFinder" class="springpythontest.support.testSupportClasses.ColonMovieFinder" scope="singleton">
		<property name="filename"><value>support/movies1.txt</value></property>
	</object>
	
	<object id="SingletonString" class="springpythontest.support.testSupportClasses.StringHolder" lazy-init="True">
		<property name="str" value="There should only be one copy of this string"></property>
	</object>
</objects>

The definitions stored in this file are fed to an XMLConfig instance which scans it, and then sends the meta-data to the ApplicationContext. Then, when the application code requests an object named MovieLister from the container, the container utilizes an object factory to create the object and return it.

from springpython.context import ApplicationContext
from springpython.config import XMLConfig

container = ApplicationContext(XMLConfig("app-context.xml"))
service = container.get_object("MovieLister")

2.3.1.1. Referenced Objects

A referenced object is where an object is needed, but instead of providing the definition right there, there is, instead, a name, referring to another object definition.

Object definitions can refer to other objects in many places including: properties, constructor arguments, and objects embedded inside various collections. This is the way to break things down into smaller pieces. It also allows you more efficiently use memory and guarantee different objects are linked to the same backend object.

The following fragment, pulled from the earlier example, shows two different properties referencing other objects. It demonstrates the two ways to refer to another object.

<object id="MovieLister" class="springpythontest.support.testSupportClasses.MovieLister" scope="prototype">
	<property name="finder" ref="MovieFinder"/>
	<property name="description"><ref object="SingletonString"/></property> 
</object>

This means that instead of defining the object meant to be injected into the description property right there, the container must look elsewhere amongst its collection of object definitions for an object named SingletonString.

[Note]Referenced objects don't have to be in same configuration

When a referenced object is encountered, finding its definition is referred back to the container. This means ANY of the input sources provided to the container can hold this definition, REGARDLESS of format.

[Note]Spring Python ONLY supports global references

While Spring Java has different levels of reference like parent, local, and global, Spring Python only supports global at this time.

In the following subsections, other types of object definitions are given. Each will also include information about embedding reference objects.

2.3.1.2. Inner Objects

Inner objects are objects defined inside another structure, and not at the root level of the XML document. The following shows an alternative configuration of a MovieLister where the finder uses a named inner object.

<object id="MovieLister3" class="springpythontest.support.testSupportClasses.MovieLister">
	<property name="finder">
		<object id="named" class="springpythontest.support.testSupportClasses.ColonMovieFinder">
			<property name="filename"><value>support/movies1.txt</value></property>
		</object>
	</property>
	<property name="description"><ref object="SingletonString"/></property>
</object>

The ColonMovieFinder is indeed an inner object because it was defined inside the MovieLister3 object. Objects defined at the top level have a container-level name that matches their id value. In this case, asking the container for a copy of MovieLister3 will yield the top level object. However, named objects develop a path-like name based on where they are located. In this case, the inner ColonMovieFinder object will have a container-level name of MovieLister3.finder.named.

Typically, neither your code nor other object definitions will have any need to reference MovieLister3.finder.named, but there may be cases where you need this. The id attribute of ColonMovieFinder can be left out (it is optional for inner objects) like this:

<object id="MovieLister2" class="springpythontest.support.testSupportClasses.MovieLister">
	<property name="finder">
		<object class="springpythontest.support.testSupportClasses.ColonMovieFinder">
			<property name="filename"><value>support/movies1.txt</value></property>
		</object>
	</property>
	<property name="description"><ref object="SingletonString"/></property>
</object>

That is slightly more compact, and usually alright because you usually wouldn't access this object from anywhere. However, if you must, the name in this case is MovieLister2.finder.<anonymous> indicating an anonymous object.

It is important to realize that inner objects have all the same privileges as top-level objects, meaning that they can also utilize reference objects, collections, and inner objects themselves.

2.3.1.3. Collections

Spring Java supports many types of collections, including lists, sets, frozen sets, maps, tuples, and java-style properties. Spring Python supports these as well. The following configuration shows usage of dict, list, props, set, frozenset, and tuple.

<object id="ValueHolder" class="springpythontest.support.testSupportClasses.ValueHolder">
	<constructor-arg><ref object="SingletonString"/></constructor-arg>
	<property name="some_dict">
		<dict>
			<entry><key><value>Hello</value></key><value>World</value></entry>
			<entry><key><value>Spring</value></key><value>Python</value></entry>
			<entry><key><value>holder</value></key><ref object="SingletonString"/></entry>
			<entry><key><value>another copy</value></key><ref object="SingletonString"/></entry>
		</dict>
	</property>
	<property name="some_list">
		<list>
			<value>Hello, world!</value>
			<ref object="SingletonString"/>
			<value>Spring Python</value>
		</list>
	</property>
	<property name="some_props">
		<props>
			<prop key="administrator">[email protected]</prop>
			<prop key="support">[email protected]</prop>
			<prop key="development">[email protected]</prop>
		</props>
	</property>
	<property name="some_set">
		<set>
			<value>Hello, world!</value>
			<ref object="SingletonString"/>
			<value>Spring Python</value>
		</set>
	</property>
	<property name="some_frozen_set">
		<frozenset>
			<value>Hello, world!</value>
			<ref object="SingletonString"/>
			<value>Spring Python</value>
		</frozenset>
	</property>
	<property name="some_tuple">
		<tuple>
			<value>Hello, world!</value>
			<ref object="SingletonString"/>
			<value>Spring Python</value>
		</tuple>
	</property>        
</object>
  • some_dict is a python dictionary with four entries.

  • some_list is a python list with three entries.

  • some_props is also a python dictionary, containing three values.

  • some_set is an instance of python's mutable set.

  • some_frozen_set is an instance of python's frozen set.

  • some_tuple is a python tuple with three values.

[Note]Java uses maps, Python uses dictionaries

While java calls key-based structures maps, python calls them dictionaries. For this reason, the code fragment shows a "dict" entry, which is one-to-one with Spring Java's "map" definition.

Java also has a Property class. Spring Python translates this into a python dictionary, making it more like an alternative to the configuring mechanism of dict.

2.3.1.4. Constructors

Python functions can have both positional and named arguments. Positional arguments get assembled into a tuple, and named arguments are assembled into a dictionary, before being passed to a function call. Spring Python takes advantage of that option when it comes to constructor calls. The following block of configuration data shows defining positional constructors.

<object id="AnotherSingletonString" class="springpythontest.support.testSupportClasses.StringHolder">
	<constructor-arg value="attributed value"/>
</object>

<object id="AThirdSingletonString" class="springpythontest.support.testSupportClasses.StringHolder">
	<constructor-arg><value>elemental value</value></constructor-arg>
</object>

Spring Python will read these and then feed them to the class constructor in the same order as shown here.

The following code configuration shows named constructor arguments. Spring Python converts these into keyword arguments, meaning it doesn't matter what order they are defined.

<object id="MultiValueHolder" class="springpythontest.support.testSupportClasses.MultiValueHolder">
	<constructor-arg name="a"><value>alt a</value></constructor-arg>
	<constructor-arg name="b"><value>alt b</value></constructor-arg>
</object>

<object id="MultiValueHolder2" class="springpythontest.support.testSupportClasses.MultiValueHolder">
	<constructor-arg name="c"><value>alt c</value></constructor-arg>
	<constructor-arg name="b"><value>alt b</value></constructor-arg>
</object>

This was copied from the code's test suite, where a test case is used to prove that order doesn't matter. It is important to note that positional constructor arguments are fed before named constructors, and that overriding a the same constructor parameter both by position and by name is not allowed by Python, and will in turn, generate a run-time error.

It is also valuable to know that you can mix this up and use both.

2.3.1.5. Values

For those of you that used Spring Python before XMLConfig, you may have noticed that expressing values isn't as succinct as the old format. A good example of the old PyContainer format would be:

<component id="user_details_service" class="springpython.security.userdetails.InMemoryUserDetailsService">
	<property name="user_dict">
		{
			"basichiblueuser"  : ("password1", ["ROLE_BASIC", "ASSIGNED_BLUE",   "LEVEL_HI"], True),
			"basichiorangeuser": ("password2", ["ROLE_BASIC", "ASSIGNED_ORANGE", "LEVEL_HI"], True),
			"otherhiblueuser"  : ("password3", ["ROLE_OTHER", "ASSIGNED_BLUE",   "LEVEL_HI"], True),
			"otherhiorangeuser": ("password4", ["ROLE_OTHER", "ASSIGNED_ORANGE", "LEVEL_HI"], True),
			"basicloblueuser"  : ("password5", ["ROLE_BASIC", "ASSIGNED_BLUE",   "LEVEL_LO"], True),
			"basicloorangeuser": ("password6", ["ROLE_BASIC", "ASSIGNED_ORANGE", "LEVEL_LO"], True),
			"otherloblueuser"  : ("password7", ["ROLE_OTHER", "ASSIGNED_BLUE",   "LEVEL_LO"], True),
			"otherloorangeuser": ("password8", ["ROLE_OTHER", "ASSIGNED_ORANGE", "LEVEL_LO"], True)
		}
	</property>
</component>
[Note]Why do I see components and not objects?

In the beginning, PyContainer was used and it tagged the managed instances as components. After replacing PyContainer with a more sophisticated IoC container, the instances are now referred to as objects, however, to maintain this legacy format, you will see component tags inside PyContainerConfig-based definitions.

While this is very succinct for expressing definitions using as much python as possible, that format makes it very hard to embed referenced objects and inner objects, since PyContainerConfig uses python's eval method to convert the material.

The following configuration block shows how to configure the same thing for XMLConfig.

<object id="user_details_service" class="springpython.security.userdetails.InMemoryUserDetailsService">
	<property name="user_dict">
		<dict>
			<entry>
				<key><value>basichiblueuser</value></key>
				<value><tuple>
					<value>password1</value>
					<list><value>ROLE_BASIC</value><value>ASSIGNED_BLUE</value><value>LEVEL_HI</value></list>
					<value>True</value>
				</tuple></value>
			</entry>
			<entry>
				<key><value>basichiorangeuser</value></key>
				<value><tuple>
					<value>password2</value>
					<list><value>ROLE_BASIC</value><value>ASSIGNED_ORANGE</value><value>LEVEL_HI</value></list>
					<value>True</value>
				</tuple></value>
			</entry>
			<entry>
				<key><value>otherhiblueuser</value></key>
				<value><tuple>
					<value>password3</value>
					<list><value>ROLE_OTHER</value><value>ASSIGNED_BLUE</value><value>LEVEL_HI</value></list>
					<value>True</value>
				</tuple></value>
			</entry>
			<entry>
				<key><value>otherhiorangeuser</value></key>
				<value><tuple>
					<value>password4</value>
					<list><value>ROLE_OTHER</value><value>ASSIGNED_ORANGE</value><value>LEVEL_HI</value></list>
					<value>True</value>
				</tuple></value>
			</entry>
			<entry>
				<key><value>basicloblueuser</value></key>
				<value><tuple>
					<value>password5</value>
					<list><value>ROLE_BASIC</value><value>ASSIGNED_BLUE</value><value>LEVEL_LO</value></list>
					<value>True</value>
				</tuple></value>
			</entry>
			<entry>
				<key><value>basicloorangeuser</value></key>
				<value><tuple>
					<value>password6</value>
					<list><value>ROLE_BASIC</value><value>ASSIGNED_ORANGE</value><value>LEVEL_LO</value></list>
					<value>True</value>
				</tuple></value>
			</entry>
			<entry>
				<key><value>otherloblueuser</value></key>
				<value><tuple>
					<value>password7</value>
					<list><value>ROLE_OTHER</value><value>ASSIGNED_BLUE</value><value>LEVEL_LO</value></list>
					<value>True</value>
				</tuple></value>
			</entry>
			<entry>
				<key><value>otherloorangeuser</value></key>
				<value><tuple>
					<value>password8</value>
					<list><value>ROLE_OTHER</value><value>ASSIGNED_ORANGE</value><value>LEVEL_LO</value></list>
					<value>True</value>
				</tuple></value>
			</entry>
		</dict>
	</property>
</object>

Of course this is more verbose than the previous block. However, it opens the door to having a much higher level of detail:

<object id="user_details_service2" class="springpython.security.userdetails.InMemoryUserDetailsService">
	<property name="user_dict">
		<list>
			<value>Hello, world!</value>
			<dict>
				<entry>
					<key><value>yes</value></key>
					<value>This is working</value>
				</entry>
				<entry>
					<key><value>no</value></key>
					<value>Maybe it's not?</value>
				</entry>
			</dict>
			<tuple>
				<value>Hello, from Spring Python!</value>
				<value>Spring Python</value>
				<dict>
					<entry>
						<key><value>yes</value></key>
						<value>This is working</value>
					</entry>
					<entry>
						<key><value>no</value></key>
						<value>Maybe it's not?</value>
					</entry>
				</dict>
				<list>
					<value>This is a list element inside a tuple.</value>
					<value>And so is this :)</value>
				</list>
			</tuple>
			<set>
				<value>1</value>
				<value>2</value>
				<value>1</value>
			</set>
			<frozenset>
				<value>a</value>
				<value>b</value>
				<value>a</value>
			</frozenset>
		</list>
	</property>
</object>

2.3.1.6. XMLConfig and basic Python types

Objects of most commonnly used Python types - str, unicode, int, long, float, decimal.Decimal, bool and complex - may be expressed in XMLConfig using a shorthand syntax which allows for a following XMLConfig file:

            
<?xml version="1.0" encoding="UTF-8"?>
<objects xmlns="http://www.springframework.org/springpython/schema/objects/1.1"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/springpython/schema/objects/1.1
       		http://springpython.webfactional.com/schema/context/spring-python-context-1.1.xsd">
       		
    <str id="MyString">My string</str>
    
    <unicode id="MyUnicode">Zażółć gęślą jaźń</unicode>
    
    <int id="MyInt">10</int>
    
    <long id="MyLong">100000000000000000000000</long>
    
    <float id="MyFloat">3.14</float>
    
    <decimal id="MyDecimal">12.34</decimal>
    
    <bool id="MyBool">False</bool>
    
    <complex id="MyComplex">10+0j</complex>
    
</objects>

2.3.1.7. Object definition inheritance

XMLConfig's definitions may be stacked up into hierarchies of abstract parents and their children objects. A child object not only inherits all the properties and constructor arguments from its parent but it can also easily override any of the inherited values. This can save a lot of typing when configuring non-trivial application contexts which would otherwise need to repeat the same configuration properties over many objects definitions.

An abstract object is identified by having an abstract="True" attribute and the child ones are those which have a parent attribute set to ID of an object from which the properties or constructor arguments should be inherited. Child objects must not specify the class attribute, its value is taken from their parents.

An object may be both a child and an abstract one.

Here's a hypothetical configuration of a set of services exposed by a server. Note how you can easily change the CRM environment you're invoking by merely changing the concrete service's (get_customer_id or get_customer_profile) parent ID.

<?xml version="1.0" encoding="UTF-8"?>
<objects xmlns="http://www.springframework.org/springpython/schema/objects/1.1"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/springpython/schema/objects/1.1
       		http://springpython.webfactional.com/schema/context/spring-python-context-1.1.xsd">

    <object id="service" class="springpythontest.support.testSupportClasses.Service" scope="singleton" abstract="True" lazy-init="True">
        <property name="ip"><value>192.168.1.153</value></property>
    </object>
    
    <object id="crm_service_dev" parent="service" abstract="True">
        <property name="port"><value>3392</value></property>
    </object>
    
    <object id="crm_service_test" parent="service" abstract="True">
        <property name="port"><value>3393</value></property>
    </object>
    
    <object id="get_customer_id" parent="crm_service_dev">
        <property name="path"><value>/soap/invoke/get_customer_id</value></property>
    </object>
    
    <object id="get_customer_profile" parent="crm_service_test">
        <property name="path"><value>/soap/invoke/get_customer_profile</value></property>
    </object>
	
</objects>

Here's how you can override inherited properties; both get_customer_id and get_customer_profile object definitions will inherit the path property however the actual objects returned by the container will use local, overridden, values of the property.

<?xml version="1.0" encoding="UTF-8"?>
<objects xmlns="http://www.springframework.org/springpython/schema/objects/1.1"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/springpython/schema/objects/1.1
       		http://springpython.webfactional.com/schema/context/spring-python-context-1.1.xsd">

    <object id="service" class="springpythontest.support.testSupportClasses.Service" scope="singleton" abstract="True" lazy-init="True">
        <property name="ip"><value>192.168.1.153</value></property>
        <property name="port"><value>3392</value></property>
        <property name="path"><value>/DOES-NOT-EXIST</value></property>
    </object>
    
    <object id="get_customer_id" parent="service">
        <property name="path"><value>/soap/invoke/get_customer_id</value></property>
    </object>
    
    <object id="get_customer_profile" parent="service">
        <property name="path"><value>/soap/invoke/get_customer_profile</value></property>
    </object>
	
</objects>

If you need to get an abstract object from a container, use the .get_object's ignore_abstract parameter, otherwise springpython.container.AbstractObjectException will be raised. Observe the difference:

# .. skip creating the context

# No exception will be raised, even though 'service' is an abstract object
service = ctx.get_object("service", ignore_abstract=True)

# Will show the object
print service

# Will raise AbstractObjectException
service = ctx.get_object("service")

2.3.2. YamlConfig - Spring Python's YAML format

YamlConfig is a class that scans object definitions stored in a YAML 1.1 format using the PyYAML project.

The following is a simple definition of objects, including scope and lazy-init. Later sections will show other options you have for wiring things together.

objects:
    - object: MovieLister
      class: springpythontest.support.testSupportClasses.MovieLister
      scope: prototype
      properties:
          finder: {ref: MovieFinder}
          description: {ref: SingletonString}

    - object: MovieFinder
      class: springpythontest.support.testSupportClasses.ColonMovieFinder
      scope: singleton
      lazy-init: True
      properties:
          filename: support/movies1.txt

    - object: SingletonString
      class: springpythontest.support.testSupportClasses.StringHolder
      lazy-init: True
      properties:
          str: There should only be one copy of this string

The definitions stored in this file are fed to an YamlConfig instance which scans it, and then sends the meta-data to the ApplicationContext. Then, when the application code requests an object named MovieLister from the container, the container utilizes an object factory to create the object and return it.

from springpython.context import ApplicationContext
from springpython.config import YamlConfig

container = ApplicationContext(YamlConfig("app-context.xml"))
service = container.get_object("MovieLister")

2.3.2.1. Referenced Objects

A referenced object is where an object is needed, but instead of providing the definition right there, there is, instead, a name, referring to another object definition.

Object definitions can refer to other objects in many places including: properties, constructor arguments, and objects embedded inside various collections. This is the way to break things down into smaller pieces. It also allows you more efficiently use memory and guarantee different objects are linked to the same backend object.

The following fragment, pulled from the earlier example, shows two different properties referencing other objects. It demonstrates the two ways to refer to another object.

object: MovieLister
class: springpythontest.support.testSupportClasses.MovieLister
scope: prototype
properties:
    finder: {ref: MovieFinder}
    description: {ref: SingletonString}

This means that instead of defining the object meant to be injected into the description property right there, the container must look elsewhere amongst its collection of object definitions for an object named SingletonString.

[Note]Referenced objects don't have to be in same configuration

When a referenced object is encountered, finding its definition is referred back to the container. This means ANY of the input sources provided to the container can hold this definition, REGARDLESS of format.

[Note]Spring Python ONLY supports global references

While Spring Java has different levels of reference like parent, local, and global, Spring Python only supports global at this time.

In the following subsections, other types of object definitions are given. Each will also include information about embedding reference objects.

2.3.2.2. Inner Objects

Inner objects are objects defined inside another structure, and not at the root level of the YAML document. The following shows an alternative configuration of a MovieLister where the finder uses a named inner object.

object: MovieLister3
class: springpythontest.support.testSupportClasses.MovieLister
properties:
    finder:
        object: named
        class: springpythontest.support.testSupportClasses.ColonMovieFinder
        properties:
            filename: support/movies1.txt
    description: {ref: SingletonString} 

The ColonMovieFinder is indeed an inner object because it was defined inside the MovieLister3 object. Objects defined at the top level have a container-level name that matches their id value. In this case, asking the container for a copy of MovieLister3 will yield the top level object. However, named objects develop a path-like name based on where they are located. In this case, the inner ColonMovieFinder object will have a container-level name of MovieLister3.finder.named.

Typically, neither your code nor other object definitions will have any need to reference MovieLister3.finder.named, but there may be cases where you need this. The value of the object key of ColonMovieFinder can be left out (it is optional for inner objects) like this:

object: MovieLister2
class: springpythontest.support.testSupportClasses.MovieLister
properties:
    finder:
        object:
        class: springpythontest.support.testSupportClasses.ColonMovieFinder
        properties:
            filename: support/movies1.txt
    description: {ref: SingletonString}

That is slightly more compact, and usually alright because you usually wouldn't access this object from anywhere. However, if you must, the name in this case is MovieLister2.finder.<anonymous> indicating an anonymous object.

It is important to realize that inner objects have all the same privileges as top-level objects, meaning that they can also utilize reference objects, collections, and inner objects themselves.

2.3.2.3. Collections

Spring Java supports many types of collections, including lists, sets, frozen sets, maps, tuples, and java-style properties. Spring Python supports these as well. The following configuration shows usage of dict, list, set, frozenset, and tuple.

      object: ValueHolder
      class: springpythontest.support.testSupportClasses.ValueHolder
      constructor-args:
          - {ref: SingletonString}
      properties:
          some_dict:
                Hello: World
                Spring: Python
                holder: {ref: SingletonString}
                another copy: {ref: SingletonString}
          some_list:
              - Hello, world!
              - ref: SingletonString
              - Spring Python
          some_props:
              administrator: [email protected]
              support: [email protected]
              development: [email protected]
          some_set:
              set:
                  - Hello, world!
                  - ref: SingletonString
                  - Spring Python
          some_frozen_set:
              frozenset:
                  - Hello, world!
                  - ref: SingletonString
                  - Spring Python
          some_tuple:
              tuple:
                  - Hello, world!
                  - ref: SingletonString
                  - Spring Python
  • some_dict is a python dictionary with four entries.

  • some_list is a python list with three entries.

  • some_props is also a python dictionary, containing three values.

  • some_set is an instance of python's mutable set.

  • some_frozen_set is an instance of python's frozen set.

  • some_tuple is a python tuple with three values.

[Note]Java uses maps, Python uses dictionaries

While java calls key-based structures maps, python calls them dictionaries. For this reason, the code fragment shows a "dict" entry, which is one-to-one with Spring Java's "map" definition.

Java also has a Property class. Since YAML already supports a key/value structure as-is, YamlConfig does not have a separate structural definition.

2.3.2.4. Support for Python builtin types and mappings of other types onto YAML syntax

Objects of commonly used Python builtin types may be tersely expressed in YamlConfig. Supported types are str, unicode, int, long, float, decimal.Decimal, bool, complex, dict, list and tuple.

Here's a sample YamlConfig featuring their usage. Note that with the exception of decimal.Decimal, names of the YAML attributes are the same as the names of Python types.

objects: 
    - object:  MyString
      str: My string
        
    - object:  MyUnicode
      unicode: Zażółć gęślą jaźń
        
    - object:  MyInt
      int: 10
    
    - object:  MyLong
      long: 100000000000000000000000
      
    - object:  MyFloat
      float: 3.14
        
    - object:  MyDecimal
      decimal: 12.34
        
    - object:  MyBoolean
      bool: False
        
    - object:  MyComplex
      complex: 10+0j
        
    - object:  MyList
      list: [1, 2, 3, 4]
        
    - object:  MyTuple
      tuple: ["a", "b", "c"]
      
    - object: MyDict
      dict:
        1: "a"
        2: "b"
        3: "c"
        
    - object: MyRef
      decimal:
        ref: MyDecimal

Under the hood, while parsing the YAML files, Spring Python will translate the definitions such as the one above into the following one:

objects: 
    - object:  MyString
      class: types.StringType
      constructor-args: ["My string"]
        
    - object:  MyUnicode
      class: types.UnicodeType
      constructor-args: ["Zażółć gęślą jaźń"]
        
    - object:  MyInt
      class: types.IntType
      constructor-args: [10]
    
    - object:  MyLong
      class: types.LongType
      constructor-args: [100000000000000000000000]
      
    - object:  MyFloat
      class: types.FloatType 
      constructor-args: [3.14]
        
    - object:  MyDecimal
      class: decimal.Decimal
      constructor-args: ["12.34"]
        
    - object: MyBoolean
      class: types.BooleanType
      constructor-args: [False]
        
    - object: MyComplex
      class: types.ComplexType
      constructor-args: [10+0j]
        
    - object: MyList
      class: types.ListType
      constructor-args: [[1,2,3,4]]
        
    - object: MyTuple
      class: types.TupleType
      constructor-args: [["a", "b", "c"]]
      
    - object: MyDict
      class: types.DictType
      constructor-args: [{1: "a", 2: "b", 3: "c"}]
      
    - object: MyRef
      class: decimal.Decimal
      constructor-args: [{ref: MyDecimal}]

Configuration of how YAML elements are mapped onto Python types is stored in the springpython.config.yaml_mappings dictionary which can be easily customized to fulfill one's needs. The dictionary's keys are names of the YAML elements and its values are the coresponding Python types, written as strings in the form of "package_name.module_name.class_name" - note that the "package_name.module_name." part is required, it needs to be a fully qualified name.

Let's assume that in your configuration you're frequently creating objects of type interest_rate.InterestRateFrequency, here's how you can save yourself a lot of typing by customizing the mappings dictionary. First, on Python side, create an InterestRate class, such as:

class InterestRate(object):
    def __init__(self, value=None):
        self.value = value

.. which will allow you to create such a YAML context

objects: 
    - object: base_interest_rate
      interest_rate: "7.35"

.. then, before creating the context, update the mappings dictionary as needed and next you'll be able to access the base_interest_rate object as if it had been defined using the standard syntax:

from springpython.context import ApplicationContext
from springpython.config import YamlConfig, yaml_mappings

yaml_mappings.update({"interest_rate": "interest_rate.InterestRate"})

# .. create the context now
container = ApplicationContext(YamlConfig("./app-ctx.yaml"))

# .. fetch the object
base_interest_rate = container.get_object("base_interest_rate")

# .. will show "7.35", as defined in the "./app-ctx.yaml" config
print base_interest_rate.value

2.3.2.5. Constructors

Python functions can have both positional and named arguments. Positional arguments get assembled into a tuple, and named arguments are assembled into a dictionary, before being passed to a function call. Spring Python takes advantage of that option when it comes to constructor calls. The following block of configuration data shows defining positional constructors.

object: AnotherSingletonString
class: springpythontest.support.testSupportClasses.StringHolder
constructor-args:
    - position 1's constructor value

Spring Python will read these and then feed them to the class constructor in the same order as shown here.

The following code configuration shows named constructor arguments. Spring Python converts these into keyword arguments, meaning it doesn't matter what order they are defined.

object: MultiValueHolder
class: springpythontest.support.testSupportClasses.MultiValueHolder
constructor-args:
    a: alt a
    b: alt b

This was copied from the code's test suite, where a test case is used to prove that order doesn't matter. It is important to note that positional constructor arguments are fed before named constructors, and that overriding a the same constructor parameter both by position and by name is not allowed by Python, and will in turn, generate a run-time error.

It is also valuable to know that you can mix this up and use both.

2.3.2.6. Object definition inheritance

Just like XMLConfig, YamlConfig allows for wiring the objects definitions into hierarchies of abstract and children objects, thus this section is in most parts a repetition of what's documented here

Definitions may be stacked up into hierarchies of abstract parents and their children objects. A child object not only inherits all the properties and constructor arguments from its parent but it can also easily override any of the inherited values. This can save a lot of typing when configuring non-trivial application contexts which would otherwise need to repeat the same configuration properties over many objects definitions.

An abstract object is identified by having an abstract attribute equal to True and the child ones are those which have a parent attribute set to ID of an object from which the properties or constructor arguments should be inherited. Child objects must not specify the class attribute, its value is taken from their parents.

An object may be both a child and an abstract one.

Here's a hypothetical configuration of a set of services exposed by a server. Note how you can easily change the CRM environment you're invoking by merely changing the concrete service's (get_customer_id or get_customer_profile) parent ID.

objects: 
    - object: service
      class: springpythontest.support.testSupportClasses.Service
      abstract: True
      scope: singleton
      lazy-init: True
      properties:
        ip: 192.168.1.153
        
    - object: crm_service_dev
      abstract: True
      parent: service
      properties:
        port: "3392"
        
    - object: crm_service_test
      abstract: True
      parent: service
      properties:
        port: "3393"
        
    - object: get_customer_id
      parent: crm_service_dev
      properties:
        path: /soap/invoke/get_customer_id
        
    - object: get_customer_profile
      parent: crm_service_test
      properties:
        path: /soap/invoke/get_customer_profile

Here's how you can override inherited properties; both get_customer_id and get_customer_profile object definitions will inherit the path property however the actual objects returned by the container will use local, overridden, values of the property.

objects: 
    - object: service
      class: foo.Service
      abstract: True
      scope: singleton
      lazy-init: True
      properties:
        ip: 192.168.1.153
        port: "3392"
        path: /DOES-NOT-EXIST
        
    - object: get_customer_id
      parent: service
      properties:
        path: /soap/invoke/get_customer_id
        
    - object: get_customer_profile
      parent: service
      properties:
        path: /soap/invoke/get_customer_profile

If you need to get an abstract object from a container, use the .get_object's ignore_abstract parameter, otherwise springpython.container.AbstractObjectException will be raised. Observe the difference:

# .. skip creating the context

# No exception will be raised, even though 'service' is an abstract object
service = ctx.get_object("service", ignore_abstract=True)

# Will show the object
print service

# Will raise AbstractObjectException
service = ctx.get_object("service")

2.3.3. PythonConfig and @Object - decorator-driven configuration

By defining a class that extends PythonConfig and using the @Object decorator, you can wire your application using pure python code.

from springpython.config import PythonConfig
from springpython.config import Object
from springpython.context import scope

class MovieBasedApplicationContext(PythonConfig):
    def __init__(self):
        super(MovieBasedApplicationContext, self).__init__()
        
    @Object(scope.PROTOTYPE)
    def MovieLister(self):
        lister = MovieLister()
        lister.finder = self.MovieFinder()
        lister.description = self.SingletonString()
        self.logger.debug("Description = %s" % lister.description)
        return lister
    
    @Object(scope.SINGLETON)
    def MovieFinder(self):
        return ColonMovieFinder(filename="support/movies1.txt")
    
    @Object(lazy_init=True)    # scope.SINGLETON is the default
    def SingletonString(self):
        return StringHolder("There should only be one copy of this string")
    
    def NotExposed(self):
        pass

As part of this example, the method NotExposed is also shown. This indicates that using get_object won't fetch that method, since it isn't considered an object.

By using pure python, you don't have to deal with any XML. If you look closely, you will notice that the container code below is only different in the line actually creating the container. Everything else is the same.

from springpython.context import ApplicationContext

container = ApplicationContext(MovieBasedApplicationContext())
service = container.get_object("MovieLister")

PythonConfig's support for abstract objects is different to that of XMLConfig or YamlConfig. With PythonConfig, the children object do not automatically inherit nor override the parents' properties, they in fact receive the values returned by their parents and it's up to them to decide, using Python code, whether to use or to discard the values received.

A child object must have as many optional arguments as there are expected to be returned by its parent.

Observe that in the following example the child definitions must define an optional 'req' argument; in runtime they will be passed its value basing on what their parent object will return. Note also that request is of PROTOTYPE scope, if it were a SINGLETON then both get_customer_id_request and get_customer_profile_request would receive the very same Request instance which, in other circumstances, could be a desirable effect but not in the example.


# stdlib
import uuid4

# .. skip Spring Python imports

class Request(object):
    def __init__(self, nonce=None, user=None, password=None):
        self.nonce = nonce
        self.user = user
        self.password = password

    def __str__(self):
        return "<id=%s %s %s %s>" % (hex(id(self)), self.nonce, self.user, self.password)

class TestAbstractContext(PythonConfig):

    @Object(scope.PROTOTYPE, abstract=True)
    def request(self):
        return Request()

    @Object(parent="request")
    def request_dev(self, req=None):
        req.user = "dev-user"
        req.password = "dev-password"
        
        return req
        
    @Object(parent="request")
    def request_test(self, req=None):
        req.user = "test-user"
        req.password = "test-password"
        
        return req
        
    @Object(parent="request_dev")
    def get_customer_id_request(self, req=None):
        req.nonce = uuid4().hex
        
        return req
        
    @Object(parent="request_test")
    def get_customer_profile_request(self, req=None):
        req.nonce = uuid4().hex
        
        return req

Same as with the other configuration modes, if you need to get an abstract object from a container, use the .get_object's ignore_abstract parameter, otherwise springpython.container.AbstractObjectException will be raised:

# .. skip creating the context

# No exception will be raised, even though 'request' is an abstract object
request = ctx.get_object("request", ignore_abstract=True)

# Will show the object
print request

# Will raise AbstractObjectException
request = ctx.get_object("request")

2.3.4. PyContainerConfig - Spring Python's original XML format

PyContainerConfig is a class that scans object definitions stored in the format defined by PyContainer, which was the original XML format used by Spring Python to define objects.

An important thing to note is that PyContainer used the term component, while Spring Python uses object. In order to support this legacy format, component will show up in PyContainerConfig-based configurations.

[Note]PyContainer's format is deprecated

PyContainer's format and the original parser was useful for getting this project started. However, it has shown its age by not being easy to revise nor extend. So this format is being retired. This parser is solely provided to help sustain existing Spring Python apps until they can migrate to either the XMLConfig or the YamlConfig format.

<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://www.springframework.org/springpython/schema/pycontainer-components"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/springpython/schema/pycontainer-components
       		http://springpython.webfactional.com/schema/context/spring-python-pycontainer-context-1.0.xsd">

    <component id="MovieLister" class="springpythontest.support.testSupportClasses.MovieLister" scope="prototype">
	    <property name="finder" local="MovieFinder"/>
	    <property name="description" local="SingletonString"/>
	</component>

	<component id="MovieFinder" class="springpythontest.support.testSupportClasses.ColonMovieFinder" scope="singleton">
		<property name="filename">"support/movies1.txt"</property>
	</component>
    
    <component id="SingletonString" class="springpythontest.support.testSupportClasses.StringHolder">
    	<property name="str">"There should only be one copy of this string"</property>
    </component>
</components>

The definitions stored in this file are fed in to a PyContainerConfig which scans it, and then sends the meta-data to the ApplicationContext. Then, when the application code requests an object named "MovieLister" from the container, the container utilizes an object factory to create an object and return it.

from springpython.context import ApplicationContext
from springpython.config import PyContainerConfig

container = ApplicationContext(PyContainerConfig("app-context.xml"))
service = container.get_object("MovieLister")

2.3.5. SpringJavaConfig

The SpringJavaConfig is a class that scans object definitions stored in the format defined by the Spring Framework's original java version. This makes it even easier to migrate parts of an existing Spring Java application onto the Python platform.

[Note]This is about configuring python objects NOT java objects

It is important to point out that this has nothing to do with configuring java-backed beans from Spring Python, or somehow injecting java-backed beans magically into a python object. This is PURELY for configuring python-backed objects using a format that was originally designed for pure java beans.

When ideas like "converting java to python" are mentioned, it is meant that re-writing certain parts of your app in python would require a similar IoC configuration, however, for the java and python parts to integrate, you must utilize interoperable solutions like web service or other remoting technologies.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

	<bean id="MovieLister" class="springpythontest.support.testSupportClasses.MovieLister" scope="prototype">
		<property name="finder" ref="MovieFinder"/>
		<property name="description"><ref bean="SingletonString"/></property>
	</bean>
	
	<bean id="MovieFinder" class="springpythontest.support.testSupportClasses.ColonMovieFinder" scope="singleton">
		<property name="filename"><value>support/movies1.txt</value></property>
	</bean>
	
	<bean id="SingletonString" class="springpythontest.support.testSupportClasses.StringHolder">
		<property name="str" value="There should only be one copy of this string"></property>
	</bean>
</beans>

The definitions stored in this file are fed in to a SpringJavaConfig which scans it, and then sends the meta-data to the ApplicationContext. Then, when the application code requests an object named "MovieLister" from the container, the container utilizes an object factory to create an object and return it.

from springpython.context import ApplicationContext
from springpython.config import SpringJavaConfig

container = ApplicationContext(SpringJavaConfig("app-context.xml"))
service = container.get_object("MovieLister")

Again, the only difference in your code is using SpringJavaConfig instead of PyContainerConfig on one line. Everything is the same, since it is all inside the ApplicationContext.

[Note]What parts of Spring Java configuration are supported?

It is important to note that only spring-beans-2.5 has been tested at this point in time. It is possible that older versions of the XSD spec may also work.

Spring Java's other names spaces, like tx and aop, probably DON'T work. They haven't been tested, and there is no special code that will utilize their feature set.

How much of Spring Java will be supported? That is an open question, best discussed on Spring Python's community forum. Basically, this is meant to ease current Java developers into Spring Python and/or provide a means to split up objects to support porting parts of your application into Python. There isn't any current intention of providing full blown support.

2.3.6. Mixing Configuration Modes

Spring Python also supports providing object definitions from multiple sources, and allowing them to reference each other. This section shows the same app context, but split between two different sources.

First, the XML file containing the key object that gets pulled:

<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://www.springframework.org/springpython/schema/pycontainer-components"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/springpython/schema/pycontainer-components
       		http://springpython.webfactional.com/schema/context/spring-python-pycontainer-context-1.0.xsd">

    <component id="MovieLister" class="springpythontest.support.testSupportClasses.MovieLister" scope="prototype">
	    <property name="finder" local="MovieFinder"/>
	    <property name="description" local="SingletonString"/>
	</component>

    <component id="SingletonString" class="springpythontest.support.testSupportClasses.StringHolder">
    	<property name="str">"There should only be one copy of this string"</property>
    </component>
</components>

Notice that MovieLister is referencing MovieFinder, however that object is NOT defined in this location. The definition is found elsewhere:

class MixedApplicationContext(PythonConfig):
    def __init__(self):
        super(MixedApplicationContext, self).__init__()
        
    @Object(scope.SINGLETON)
    def MovieFinder(self):
        return ColonMovieFinder(filename="support/movies1.txt")
[Note]Object ref must match function name

In this situation, an XML-based object is referencing python code by the name MovieFinder. It is of paramount importance that the python function have the same name as the referenced string.

With some simple code, this is all brought together when the container is created.

from springpython.context import ApplicationContext
from springpython.config import PyContainerConfig

container = ApplicationContext([MixedApplicationContext(),
                                PyContainerConfig("mixed-app-context.xml")])
movieLister = container.get_object("MovieLister")

In this case, the XML-based object definition signals the container to look elsewhere for a copy of the MovieFinder object, and it succeeds by finding it in MixedApplicationContext.

It is possible to switch things around, but it requires a slight change.

class MixedApplicationContext2(PythonConfig):
    def __init__(self):
        super(MixedApplicationContext2, self).__init__()
        
    @Object(scope.PROTOTYPE)
    def MovieLister(self):
        lister = MovieLister()
        lister.finder = self.app_context.get_object("MovieFinder")  # <-- only line that is different
        lister.description = self.SingletonString()
        self.logger.debug("Description = %s" % lister.description)
        return lister
    
    @Object    # scope.SINGLETON is the default
    def SingletonString(self):
        return StringHolder("There should only be one copy of this string")
<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://www.springframework.org/springpython/schema/pycontainer-components"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/springpython/schema/pycontainer-components
       		http://springpython.webfactional.com/schema/context/spring-python-pycontainer-context-1.0.xsd">

	<component id="MovieFinder" class="springpythontest.support.testSupportClasses.ColonMovieFinder" scope="singleton">
		<property name="filename">"support/movies1.txt"</property>
	</component>

</components>

An XML-based object definition can refer to a @Object by name, however, the python code has to change its direct function call to a container lookup, otherwise it will fail.

[Note]PythonConfig is ApplicationContextAware

In order to perform a get_object, the configuration needs a handle on the surrounding container. The base class PythonConfig provides this, so that you can easily look for any object (local or not) by using self.app_context.get_object("name")

2.4. Object Factories

Spring Python offers two types of factories, ReflectiveObjectFactory and PythonObjectFactory. These classes should rarely be used directly by the developer. They are instead used by the different types of configuration scanners.

2.5. Testable Code

One key value of using the IoC container is the how you can isolate parts of your code for better testing. Imagine you had the following configuration:

from springpython.config import *
from springpython.context import *

class MovieBasedApplicationContext(PythonConfig):
    def __init__(self):
        super(MovieBasedApplicationContext, self).__init__()
        
    @Object(scope.PROTOTYPE)
    def MovieLister(self):
        lister = MovieLister()
        lister.finder = self.MovieFinder()
        lister.description = self.SingletonString()
        self.logger.debug("Description = %s" % lister.description)
        return lister
    
    @Object(scope.SINGLETON)
    def MovieFinder(self):
        return ColonMovieFinder(filename="support/movies1.txt")
    
    @Object    # scope.SINGLETON is the default
    def SingletonString(self):
        return StringHolder("There should only be one copy of this string")

To inject a test double for MovieFinder, your test code would only have to extend the class and override the MovieFinder method, and replace it with your stub or mock object. Now you have a nicely isolated instance of MovieLister.

class MyTestableAppContext(MovieBasedApplicationContext):
    def __init__(self):
        super(MyTestableAppContext, self).__init__()
        
    @Object
    def MovieFinder(self):
        return MovieFinderStub()

2.6. Querying and modifying the ApplicationContext in runtime

ApplicationContext instances expose two attributes and an utility method which let you learn about their current state and dynamically alter them in runtime.

  • object_defs is a dictionary of objects definitions, that is, the templates based upon which the container will create appropriate objects, e.g. your singletons,

  • objects is a dictionary of already created objects stored for later use,

  • get_objects_by_type(type, include_type=True) returns those ApplicationContext's objects which are instances of a given type or of its subclasses. If include_type is False then only instances of the type's subclasses will be returned.

Here's an example showing how you can easily query a context to find out what definitions and objects it holds. The context itself is stored using PythonConfig in the sample_context.py module and demo.py contains the code which examines the context.

#
# sample_context.py
#

from springpython.config import Object
from springpython.context import scope
from springpython.config import PythonConfig

class MyClass(object):
    pass

class MySubclass(MyClass):
    pass

class SampleContext(PythonConfig):
    def __init__(self):
        super(SampleContext, self).__init__()
        
    @Object
    def http_port(self):
        return 18000
    
    @Object
    def https_port(self):
        return self._get_https_port()
    
    def _get_https_port(self):
        return self.http_port() + 443
        
    @Object
    def my_class_object1(self):
        return MyClass()
        
    @Object
    def my_class_object2(self):
        return MyClass()
        
    @Object
    def my_subclass_object1(self):
        return MySubclass()
        
    @Object
    def my_subclass_object2(self):
        return MySubclass()
        
    @Object
    def my_subclass_object3(self):
        return MySubclass()
        

#
# demo.py
#

# Spring Python
from springpython.context import ApplicationContext

# Our sample code.
from sample_context import SampleContext, MyClass, MySubclass

# Create the context.
ctx = ApplicationContext(SampleContext())

# Do we have an 'http_port' object?
print "http_port" in ctx.objects

# Does the context have a definition of an 'ftp_port' object?
print "ftp_port" in ctx.object_defs

# How many objects are there? Observe the result is 7, that's because one of
# the methods - _get_https_port - is not managed by the container.
print len(ctx.objects)

# List the names of all objects defined.
print ctx.object_defs.keys()

# Returns all instances of MyClass and of its subclasses.
print ctx.get_objects_by_type(MyClass)

# Returns all instances of MyClass' subclasses only.
print ctx.get_objects_by_type(MyClass, False)

# Returns all integer objects.
print ctx.get_objects_by_type(int)

The .object_defs dictionary stores instances of springpython.config.ObjectDef class, these are the objects you need to inject into the container to later successfully access them as if they were added prior to the application's start. An ObjectDef allows one to specify the very same set of parameters an @Object decorator does. The next examples shows how to insert two definitions into a context, one will be a prototype - a new instance of Foo will be created on each request, the second one will be a singleton - only one instance of Bar will ever be created and stored in a cache of singletons. This time the example employs the Python's standard library logging module to better show in the DEBUG mode what is going on under the hood.

#
# sample_context2.py
#


# Spring Python
from springpython.config import PythonConfig

class SampleContext2(PythonConfig):
    def __init__(self):
        super(SampleContext2, self).__init__()
 

#
# demo2.py
#
        
# stdlib
import logging

# Spring Python
from springpython.config import Object, ObjectDef
from springpython.context import ApplicationContext
from springpython.factory import PythonObjectFactory
from springpython.context.scope import SINGLETON, PROTOTYPE

# Our sample code.
from sample_context2 import SampleContext2

# Configure logging.
log_format = "%(msecs)d - %(levelname)s - %(name)s - %(message)s"
logging.basicConfig(level=logging.DEBUG, format=log_format)

class Foo(object):
    def run(self):
        return "Foo!"
        
class Bar(object):
    def run(self):
        return "Bar!"

# Create the context - part 1. in the logs.
ctx = ApplicationContext(SampleContext2())

# Definitions of objects that will be dynamically injected into container.

@Object(PROTOTYPE)
def foo():
    """ Returns a new instance of Foo on each call.
    """
    return Foo()
    
@Object # SINGLETON is the default.
def bar():
    """ Returns a singleton Bar every time accessed.
    """
    return Bar()

# A reference to the function wrapping the actual 'foo' function.
foo_wrapper = foo.func_globals["_call_"]

# Create an object definition, note that we're telling to return
foo_object_def = ObjectDef(id="foo", factory=PythonObjectFactory(foo, foo_wrapper), scope=PROTOTYPE, lazy_init=foo_wrapper.lazy_init)

# A reference to the function wrapping the actual 'bar' function.
bar_wrapper = foo.func_globals["_call_"]

bar_object_def = ObjectDef(id="foo", factory=PythonObjectFactory(bar, bar_wrapper), scope=SINGLETON, lazy_init=bar_wrapper.lazy_init)

ctx.object_defs["foo"] = foo_object_def
ctx.object_defs["bar"] = bar_object_def

# Access "foo" - part 2. in the logs.
for x in range(3):
    foo_instance = ctx.get_object("foo")

# Access "bar" - part 3. in the logs.
for x in range(3):
    bar_instance = ctx.get_object("bar")

Here's how it shows in the logs. For clarity, the log has been divided into three parts. Part 1. reads the object definitions from SampleContext2, as we see, nothing has been read from it as it's still been empty at this point. After adding definitions to the .object_defs dictionary, we're now at parts 2. and 3. - in 2. the 'foo' object, a prototype one, is being created three times, as expected. In part 3. the singleton 'bar' object is created and stored in a singleton cache once only even though we're accessing it three times in our code.

# Part 1.

100 - DEBUG - springpython.config.PythonConfig - ==============================================================
100 - DEBUG - springpython.config.PythonConfig - Parsing <sample_context2.SampleContext2 object at 0x17e70d0>
101 - DEBUG - springpython.config.PythonConfig - ==============================================================
101 - DEBUG - springpython.container.ObjectContainer - === Done reading object definitions. ===

# Part 2.

102 - DEBUG - springpython.context.ApplicationContext - Did NOT find object 'foo' in the singleton storage.
102 - DEBUG - springpython.context.ApplicationContext - Creating an instance of id=foo props=[] scope=scope.PROTOTYPE factory=PythonObjectFactory(<function foo at 0x184c2a8>)
102 - DEBUG - springpython.factory.PythonObjectFactory - Creating an instance of foo
102 - DEBUG - springpython.config.objectPrototype<function foo at 0x7f6d15db0a28> - ()scope.PROTOTYPE - This IS the top-level object, calling foo().
102 - DEBUG - springpython.config.objectPrototype<function foo at 0x7f6d15db0a28> - ()scope.PROTOTYPE - Found <__main__.Foo object at 0x184b650>

102 - DEBUG - springpython.context.ApplicationContext - Did NOT find object 'foo' in the singleton storage.
102 - DEBUG - springpython.context.ApplicationContext - Creating an instance of id=foo props=[] scope=scope.PROTOTYPE factory=PythonObjectFactory(<function foo at 0x184c2a8>)
102 - DEBUG - springpython.factory.PythonObjectFactory - Creating an instance of foo
103 - DEBUG - springpython.config.objectPrototype<function foo at 0x7f6d15db0a28> - ()scope.PROTOTYPE - This IS the top-level object, calling foo().
103 - DEBUG - springpython.config.objectPrototype<function foo at 0x7f6d15db0a28> - ()scope.PROTOTYPE - Found <__main__.Foo object at 0x184b690>

103 - DEBUG - springpython.context.ApplicationContext - Did NOT find object 'foo' in the singleton storage.
103 - DEBUG - springpython.context.ApplicationContext - Creating an instance of id=foo props=[] scope=scope.PROTOTYPE factory=PythonObjectFactory(<function foo at 0x184c2a8>)
103 - DEBUG - springpython.factory.PythonObjectFactory - Creating an instance of foo
103 - DEBUG - springpython.config.objectPrototype<function foo at 0x7f6d15db0a28> - ()scope.PROTOTYPE - This IS the top-level object, calling foo().
103 - DEBUG - springpython.config.objectPrototype<function foo at 0x7f6d15db0a28> - ()scope.PROTOTYPE - Found <__main__.Foo object at 0x184b650>

# Part 3.

103 - DEBUG - springpython.context.ApplicationContext - Did NOT find object 'bar' in the singleton storage.
103 - DEBUG - springpython.context.ApplicationContext - Creating an instance of id=foo props=[] scope=scope.SINGLETON factory=PythonObjectFactory(<function bar at 0x184c578>)
103 - DEBUG - springpython.factory.PythonObjectFactory - Creating an instance of bar
104 - DEBUG - springpython.config.objectSingleton<function bar at 0x17e5aa0> - ()scope.SINGLETON - This IS the top-level object, calling bar().
104 - DEBUG - springpython.config.objectSingleton<function bar at 0x17e5aa0> - ()scope.SINGLETON - Found <__main__.Bar object at 0x184b690>
104 - DEBUG - springpython.context.ApplicationContext - Stored object 'bar' in container's singleton storage

Please note that what has been shown above applies to runtime only, adding object definitions to the container doesn't mean the changes will be in any way serialized to the file system, they are transient and will be lost when the application will be shutting down. Another thing to keep in mind is that you'll be modifying a raw Python dictionary and if your application is multi-threaded, you'll have to serialize the access from concurrent threads yourself.