Getting Started with Spring Python

0
129
11 min read

 

Spring Python 1.1

Spring Python 1.1

Create powerful and versatile Spring Python applications using pragmatic libraries and useful abstractions

 

  • Maximize the use of Spring features in Python and develop impressive Spring Python applications
  • Explore the versatility of Spring Python by integrating it with frameworks, libraries, and tools
  • Discover the non-intrusive Spring way of wiring together Python components
  • Packed with hands-on-examples, case studies, and clear explanations for better understanding 

        Read more about this book      

(For more resources on this subject, see here.)

Spring Python for Python developers

You have already picked one of the most popular and technically powerful dynamic languages to develop software, Python. Spring Python makes it even easier to solve common problems encountered by Python developers every day.

Exploring Spring Python’s non-invasive nature

Spring Python has a non-invasive nature, which means it is easy to adopt the parts that meet your needs, without rewriting huge blocks of code. For example, Pyro (http://pyro.sourceforge.net) is a 3rd party library that provides an easy way to make remote procedure calls.

In order to demonstrate the Spring way of non-invasiveness, let’s code a simple service, publish it as a web service using Pyro’s API, and then publish it using Spring Python’s wrapper around Pyro. This will show the difference in how Spring Python simplifies API access for us, and how it makes the 3rd party library easier to use without as much rework to our own code.

  1. First, let’s write a simple service that parses out the parameters from a web request string:

    class ParamParser(object): def parse_web_parms(self, parm): return [tuple(p.split("=")) for p in parm.split("&")]

  2. Now we can write a simple, functional piece of code that uses our service in order to have a working version.

    parser = ParamParser() parser.parse_web_parms("pages=5&article=Spring_Python")

    This is just instantiating the ParamParser and accessing the function. To make this a useful internet service, it needs to be instantiated on a central server and should be configured to listen for calls from clients.

  3. The next step is to advertise it as a web service using the API of Pyro. This will make it reachable by multiple Pyro clients. To do this, we define a daemon which will host our service on port 9000 and initialize it.

    daemon = Pyro.core.Daemon(host="localhost", port="9000") Pyro.core.initServer()

  4. Next, we create a Pyro object instance to act as proxy to our service as well as an instance of our ParamParser. We configure the proxy to delegate all method calls to our service.

    pyro_proxy = Pyro.core.ObjBase() parser = ParamParser() pyro_proxy.delegateTo(parser)

  5. Finally, we register the pyro_proxy object with the daemon, and startup a listen-dispatch loop so that it’s ready to handle requests:

    daemon.connect(pyro_proxy, "mywebservice") daemon.requestLoop(True)

When we run this server code, an instance of our ParamParser will be created and advertised at PYROLOC://localhost:9000/mywebservice.

To make this service complete, we need to create a Pyro client that will call into our service. The proxy seamlessly transfers Python objects over the wire using the Pyro library, in this case the tuple of request parameters.

url_base = "PYROLOC://localhost:9000" client_proxy = Pyro.core.getProxyForURI( url_base + "/mywebservice") print client_proxy.parse_web_parms( "pages=5&article=Spring_Python")

The Pyro library is easy to use. One key factor is how our ParamParser never gets tightly coupled to the Pyro machinery used to serve it to remote clients. However, it’s very invasive.

What if we had already developed a simple application on a single machine with lots of methods making use of our utility? In order to convert our application into a client-server application, we would have to rewrite it to use the Pyro client proxy pattern everywhere that it was called. If we miss any instances, we will have bugs that need to be cleaned up. If we had written automated tests, they would also have to be rewritten as well. Converting a simple, one-machine application into a multi-node application can quickly generate a lot of work.

That is where Spring Python comes in. It provides a different way of creating objects which makes it easy for us to replace a local object with a remoting mechanism such as Pyro.

Let’s utilize Spring Python’s container to create our parser and also to serve it up with Pyro.

from springpython.config import PythonConfig from springpython.config import Object from springpython.remoting.pyro import PyroServiceExporter from springpython.remoting.pyro import PyroProxyFactory class WebServiceContainer(PythonConfig): def __init__(self): super(WebServiceContainer, self).__init__() @Object(lazy_init=True) def my_web_server(self): return PyroServiceExporter(service=ParamParser(), service_name="mywebservice", service_port=9000) @Object(lazy_init=True) def my_web_client(self): myService = PyroProxyFactory() myService.service_url="PYROLOC://localhost:9000/mywebservice" return myService

With this container definition, it is easy to write both a server application as well as a client application. To spin up one instance of our Pyro server, we use the following code:

from springpython.context import ApplicationContext container = ApplicationContext(WebServiceContainer()) container.get_object("my_web_server")

The client application looks very similar.

from springpython.context import ApplicationContext container = ApplicationContext(WebServiceContainer()) myService = container.get_object("my_web_client") myService.parse_web_parms("pages=5&article=Spring_Python")

The Spring Python container works by containing all the definitions for creating key objects. We create an instance of the container, ask it for a specific object, and then use it.

This easily looks like just as much (if not more) code than using the Pyro API directly. So why is it considered less invasive?

Looking at the last block of code, we can see that we are no longer creating the parser or the Pyro proxy. Instead, we are relying on the container to create it for us. The Spring Python container decouples the creation of our parser, whether its for a local application, or if it uses Pyro to join them remotely. The server application doesn’t know that it is being exported as a Pyro service, because all that information is stored in the WebServiceContainer. Any changes made to the container definition aren’t seen by the server application code.

The same can be said for the client. By putting creation of the client inside the container, we don’t have to know whether we are getting an instance of our service or a proxy. This means that additional changes can be made inside the definition of the container of Spring Python, without impacting our client and server apps. This makes it easy to split the server and client calls into separate scripts to be run in separate instances of Python or on separate nodes in our enterprise.

This demonstrates how it is possible to mix in remoting to our existing application. By using this pattern of delegating creation of key objects to the container, it is easy to start with simple object creation, and then layer on useful services such as remoting. Later in this book, we will also see how this makes it easy to add other services like transactions and security. Due to Spring Python’s open ended design, we can easily create new services and add them on without having to alter the original framework.

Adding in some useful templates

In addition to the non-invasive ability to mix in services, Spring Python has several utilities that ease the usage of low level APIs through a template pattern. The template pattern involves capturing a logical flow of steps. What occurs at each step is customizable by the developer, while still maintaining the same overall sequence.

One example where a template would be useful is for writing a SQL query. Coding SQL queries by hand using Python’s database API (http://www.python.org/dev/peps/pep-0249) is very tedious. We must properly handle errors and harvest the results. The extra code involved with connecting things together and handling issues is commonly referred to as plumbing code. Let’s look at the following code to see how Python’s database API functions.

The more plumbing code we have to maintain, the higher the cost. Having an application with dozens or hundreds of queries can become unwieldy, even cost prohibitive to maintain.

  1. Using Python’s database API, we only have to write the following code once for setup.

    ### One time setup import MySQLdb conn = MySQLdb.connection(username="me", password"secret", hostname="localhost", db="springpython")

  2. Now let’s use Python’s database API to perform a single query.

    ### Repeated for every query cursor = conn.cursor() results = [] try: cursor.execute("""select title, air_date, episode_number, writer from tv_shows where name = %s""", ("Monty Python",)) for row in cursor.fetchall(): tvShow = TvShow(title=row[0], airDate=row[1], episodeNumber=row[2], writer=row[3]) results.append(tvShow) finally: try: cursor.close() except Exception: pass conn.close() return results

    The specialized code we wrote to look up TV shows is contained in the execute statement and also the part that creates an instance of TvShow. The rest is just plumbing code needed to handle errors, manage the database cursor, and iterate over the results.

    This may not look like much, but have you ever developed an application with just one SQL query? We could have dozens or even hundreds of queries, and having to repeatedly code these steps can become overwhelming. Spring Python’s DatabaseTemplate lets us just inject the query string and and a row mapper to reduce the total amount of code that we need to write.

  3. We need a slightly different setup than before.

    """One time setup""" from springpython.database.core import * from springpython.database.factory import * connectionFactory = MySQLConnectionFactory(username="me", password="secret", hostname="localhost", db="springpython")

  4. We also need to define a mapping to generate our TvShow objects.

    class TvShowMapper(RowMapper): def map_row(self, row, metadata=None): return TvShow(title=row[0], airDate=row[1], episodeNumber=row[2], writer=row[3])

  5. With all this setup, we can now create an instance of DatabaseTemplate and use it to execute the same query with a much lower footprint.

    dt = DatabaseTemplate(connectionFactory) """Repeated for each query""" results = dt.query("""select title, air_date, episode_number, writer from tv_shows where name = %s""", ("Monty Python",), TvShowMapper())

This example shows how we can replace 19 lines of code with a single statement using Spring Python’s template solution.

Object Relational Mappers (ORMs) have sprung up in response to the low level nature of ANSI SQL’s protocol. Many applications have simple object persistence requirements and many of us would prefer working on code, and not database design. By having a tool to help do the schema management work, these ORMs have been a great productivity boost.

But they are not necessarily the answer for every use case. Some queries are very complex and involve looking up information spread between many tables, or involve making complex calculations and involve decoding specific values. Also, many legacy systems are denormalized and don’t fit the paradigm that ORMs were originally designed to handle. The complexity of these queries can require working around, or even against, the ORM-based solutions, making them not worth the effort.

To alleviate the frustration of working with SQL, Spring Python’s DatabaseTemplate greatly simplifies writing SQL, while giving you complete freedom in mapping the results into objects, dictionaries, and tuples. DatabaseTemplate can easily augment your application, whether or not you are already using an ORM. That way, simple object persistence can be managed with ORM, while complex queries can be handed over to Spring Python’s DatabaseTemplate, resulting in a nice blend of productive, functional code.

Other templates, such as TransactionTemplate, relieve you of the burden of dealing with the low level idioms needed to code transactions that makes them challenging to incorporate correctly. Later in this book, we will learn how easy it is to add transactions to our code both programmatically and declaratively.

Applying the services you need and abstracting away low level APIs is a key part of the Spring way and lets us focus our time and effort on our customer’s business requirements instead of our own technical ones.

By using the various components we just looked at, it isn’t too hard to develop a simple Pyro service that serves up TV shows from a relational database.

from springpython.database.factory import * from springpython.config import * from springpython.remoting.pyro import * class TvShowMapper(RowMapper): def map_row(self, row, metadata=None): return (title=row[0], airDate=row[1], episodeNumber=row[2], writer=row[3]) class TvShowService(object): def __init__(self): self.connFactory = MySQLConnectionFactory(username="me", password="secret", hostname="localhost", db="springpython") self.dt = DatabaseTemplate(connFactory) def get_tv_shows(self): return dt.query("""select title, air_date, episode_number, writer from tv_shows where name = %s""", ("Monty Python",), TvShowMapper()) class TvShowContainer(PythonConfig): def __init__(self): super(TvShowContainer, self).__init__() @Object(lazy_init=True) def web_server(self): return PyroServiceExporter(service=TvShowService(), service_name="tvshows", service_port=9000) @Object(lazy_init=True) def web_client(self): myService = PyroProxyFactory() myService.service_url="PYROLOC://localhost:9000/tvshows" return myService if __name__ == "__main__": container = ApplicationContext(TvShowContainer()) container.get_object("web_server")

By querying the database for TV shows and serving it up through Pyro, this block of code demonstrates how easy it is to use these powerful modules without mixing them together. It is much easier to maintain software over time when things are kept simple and separated.

We just took a quick walk through SQL and Pyro and examined their low level APIs. Many low level APIs require a certain sequence of steps to properly utilize them. We just looked at a SQL query. The need for templates also exists with database transactions and LDAP calls. By capturing the flow of the API in a Spring Python template and allowing you to insert your custom code, you can get out of writing plumbing code and instead work on your application’s business logic.

LEAVE A REPLY

Please enter your comment!
Please enter your name here