This article focuses on providing an introduction to the Spring Python which is a framework that simplifies writing Python applications. The first section of the article provides introductory details on setting up the Python environment and also introduces simple constructs on Python language which will be useful for novice Python programmers. The later section of the article focuses on setting up the Spring Python framework for writing Python applications using the Spring framework. This article provides various sample code snippets on various topics relevant to each section as and when required. This article assumes that the reader is comfortable with the core aspects of Spring framework like Spring Container, Dependency Injection etc.
Python is an easy to use language for writing robust applications and it is read by an interpreter for execution. Since the later sections of the article focuses on writing Spring applications for Python, this section will provide basic language constructs of Python like variable declaration, string manipulation, defining listing, conditional blocks, function definitions, finally with classes and objects.
Download the latest stable distribution of python from http://www.python.org/download/ and install it. Define an environment variable called PYTHON pointing to C:\\Python (assuming that the installation directory is C:\\Python) and update the path variable to include the python home. To make sure things are correct, invoke the python interpreter by typing python.exe in the command prompt. It will produce the following output message
python Python 2.7 (#1, Feb 28 2010, 00:02:06) Type "help", "copyright", "credits" or "license" for more information.
Type the following code listing in a text editor and save it as print.py. Python’s file extension should be ‘.py’ so that it can be recognized by Python interpretor. The code declares a variable with the name ‘color’ for storing a string content. It then displays the message ‘Color value is blue’ on the console. Note that the function print is used to output messages to the console. String literals can be enclosed in double quotes. Also note the concatenation operator used for merging two strings is ‘+’.
color = "blue" print "Color value is " + color
Go to the command prompt and type python.exe print.py. This will invoke the python interpretor with the file name print.py which is to be interpreted and displays the following message in the console.
Color value is blue
Have a look at the following code which declares a list for storing various colors. Each item within ‘‘ will be interpreted as a list item. To find out the length of the list, the function len() can be used by passing in the list object. Note that the method insert(), append(), remove() exists on the list object and the method append() is used here for adding a new item at the end.
colors = ['red', 'blue', 'green'] print colors print "Length is " print len(colors) new_colors = ['yellow', 'black', colors, 'brown'] print new_colors new_colors.append("test1"); print new_colors The output will look like the following when running the above code. ['red', 'blue', 'green'] Length is 3 ['yellow', 'black', ['red', 'blue', 'green'], 'brown'] ['yellow', 'black', ['red', 'blue', 'green'], 'brown', 'test1']
The output will look like the following when running the above code.
['red', 'blue', 'green'] Length is 3 ['yellow', 'black', ['red', 'blue', 'green'], 'brown'] ['yellow', 'black', ['red', 'blue', 'green'], 'brown', 'test1']
In this section, we will see the conditional construct ‘if’ and ‘else’ used in Python. The following example also demonstrates the usage of getting input from the user through the function raw_input(). Note that the return type of the raw_input() function is string and if we want to do a conversion to integer, we have to use the function int().
value = int(raw_input("Enter a number: ")); if value == 0: print "Value is equal to 0"; elif value < 0: print "Value is negative"; elif value > 0: print "Value is positive";
Note that the keywords used for the conditional constructs are if and elif. Also the beginning of the conditional block is represented through : and ; can be used as statement terminator.
Here, we will look into the usage of the looping construct. In the following code snippet, we have declared a list of holding numbers. Then we use for loop for iterating over the list of items. The syntax is “for item in itemCollection”. Also note that the for loop block begins with :.
numbers = ['one', 'two', 'three', 'four', 'five']; for number in numbers: print "Number is " + number When running the above program, the output looks like, Number is one Number is two Number is three Number is four Number is five
The following code snippet illustrates the usage of while loop for printing the values of a and b after getting incremented.
a = 10; b =100; while a <= b: print "A is " + str(a); print "B is " + str(b); a = a + 10;
The output for the above program is displayed here.
A is 10 B is 100 A is 20 B is 100 A is 30 B is 100 A is 40 B is 100 A is 50 B is 100 A is 60 B is 100 A is 70 B is 100 A is 80 B is 100 A is 90 B is 100 A is 100 B is 100
We will see how to define functions in python. Function definitions begin with the keyword ‘def’ which stands for definition. Following the keyword will be the name of the function. Function parameters follow between ‘(‘ and ‘)’. Note the usage of the function add() which is given below.
def add(a = 0,b = 0): print "Addition of " + str(a) + " and " + str(b) + " is " + str((a+b)); add(); add(10); add(10,10);
The function add() accepts two parameters a and b. It is also possible to define default values to function parameters in the function definition itself.
Note that for the parameters a and b we have defined the default values as 0. In such cases, the arguments can be ignored while calling the function. Have a look at the first invocation of the function add() where the optional parameters are missing in which case the values assigned to a and b will be 0. The output will be the following,
Addition of 0 and 0 is 0 Addition of 10 and 0 is 10 Addition of 10 and 10 is 20
Classes and Objects
Python also suports object orientation capabilities to the language through the declaration of classes and objects. The class declaration begins with the class keyword with the name of the class following afterwards.
class Test: varA = "Test Variable"; def function1(): print "Test function1"; def function2(): print "Test function2"; test1 = Test(); print "Variable val is " + test1.varA test1.function1; #test1.function2();
The name of the class is ‘Test’, two functions are defined ‘function1′ and ‘function2′ and an attribute by the name ‘varA’ is defined with some default value. Objects can be created to a class in the following manner.
objectName = className();
Note that the syntax looks very similar to the syntax present in languages like C++ and Java. The code creates an object for the class and then displays the value attribute and then it calls the functions ‘function1′ and ‘function2′.
Spring Python is a framework which provides integration support to Python applications. With Spring Python, Python applications can extend the spring framework’s capabilities such as Dependency Injection, IOC Container, Aspect oriented support, Remoting etc. We will be discussing all these capabilities in the following sections.
python setup.py install
This makes sure that the Spring Python‘s libraries are visible to the Python interpreter.
First Spring Python Application
First, we will make use of Spring’s configuration based approach in the following example. The example will declare a Service in a configuration file and then will make use of Spring Python‘s libraries for locating and loading it.
The following listing provides a simple listing that defines a Service ‘TestService’ which defines a method ‘invokeService’. Note that the method takes two parameters, the first being the invoking object denoted as ‘self’ which is similar to ‘this’ keyword in Java. The second parameter ‘service_invoker’ is the client name who calls this service.
class TestService: def invokeService(self, service_invoker): print "Service is invoker by " + service_invoker
Using the Test Service
Have a look at the following code which acts as the client code that locates and loads the service. Note the usage of the Spring Python‘s classes.
from springpython.context import ApplicationContext from springpython.config import XMLConfig context = ApplicationContext(XMLConfig("test-service.xml")); testService = context.get_object("testService"); testService.invokeService("test");
For importing classes from a module, the syntax used is “from moduleName import className”. Here the module names are ‘springpython.context’ and ‘springpython.config’. The classes to be imported are ApplicationContext and XMLConfig. The class XMLConfig is used to represent the information stored in an XML configuration. The class ApplicationContext is used which takes a XMConfig object as an input for locating and loading bean definitions. Note that an instance of TestService is acquired by calling the method get_object() by passing in an identifier to the object.
The configuration file referred in the above code listing is given below. It defines a bean with the identifier ‘testService’. The two possible scopes that can be given are singleton and prototype. The scope singleton ensures that only one object is created irrespective of the number of calls made to get_object() and when the scope prototype is specified, it is always to create a new object.
<?xml version="1.0" encoding="UTF-8"?> <objects xmlns="http://www.springframework.org/springpython/schema/objects" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/springpython/schema/objects http://springpython.webfactional.com/schema/context/spring-python-context-1.0.xsd"> <object id="testService" scope="prototype"> </object> </objects>
In the last section, we saw the usage of Application, now we will extend the capabilities of using Dependency Injection in the following example. In this section, we will see the usage of Book and chapters. A book can contain multiple chapters, hence a one-to-many relationship is established between Book and Chapter objects.
Have a look at the code listing that defines the Book class. The class defines properties such as the name, author and a list of chapter objects.
class Book: name = ""; author = ""; chapters = ; def set(self, name, author): self.name = name; self.author = author; def addChapter(self, chapter): self.chapters.append(chapter); def removeChapter(self, chapter): self.chapters.remove(chapter); def display(self): print "Book Information :"; print "\tName is '" + self.name + "'"; print "\tAuthor is '" + self.author + "'"; print "\tChapters:"; for aChapter in self.chapters: print aChapter.display();
The code also define methods addChapter() and removeChapter() which will respectively add and remove a chapter object into the book object. Also the method display() is provided for displaying the book information.
The code listing for the Chapter class is given below. This model also defines the properties name and the number of pages contained in a chapter. The method display() is defined for displaying the information on the Chapter object.
class Chapter: name = "test"; pages = 0; def set(self, name, pages): self.name = name; self.pages = pages; def display(self): print "\t\tChapter Info :"; print "\t\t\t Name is '" + self.name + "'"; print "\t\t\t No of pages is '" + str(self.pages) + "'";
The client program that will make use of Book and Chapter objects is given below. It loads the object information from the context file book.xml.
from springpython.config import XMLConfig from springpython.context import ApplicationContext from book import Book from chapter import Chapter configuration = XMLConfig("book.xml"); context = ApplicationContext(configuration); bookObject = context.get_object("book-001"); bookObject.display(); print "----------------------------------------------------"; newChapterReflection = Chapter(); newChapterReflection.name = "Using Reflection"; newChapterReflection.pages = 55; bookObject.addChapter(newChapterReflection); newChapterJdbc = Chapter(); newChapterJdbc.name = "JDBC Programming"; newChapterJdbc.pages = 62; bookObject.addChapter(newChapterJdbc); bookObject.display(); print "----------------------------------------------------";
Once the static book object information maintained in the context information is loaded, the program tries to create new chapter objects and tries to add it to the existing book object.
The configuration file that stores the Book and the chapter information is given below. Note that the definition of the book object is defined and given an identifier ‘book-001′.
<?xml version="1.0" encoding="UTF-8"?> <objects xmlns="http://www.springframework.org/springpython/schema/objects" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/springpython/schema/objects http://springpython.webfactional.com/schema/context/spring-python-context-1.0.xsd"> <object id="book-001" scope="prototype"> <property name="name" value="Java - Complete Reference"/> <property name="author" value="James"/> <property name="chapters"> <list> <ref object = "book-001-chapter-001"/> <ref object = "book-001-chapter-002"/> </list> </property> </object> <object id="book-001-chapter-001" scope="prototype"> <property name="name" value="Introduction"/> <property name="pages" value="30"/> </object> <object id="book-001-chapter-002" scope="prototype"> <property name="name" value="Classes and Objects"/> <property name="pages" value="48"/> </object> </objects>
The above configuration file also declares new chapter objects and the identifiers are referred to as book object as a list.
In this example, we will see how to declare and use annotations on Python classes using Spring Python‘s framework. We will also see the usage of PythonConfig class in this example. In the last few sections, we keep defining bean objects in the xml configuration file, represent the configuration file as a context and then load the bean definition from the context.
Have a look at the following code that declares the Employee and the Manager classes. We have also created a class OrganizationContext which extends PythonConfig class. By doing this, any method that has @Object annotation can be looked as bean definition.
from springpython.config import *; from springpython.context import * class Employee: name = ""; age = 0; def display(self): print "Employee name is " + self.name; print "Employee Age is " + self.name; class Manager: name = ""; age = 0; def display(self): print "Manager name is " + self.name; print "Manager Age is " + self.name; class OrganizationContext(PythonConfig): @Object def Employee(self): employee = Employee(); employee.name = "David"; employee.age = 31; @Object def Manager(self): manager = Manager(); manager.name = "Mike"; manager.age = 38;
In the above code, the class OrganizationContext has the method Employee() and Manager() decorated with the annotation @Object. This makes the client to locate the employee and the manager objects from the organization context.
The below listing shows the usage of the client that looks up the organizational objects Employee and Manager from the Organization context.
from springpython.context import ApplicationContext from annotation import OrganizationContext organizationContext = OrganizationContext(); context = ApplicationContext(organizationContext); employee = context.get_object("Employee"); employee.display(); manager = context.get_object("Manager"); manager.display();
This time we are not using XMLConfig class for representing the configuration information because the configurational objects are present in the class OrganizationContext. Rest of the code is the same which gets a reference to the Manager and the Employee objects.
In this section, we will look into Spring Python‘s AOP capabilities in the form of Interceptors. Interceptors are used to make interceptor calls to a method for hooking in customized logic. For example, for bank service methods’ transactional related capabilities can be modeled as an interceptor and can be hooked in across the business method calls.
In our example, we will see how to define multiple interceptors that declares specialized logic and will see how they can be injected between service calls. The below code listing defines the PrinterService which provides methods startPrinting() and stopPrinting() for stating and stopping the printing operation on a given filename.
class PrinterService: def startPrinting(self, filename): print "Starting to print the filename '" + filename + "'"; def stopPrinting(self, filename): print "Stopping the print operation on the filename '" + filename + "'";
We will add a simple logging interceptor so that operations done on the printer object can be logged for history tracking purposes. Have a look at the following looging interceptor code which extends the predefined class MethodInterceptor. The customized class should override the method invoke() that will be called during the invocation of the method.
This method can control whether the method can be invoked or not. But for this simple logging interceptor, the implementation will always proceed with the method invocation before making the method proceeding.
from springpython.aop import *; class LogInterceptor(MethodInterceptor): def invoke(self, invocation): #method_name = str(invocation.__getattr__("method_name")); method_name = ""; print "Before the invocation of the method '" + method_name + "'"; invocation.proceed(); print "After the invocation of the method '" + method_name + "'";
The second variation of the interceptor that we want to attach to the printer service is the security interceptor which provides authorization support to the printer service by only allowing certain modules to use the Printer service. For example, the below implementation checks for the caller name, if the caller is an admin-module, then it continues with the method proceeding, else the service methods won’t be invoked at all.
from springpython.aop import * class SecurityInteceptor(MethodInterceptor): caller_name = "not-initialized"; def invoke(self, invocation): if (self.caller_name == "admin-module"): invocation.proceed();
Here is the client code that makes use of the interceptors. The concepts for defining interceptors for Python applications are borrowed from the Core spring framework. It defines a proxy factory and sets the target of the proxy factory bean to the real service object. It then updates the interceptors list in the proxy factory bean to add the customized interceptor implementation of logging and security.
from springpython.aop import * from printer import PrinterService from log import LogInterceptor from security import SecurityInteceptor proxyFactory = ProxyFactory(); printerService = PrinterService(); proxyFactory.target = printerService; loggerInterceptor = LogInterceptor() proxyFactory.interceptors.append(loggerInterceptor); securityInterceptor = SecurityInteceptor(); securityInterceptor.caller_name = "admin-module"; proxyFactory.interceptors.append(securityInterceptor); printerServiceProxy = proxyFactory.getProxy() printerServiceProxy.startPrinting("test.txt"); printerServiceProxy.stopPrinting("test.txt");
The client will be given a proxy implementation of the Printer service object, which can be acquired by calling getProxy() method defined on the ProxyFactoryBean. Now, whenever the service methods startPrinting() and stopPrinting() are invoked, the calls will be intercepted for the execution of the customized interceptors and after that the real method invocations will be made to proceed.
In the final section of the article, we will introduce Remoting capabilities for Python applications. Remoting essentially defines the availability of writing services that is deployed in a remote machine and the client capabilities for the remote object retrieval for its usage. In this section, we will write a simple server object, export it to a remote object and then write a sample client which will use this object.
Here is the code listing for a simple service which when given a stock symbol will return the organization name. Note, because it is a simple service we have hard-coded few symbol names for testing purpose.
class StockNameFetcher: def fetchFor(self, symbol): if (symbol == "MSFT"): return "Microsoft Corp"; elif (symbol == "ACN"): return "Accenture Ltd"; elif (symbol == "AMZN"): return "Amazon"; return "Unknown";
Exporting as service
In this section, we will see how to make use of PyroServiceExporter for exporting the stock name fetcher service so that it can be remotely accessible to a client.
from springpython.remoting.pyro import PyroServiceExporter from stock import StockNameFetcher exporter = PyroServiceExporter(); exporter.service_name = "organizationNameFetcherService"; fetcher = StockNameFetcher(); exporter.service = fetcher; exporter.service_host = "127.0.0.1"; exporter.service_port = 3333; proxyFactory = PyroProxyFactory(); proxyFactory.service_url = "PYROLOC://127.0.0.1:3333/organizationNameFetcherService";
In the above code, we have defined an instance of PyroServiceExporter object, set the required attributes such as the name of the service, the service instance to be exported, host name and the port number of the service etc. Finally, we have made use of PyroProxyFactory for providing a proxy reference to the running service object in the URL mentioned in ‘service_url’.
The client code is pretty much simple and it uses the same PyroProxyFactory object for looking up and getting a proxied implementation of the service. Finally it calls the service method for getting the name of the organization for the given symbol.
from springpython.remoting.pyro import PyroProxyFactory proxyService = PyroProxyFactory() proxyService.service_url = "PYROLOC://127.0.0.1:3333/organizationNameFetcherService" print proxyService.fetchFor("ABC") print proxyService.fetchFor("MSFT")
This article provided a step-by-step approach for learning and exploring the various aspects of Spring Python framework. Initially it introduced the various language constructs related to Python. Then it proceeded with incorporating the extended features of Core Spring like IOC, Dependency Injection, AOP, Remoting etc with plenty of code samples.