.. _manual-helloworld:
Hello World
===========
This example uses the stock simple wsgi webserver to deploy this service. You
should probably use a full-fledged server when deploying your service for
production purposes.
Defining an Rpclib Service
--------------------------
Here we introduce the fundamental mechanisms the rpclib offers to expose your
services.
The simpler version of this example is available here: http://github.com/arskom/rpclib/blob/master/examples/helloworld_soap.py
::
import logging
from rpclib.application import Application
from rpclib.decorator import srpc
from rpclib.interface.wsdl import Wsdl11
from rpclib.protocol.soap import Soap11
from rpclib.service import ServiceBase
from rpclib.model.complex import Iterable
from rpclib.model.primitive import Integer
from rpclib.model.primitive import String
from rpclib.server.wsgi import WsgiApplication
class HelloWorldService(ServiceBase):
@srpc(String, Integer, _returns=Iterable(String))
def say_hello(name, times):
'''
Docstrings for service methods appear as documentation in the wsdl
what fun
@param name the name to say hello to
@param the number of times to say hello
@return the completed array
'''
for i in xrange(times):
yield 'Hello, %s' % name
if __name__=='__main__':
try:
from wsgiref.simple_server import make_server
except ImportError:
print "Error: example server code requires Python >= 2.5"
logging.basicConfig(level=logging.DEBUG)
logging.getLogger('rpclib.protocol.xml').setLevel(logging.DEBUG)
application = Application([HelloWorldService], 'rpclib.examples.hello.soap',
interface=Wsdl11(), in_protocol=Soap11(), out_protocol=Soap11())
server = make_server('127.0.0.1', 7789, WsgiApplication(application))
print "listening to http://127.0.0.1:7789"
print "wsdl is at: http://localhost:7789/?wsdl"
server.serve_forever()
Dissecting this example: Application is the glue between one or more service definitions,
interface and protocol choices. ::
from rpclib.application import Application
The srpc decorator exposes methods as remote procedure calls and declares the
data types it accepts and returns. The 's' prefix is short for static. It means
no implicit argument will be passed to the function. In the @rpc case, the
function gets a rpclib.MethodContext instance as first argument. ::
from rpclib.decorator import srpc
We are going to expose the service definitions using the Wsdl 1.1 document
standard. The methods will use Soap 1.1 protocol to communicate with the outside
world. They're instantiated and passed to the Application constructor. You need
to pass fresh instances to each application instance. ::
from rpclib.interface.wsdl import Wsdl11
from rpclib.protocol.soap import Soap11
For the sake of this tutorial, we are going to use HttpRpc as well. It's a
rest-like protocol, but it doesn't care about HTTP verbs (yet). ::
from rpclib.protocol.http import HttpRpc
The HttpRpc serializer does not support complex types. So we will use the
XmlObject serializer as the out_protocol to prevent the clients from dealing
with Soap cruft. ::
from rpclib.protocol.http import XmlObject
ServiceBase is the base class for all service definitions. ::
from rpclib.service import ServiceBase
The names of the needed types for implementing this service should be
self-explanatory. ::
from rpclib.model.complex import Iterable
from rpclib.model.primitive import Integer
from rpclib.model.primitive import String
Our server is going to use HTTP as transport, so we import the WsgiApplication
from the server.wsgi module. It's going to wrap the application instance. ::
from rpclib.server.wsgi import WsgiApplication
We start by defining our service. The class name will be made public in the
wsdl document unless explicitly overridden with `__service_name__` class
attribute. ::
class HelloWorldService(ServiceBase):
The srpc decorator flags each method as a remote procedure call and defines the
types and order of the soap parameters, as well as the type of the return value.
This method takes in a string and an integer and returns an iterable of strings,
just like that: ::
@srpc(String, Integer, _returns=Iterable(String))
The method itself has nothing special about it whatsoever. All input variables
and return types are standard python objects::
def say_hello(name, times):
for i in xrange(times):
yield 'Hello, %s' % name
When returning an iterable, you can use any type of python iterable. Here, we
chose to use generators.
Deploying the service using SOAP
--------------------------------
Now that we have defined our service, we are ready to share it with the outside
world.
We are going to use the ubiquitious Http protocol as a transport, using a
Wsgi-compliant http server. This example uses Python's stock simple wsgi web
server. Rpclib has been tested with several other web servers. Any
WSGI-compliant server should work.
This is the required import: ::
if __name__=='__main__':
from wsgiref.simple_server import make_server
Here, we configure the python logger to show debugging output. We have to
specifically enable the debug output from the soap handler. That's because the
xml formatting code is run only when explicitly enabled for performance
reasons. ::
logging.basicConfig(level=logging.DEBUG)
logging.getLogger('rpclib.protocol.xml').setLevel(logging.DEBUG)
We glue the service definition, interface document and input and output protocols
under the targetNamespace 'rpclib.examples.hello.soap': ::
application = Application([HelloWorldService], 'rpclib.examples.hello.soap',
interface=Wsdl11(), in_protocol=Soap11(), out_protocol=Soap11())
We then wrap the rpclib application with its wsgi wrapper: ::
wsgi_app = WsgiApplication(application)
The above two lines can be replaced with an easier-to-use function that covers
this common use case: ::
from rpclib.util.simple import wsgi_soap_application
wsgi_app = wsgi_soap_application([HelloWorldService], 'rpclib.examples.hello.soap')
We now register the WSGI application as the handler to the wsgi server, and run
the http server: ::
server = make_server('127.0.0.1', 7789, wsgi_app)
print "listening to http://127.0.0.1:7789"
print "wsdl is at: http://localhost:7789/?wsdl"
server.serve_forever()
.. NOTE::
* **Django users:** See this gist for a django wrapper example: https://gist.github.com/1242760
* **Twisted users:** See the this example that illustrates deploying an
Rpclib application using twisted: http://github.com/arskom/rpclib/blob/master/examples/helloworld_soap_twisted.py
You can test your service using suds. Suds is a separate project for building
pure-python soap clients. To learn more visit the project's page:
https://fedorahosted.org/suds/. You can simply install it using
``easy_install suds``.
So here's how you can use suds to test your new rpclib service:
::
from suds.client import Client
hello_client = Client('http://localhost:7789/?wsdl')
result = hello_client.service.say_hello("Dave", 5)
print result
The script's output would be as follows: ::
(stringArray){
string[] =
"Hello, Dave",
"Hello, Dave",
"Hello, Dave",
"Hello, Dave",
"Hello, Dave",
}
Deploying service using HttpRpc
-------------------------------
This example is available here: http://github.com/arskom/rpclib/blob/master/examples/helloworld_http.py.
The only difference between the SOAP and the HTTP version is the application
instantiation line: ::
application = Application([HelloWorldService], 'rpclib.examples.hello.http',
interface=Wsdl11(), in_protocol=HttpRpc(), out_protocol=XmlObject())
We still want to keep Xml as the output protocol as the HttpRpc protocol is
not able to handle complex types.
Here's how you can test your service using wget: ::
wget "http://localhost:7789/say_hello?times=5&name=Dave" -qO -
If you have HtmlTidy installed, you can use this command to get a more readable
output. ::
wget "http://localhost:7789/say_hello?times=5&name=Dave" -qO - | tidy -xml -indent
The command's output would be as follows: ::
Hello, Dave
Hello, Dave
Hello, Dave
Hello, Dave
Hello, Dave
What's next?
^^^^^^^^^^^^
See the :ref:`manual-user-manager` tutorial that will walk you through
defining complex objects and using events.