Permalink
Browse files

Merge pull request #173 from plq/master

MessagePack integration.
  • Loading branch information...
2 parents 61a1945 + 66c8300 commit 1fbafe96f60519bce6d5ddb5496dc8b6f5b0736f @ugurcan377 ugurcan377 committed Aug 7, 2012
View
@@ -5,6 +5,8 @@ rpclib-2.8.0-rc -> spyne-2.8.0-rc
---------------------------------
* Rpclib is dead. Long live Spyne!
* Add support for JsonObject protocol. This initial version is expremental.
+ * Add support for MessagePackObject and MessagePackRpc protocols. These
+ initial versions are expremental.
* Make DateTime string format customizable.
* Implement TwistedWebResource that exposes an ``Application`` instance as a
``twisted.web.resource.Resource`` child.
@@ -29,11 +29,11 @@ Discussion thread: https://answers.launchpad.net/ladon/+question/171664
to do with the ugliness of a raw wsdl document.
* Does not support ZeroMQ.
* Uses standard python tools for xml parsing which is good for pure-python
- deployments. Spyne uses lxml, due to its excellent namespace support and
+ deployments. Spyne uses lxml, due to its excellent xml namespace support and
speed. So Spyne-based solutions are easier to develop and faster to work with
but more difficult to deploy.
* Does not do declarative input validation. Traditional (imperative) input
- validation is possible via a code injection mechanism similar to spyne's
+ validation is possible via a code injection mechanism similar to Spyne's
events.
* Does not support HttpRpc.
* Does not have a Soap client.
@@ -32,7 +32,7 @@ The simpler version of this example is available here: http://github.com/arskom/
@srpc(String, Integer, _returns=Iterable(String))
def say_hello(name, times):
'''
- Docstrings for service methods appear as documentation in the wsdl
+ Docstrings for service methods appear as documentation in the wsdl.
<b>what fun</b>
@param name the name to say hello to
@param the number of times to say hello
@@ -73,24 +73,12 @@ function gets a spyne.MethodContext instance as first argument. ::
from spyne.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
+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 spyne.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 spyne.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 spyne.protocol.http import XmlObject
-
ServiceBase is the base class for all service definitions. ::
from spyne.service import ServiceBase
@@ -130,8 +118,8 @@ and return types are standard python objects::
When returning an iterable, you can use any type of python iterable. Here, we
chose to use generators.
-Deploying the service using SOAP
---------------------------------
+Deploying the service using Soap via Wsgi
+-----------------------------------------
Now that we have defined our service, we are ready to share it with the outside
world.
@@ -180,13 +168,12 @@ the http server: ::
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
- Spyne application using twisted: http://github.com/arskom/spyne/blob/master/examples/helloworld_soap_twisted.py
+ * **Django users:** See django wrapper example: https://github.com/arskom/spyne/blob/master/examples/django
+ * **Twisted users:** See the these examples that illustrate two ways of
+ deploying a Spyne application using Twisted: http://github.com/arskom/spyne/blob/master/examples/twisted
-You can test your service using suds. Suds is a separate project for building
+You can test your service using suds. Suds is a separate project for implementing
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``.
@@ -197,55 +184,63 @@ So here's how you can use suds to test your new spyne service:
from suds.client import Client
hello_client = Client('http://localhost:7789/?wsdl')
- result = hello_client.service.say_hello("Dave", 5)
- print result
+ print hello_client.service.say_hello("Punk", 5)
The script's output would be as follows: ::
(stringArray){
string[] =
- "Hello, Dave",
- "Hello, Dave",
- "Hello, Dave",
- "Hello, Dave",
- "Hello, Dave",
+ "Hello, Punk",
+ "Hello, Punk",
+ "Hello, Punk",
+ "Hello, Punk",
+ "Hello, Punk",
}
-Deploying service using HttpRpc
--------------------------------
+Deploying service using HttpRpc via Wsgi
+----------------------------------------
This example is available here: http://github.com/arskom/spyne/blob/master/examples/helloworld_http.py.
-The only difference between the SOAP and the HTTP version is the application
-instantiation line: ::
+
+For the sake of this tutorial, we are going to use HttpRpc as well. HttpRpc is
+a rest-like protocol, but it doesn't care about HTTP verbs (yet). ::
+
+ from spyne.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 spyne.protocol.http import XmlObject
+
+Besides the imports, the only difference between the SOAP and the HTTP version
+is the application instantiation line: ::
application = Application([HelloWorldService], 'spyne.examples.hello.http',
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 curl: ::
- curl "http://localhost:7789/say_hello?times=5&name=Dave"
+ curl "http://localhost:7789/say_hello?times=5&name=Punk"
If you have HtmlTidy installed, you can use this command to get a more readable
output. ::
- curl "http://localhost:7789/say_hello?times=5&name=Dave" | tidy -xml -indent
+ curl "http://localhost:7789/say_hello?times=5&name=Punk" | tidy -xml -indent
The command's output would be as follows: ::
<?xml version='1.0' encoding='utf8'?>
<ns1:say_helloResponse xmlns:ns1="spyne.examples.hello.http"
xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/">
<ns1:say_helloResult>
- <ns1:string>Hello, Dave</ns1:string>
- <ns1:string>Hello, Dave</ns1:string>
- <ns1:string>Hello, Dave</ns1:string>
- <ns1:string>Hello, Dave</ns1:string>
- <ns1:string>Hello, Dave</ns1:string>
+ <ns1:string>Hello, Punk</ns1:string>
+ <ns1:string>Hello, Punk</ns1:string>
+ <ns1:string>Hello, Punk</ns1:string>
+ <ns1:string>Hello, Punk</ns1:string>
+ <ns1:string>Hello, Punk</ns1:string>
</ns1:say_helloResult>
</ns1:say_helloResponse>
@@ -25,7 +25,7 @@ here: http://github.com/arskom/spyne/blob/master/examples/user_manager/server_ba
from spyne.decorator import rpc
from spyne.interface.wsdl import Wsdl11
from spyne.protocol.soap import Soap11
- from spyne.model.primitive import String
+ from spyne.model.primitive import Unicode
from spyne.model.primitive import Integer
from spyne.model.complex import Array
from spyne.model.complex import Iterable
@@ -39,16 +39,16 @@ here: http://github.com/arskom/spyne/blob/master/examples/user_manager/server_ba
class Permission(ComplexModel):
__namespace__ = 'spyne.examples.user_manager'
- application = String
- operation = String
+ application = Unicode
+ operation = Unicode
class User(ComplexModel):
__namespace__ = 'spyne.examples.user_manager'
user_id = Integer
- user_name = String
- first_name = String
- last_name = String
+ user_name = Unicode
+ first_name = Unicode
+ last_name = Unicode
permissions = Array(Permission)
class UserManagerService(ServiceBase):
@@ -108,37 +108,38 @@ here: http://github.com/arskom/spyne/blob/master/examples/user_manager/server_ba
server.serve_forever()
-Juping into what's new: Spyne uses ``ComplexModel`` as a general type that when extended will produce complex
-serializable types that can be used in a public service. The ``Permission`` class is a
-fairly simple class with just two members: ::
+Jumping into what's new: Spyne uses ``ComplexModel`` as a general type that,
+when subclassed, will produce complex serializable types that can be used in a
+public service. The ``Permission`` class is a fairly simple class with just
+two members: ::
class Permission(ComplexModel):
- application = String
- feature = String
+ application = Unicode
+ feature = Unicode
Let's also look at the ``User`` class: ::
class User(ComplexModel):
user_id = Integer
- username = String
- firstname = String
- lastname = String
+ username = Unicode
+ firstname = Unicode
+ lastname = Unicode
Nothing new so far.
Below, you can see that the ``email`` member which has a regular expression
-restriction defined. The String type accepts other restrictions, please refer to
-the :class:`spyne.model.primitive.String` documentation for more information: ::
+restriction defined. The ``Unicode`` type accepts other restrictions, please refer to
+the :class:`spyne.model.primitive.Unicode` documentation for more information: ::
- email = String(pattern=r'\b[a-z0-9._%+-]+@[a-z0-9.-]+\.[A-Z]{2,4}\b')
+ email = Unicode(pattern=r'\b[a-z0-9._%+-]+@[a-z0-9.-]+\.[A-Z]{2,4}\b')
The ``permissions`` attribute is an array, whose native type is a ``list`` of ``Permission``
objects. ::
permissions = Array(Permission)
-The following is deserialized as a generator, but looks the same from the protocol and
-interface points of view: ::
+The following is deserialized as a generator, but looks the same from the
+points of view of protocol and interface documents: ::
permissions = Iterable(Permission)
@@ -148,16 +149,21 @@ representations. ::
permissions = Permission.customize(max_occurs='unbounded')
+With the ``Array`` and ``Iterable`` types, a container class wraps multiple
+occurences of the inner data type.
+
Here, we need to use the :func:`spyne.model._base.ModelBase.customize` call
-because calling a ``ComplexModel`` child instantiates that class, whereas
-calling a ``SimpleModel`` child returns a duplicate of that class. The
-``customize`` function just sets given arguments as class attributes to
+because calling a ``ComplexModel`` subclass instantiates that class, whereas
+calling a ``SimpleModel`` child implicitly calls the ``.customize`` method of
+that class.
+
+The ``customize`` function just sets given arguments as class attributes to
``cls.Attributes`` class. You can refer to the documentation of each class to
see which member of the ``Attributes`` class is used for the given object.
Here, we define a function to be called for every method call. It instantiates
the ``UserDefinedContext`` class and sets it to the context object's ``udc``
-attribute, which is in fact short for 'user defined context'. ::
+attribute, which is in fact short for 'User Defined Context'. ::
def _on_method_call(ctx):
ctx.udc = UserDefinedContext()
@@ -167,11 +173,19 @@ We register it to the application's 'method_call' handler. ::
application.event_manager.add_listener('method_call', _on_method_call)
Note that registering it to the service definition's event manager would have
-the same effect: ::
+the same effect, but it'd have to be set for every service definition: ::
UserManagerService.event_manager.add_listener('method_call', _on_method_call)
-Here, we define the UserDefinedContext object. It's just a regular python class
+You can also prefer to define your own ``ServiceBase`` class and use it as a
+base class throughout your projects: ::
+
+ class MyServiceBase(ServiceBase):
+ pass
+
+ MyServiceBase.event_manager.add_listener('method_call', _on_method_call)
+
+Next, we define the UserDefinedContext object. It's just a regular python class
with no specific api it should adhere to, other than your own. ::
class UserDefinedContext(object):
@@ -195,12 +209,11 @@ events to measure method performance)
What's next?
------------
-This tutorial walks you through what you need to know to expose basic
+This tutorial walks you through what you need to know to expose more complex
services. You can read the :ref:`manual-sqlalchemy` document where the
:class:`spyne.model.table.TableModel` class and its helpers are introduced.
You can also have look at the :ref:`manual-metadata` section where service
metadata management apis are introduced.
Otherwise, please refer to the rest of the documentation or the mailing list
if you have further questions.
-
@@ -31,75 +31,38 @@
import logging
import time
-import sys
from twisted.python import log
-from twisted.web.server import Site
-from twisted.web.static import File
-from twisted.internet import reactor
-from twisted.python import log
from spyne.application import Application
from spyne.decorator import srpc
from spyne.protocol.http import HttpRpc
-from spyne.protocol.json import JsonObject
from spyne.protocol.xml import XmlObject
from spyne.service import ServiceBase
-from spyne.model.complex import Array
from spyne.model.primitive import Integer
-from spyne.model.primitive import String
-from spyne.server.wsgi import WsgiApplication
-from spyne.server.twisted import TwistedWebResource
-from spyne.util.wsgi_wrapper import run_twisted
-
-'''
-This is the HelloWorld example running in a single-process twisted setup.
-'''
host = '127.0.0.1'
port = 9752
class SomeService(ServiceBase):
- @srpc(String, Integer, _returns=Array(String))
- def say_hello(name, times):
- '''Docstrings for service methods appear as documentation in the wsdl.
-
- @param name the name to say hello to
- @param the number of times to say hello
- @return the completed array
- '''
- results = []
- for i in range(0, times):
- results.append('Hello, %s' % name)
-
- return results
-
@srpc(Integer)
def block(seconds):
"""Blocks the reactor for given number of seconds."""
time.sleep(seconds)
-
-if __name__=='__main__':
+def initialize():
logging.basicConfig(level=logging.DEBUG)
logging.getLogger('spyne.protocol.xml').setLevel(logging.DEBUG)
- application = Application([SomeService], 'spyne.examples.hello.twisted',
- in_protocol=HttpRpc(), out_protocol=XmlObject())
-
- application.interface.nsmap[None] = application.interface.nsmap['tns']
- application.interface.prefmap[application.interface.nsmap['tns']] = None
- del application.interface.nsmap['tns']
-
observer = log.PythonLoggingObserver('twisted')
log.startLoggingWithObserver(observer.emit, setStdout=False)
- wr = TwistedWebResource(application)
- site = Site(wr)
- reactor.listenTCP(port, site)
+ application = Application([SomeService], 'spyne.examples.hello.twisted',
+ in_protocol=HttpRpc(), out_protocol=XmlObject())
- logging.info("listening on: %s:%d" % (host,port))
- logging.info('wsdl is at: http://0.0.0.0:7789/?wsdl')
+ application.interface.nsmap[None] = application.interface.nsmap['tns']
+ application.interface.prefmap[application.interface.nsmap['tns']] = None
+ del application.interface.nsmap['tns']
- sys.exit(reactor.run())
+ return application
Oops, something went wrong.

0 comments on commit 1fbafe9

Please sign in to comment.