Spring Python, Pyro and remote services
One of the Spring Python‘s features is a wrapper around Pyro, a Python Remote
Objects library which makes it very easy to expose Python applications as network services
running across distributed systems. You write ordinary Python code that is unaware
of any networking and Spring Python lets you publish and invoke it through
its PyroServiceExporter and PyroProxyFactory objects. The setup uses a binary
pickle protocol underneath so it’s only available if both sides, both client and the
server, are written in Python.
Here’s how the service’s code for returning current system’s uptime might look
like. As you can see, there’s nothing Spring Python going on there and in fact
it’s just a simple class with just one method which knows nothing about Spring
Python nor is it aware that it’s going to be available through any network.
Staying out of your way and letting you reuse an already existing code is one
of the biggest advantages of Spring Python, sure, you need to learn its API
and principles but you can still simply code in Python and not be afraid of
getting caught in some artificial maze of interfaces and dependencies.
# stdlib import logging import commands # Configure logging subsystem logging.basicConfig(level=logging.DEBUG) class Service(object): def get_uptime(self): logging.info("get_uptime invoked") return commands.getoutput("uptime") # Create a service .. service = Service() # .. get the uptime .. uptime = service.get_uptime() # and print it on the console. logging.info("Current uptime: [%s]" % uptime)
And here’s the Spring Python’s approach to exposing the service through Pyro
protocol, new bits have been highlighted in green. PyroServiceExporter is an
object which grabs a service, lets you set its name and let it run on a given
host and port. Note that the service’s code hasn’t been changed at all.
# stdlib import logging import commands # Spring Python from springpython.remoting.pyro import PyroServiceExporter # Configure logging subsystem logging.basicConfig(level=logging.DEBUG) class Service(object): def get_uptime(self): logging.info("get_uptime invoked") return commands.getoutput("uptime") # Create a service .. service = Service() # .. export it through Pyro .. service_exporter = PyroServiceExporter() service_exporter.service = service service_exporter.service_name = "Service" service_exporter.service_host = "127.0.0.1" service_exporter.service_port = "16099" # .. and start the server. service_exporter.after_properties_set()
Such a service needs a client now and here’s the code that invokes the server
looks like, key parts are in green again. PyroProxyFactory needs a server’s
location and then invoking a server is only a matter of invoking a method of
an object, it can’t be easier than that
# stdlib import logging # Spring Python from springpython.remoting.pyro import PyroProxyFactory # Configure logging subsystem logging.basicConfig(level=logging.DEBUG) # Point the client to a server .. service = PyroProxyFactory() service.service_url = "PYROLOC://127.0.0.1:16099/Service" # .. invoke the service .. uptime = service.get_uptime() # and print it on the console. logging.info("Current uptime: [%s]" % uptime)
Of course there’s still room for improvement. Firstly, there’s no authorization
of whatsoever, everyone can invoke the service. Secondly, the configuration of
both parties is written directly in the source code. It would be much better if
it all were externalized to some other place and that’s exactly what the next
installment will be about, we’ll be using Spring Python’s Inversion of Control
features to decouple the actual implementation from its configuration.
Update: here is the second part which shows Spring Python’s IoC in action.