File: soaplib_docs.markdown

package info (click to toggle)
python-soaplib 0.8.1-2
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd, squeeze, wheezy
  • size: 332 kB
  • ctags: 517
  • sloc: python: 3,453; makefile: 2
file content (452 lines) | stat: -rw-r--r-- 13,901 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
Soaplib Documentation
=====================

Table of Contents
-----------------
1. Overview
	* What is soaplib?
	* Requirements
2. Intro Examples
	* HelloWorld
	* UserManager
3. Serializers
	* Primitive
	* Collections
	* Class
	* Fault
	* Binary
	* Any
	* Custom
4. Client
5. Message API
6. WSGI
	* Deployment
	* Hooks
	* servers
7. Async. Web Services
	* Correlation
8. Interoperability
	* Axis
	* .NET
9. wsdl2py
10. security
	* none
		
Overview
========

What is soaplib?
----------------

Soaplib is an easy to use python library written at 
[Optio Software, Inc.](http://www.optio.com/)
for writing and calling soap web services.	Writing soap web services in
any language has always been extremely difficult and yielded mixed
results.  With a very small amount of code, soaplib allows you to write
a useful web service and deploy it as a WSGI application.  WSGI is a python
web standard for writing portable, extendable web applications in python.
More information on WSGI can be found [here](http://wsgi.org/wsgi).

Features
--------
* deploy services as WSGI applications
* handles all xml (de)serialization
* on-demand WSDL generation
* doesn't get in your way!!!

Requirements
------------
* Python 2.4 or greater (tested mainly on 2.4.3)
* [ElementTree](http://effbot.org/downloads/elementtree-1.2.6-20050316.tar.gz) (available through easy_install)
* [cElementTree](http://effbot.org/downloads/cElementTree-1.0.5-20051216.tar.gz) (available through easy_install)
* a WSGI-compliant web server (CherryPy, WSGIUtils, Flup, etc.)
* [pytz](http://pytz.sourceforge.net/)(available through easy_install)
* [easy_install](http://peak.telecommunity.com/dist/ez_setup.py) (optional)
		
Intro Examples
==============
	
HelloWorld	
----------

1. Declaring a Soaplib Service

		from soaplib.wsgi_soap import SimpleWSGISoapApp
		from soaplib.service import soapmethod
		from soaplib.serializers.primitive import String, Integer, Array
	
		class HelloWorldService(SimpleWSGISoapApp):
			@soapmethod(String,Integer,_returns=Array(String))
			def say_hello(self,name,times):
				results = []
					for i in range(0,times):
						results.append('Hello, %s'%name)
					return results
			
		if __name__=='__main__':
			from wsgiref.simple_server import make_server
			server = make_server('localhost', 7789, HelloWorldService())
			server.serve_forever()
	
	Dissecting this example:
	
	`SimpleWSGISoapApp` is the base class for WSGI soap services.
	
		from soaplib.wsgi_soap import SimpleWSGISoapApp
	
	The `soapmethod` decorator exposes methods as soap method and declares the
	data types it accepts and returns
	
		from soaplib.service import soapmethod
	
	Import the serializers to for this method (more on serializers later)
	
		from soaplib.serializers.primitive import String, Integer, Array
	
	Extending `SimpleWSGISoapApp` is an easy way to soap service that can
	be deployed as a WSGI application.
			
		class HelloWorldService(SimpleWSGISoapApp):

	The `soapmethod` decorator flags each method as a soap method, 
	and defines the types and order of the soap parameters, as well
	as the return value.  This method takes in a `String`, an `Integer`
	and returns an Array of Strings -> `Array(String)`. 
		
		@soapmethod(String,Integer,_returns=Array(String))

	The method itself has nothing special about it whatsoever. All
	input variables and return types are standard python objects.
	
		def say_hello(self,name,times):
			results = []
				for i in range(0,times):
					results.append('Hello, %s'%name)
				return results
		
2. Deploying the service		

	deploy this web service.  Soaplib has been tested with several other web servers, 
	This example uses the reference WSGI web server (available in Python 2.5+) to 
	and any WSGI-compliant server *should* work.
	
		if __name__=='__main__':
			from wsgiref.simple_server import make_server
			server = make_server('localhost', 7789, HelloWorldService())
			server.serve_forever()

3. Calling this service
	
		>>> from soaplib.client import make_service_client
		>>> client = make_service_client('http://localhost:7789/',HelloWorldService())
		>>> print client.say_hello("Dave",5)
		['Hello, Dave','Hello, Dave','Hello, Dave','Hello, Dave','Hello, Dave']
		
	`soaplib.client.make_service_client` is a utility method to construct a callable 
	client to the remote web service.  `make_service_client` takes the url of the
	remote functionality, as well as a _stub_ of the remote service.  As in this case,
	the _stub_ can be the instance of the remote functionality, however the requirements
	are that it just have the same method signatures and definitions as the server 
	implementation. 

User Manager
------------

Lets try a more complicated example than just strings and integers!	 The following is
an extremely simple example using complex, nested data.

	from soaplib.wsgi_soap import SimpleWSGISoapApp
	from soaplib.service import soapmethod
	from soaplib.serializers.primitive import String, Integer, Array
	from soaplib.serializers.clazz import ClassSerializer

	user_database = {}
	userid_seq = 1

	class Permission(ClassSerializer):
		class types:
			application = String
			feature = String

	class User(ClassSerializer):
		class types:
			userid = Integer
			username = String
			firstname = String
			lastname = String
			permissions = Array(Permission)

	class UserManager(SimpleWSGISoapApp):

		@soapmethod(User,_returns=Integer)
		def add_user(self,user):
			global user_database
			global userid_seq
			user.userid = userid_seq
			userid_seq = user_seq+1
			user_database[user.userid] = user
			return user.userid

		@soapmethod(Integer,_returns=User)
		def get_user(self,userid):
			global user_database
			return user_database[userid]

		@soapmethod(User)
		def modify_user(self,user):
			global user_database
			user_database[user.userid] = user

		@soapmethod(Integer)
		def delete_user(self,userid):
			global user_database
			del user_database[userid]

		@soapmethod(_returns=Array(User))
		def list_users(self):
			global user_database
			return [v for k,v in user_database.items()]

	if __name__=='__main__':
		from wsgiref.simple_server import make_server
		server = make_server('localhost', 7789, UserManager())
		server.start()

Jumping into what's new:

	class Permission(ClassSerializer):
		class types:
			application = String
			feature = String

	class User(ClassSerializer):
		class types:
			userid = Integer
			username = String
			firstname = String
			lastname = String
			permissions = Array(Permission)

The `Permission` and `User` structures in the example are standard python objects
that extend `ClassSerializer`.	The `ClassSerializer` uses an innerclass called
`types` to declare the attributes of this class.  At instantiation time, a 
metaclass is used to inspect the `types` and assigns the value of `None` to
each attribute of the `types` class to the new object.

	>>> u = User()
	>>> u.username = 'jimbob'
	>>> print u.userid
	None
	>>> u.firstname = 'jim'
	>>> print u.firstname
	jim
	>>> 

Serializers
===========
In soaplib, the serializers are the components responsible for converting indivdual
parameters to and from xml, as well as supply the information necessary to build the
wsdl.  Soaplib has many built-in serializers that give you most of the common datatypes
generally needed.

Primitives
----------
The basic primitive types are `String`, `Integer`, `DateTime`, `Null`, `Float`, `Boolean`.	These are some
of the most basic blocks within soaplib.  

	>>> from soaplib.serializers.primitive import *		   
	>>> import cElementTree as et
	>>> element = String.to_xml('abcd','nodename')
	>>> print et.tostring(element)
	<nodename xsi:type="xs:string">abcd</nodename>
	>>> print String.from_xml(element)
	abcd
	>>> String.get_datatype()
	'string'
	>>> String.get_datatype(nsmap)
	'xs:string'
	>>> 


Collections
-----------
The two collections available in soaplib are `Array`s and `Map`s.  Unlike the primitive 
serializers, both of these serializers need to be instantiated with the proper internal 
type so it can properly (de)serialize the data.	 All `Array`s and `Map`s are homogeneous, 
meaning that the data they hold are all of the same type.  For mixed typing or more dynamic
data, use the `Any` type.

	>>> from soaplib.serializers.primitive import *
	>>> import cElementTree as et
	>>> array_serializer = Array(String)
	>>> element = array_serializer.to_xml(['a','b','c','d'])
	>>> print et.tostring(element)
	<xsd:retval SOAP-ENC:arrayType="xs:string[4]"><string xsi:type="xs:string">a</string><string xsi:type="xs:string">b</string><string xsi:type="xs:string">c</string><string xsi:type="xs:string">d</string></xsd:retval>
	>>> print array_serializer.from_xml(element)
	['a', 'b', 'c', 'd']
	>>>	  

Class
-----
The `ClassSerializer` is used to define and serialize complex, nested structures.

	>>> from soaplib.serializers.primitive import *	   
	>>> import cElementTree as et
	>>> from soaplib.serializers.clazz import *
	>>> class Permission(ClassSerializer):
	...		class types:
	...			application = String
	...			feature = String
	>>>
	>>> class User(ClassSerializer):
	...		class types:
	...			userid = Integer
	...			username = String
	...			firstname = String
	...			lastname = String... 
	...			permissions = Array(Permission)
	>>> 
	>>> u = User()
	>>> u.username = 'bill'
	>>> u.permissions = [] 
	>>> p = Permission()			
	>>> p.application = 'email'
	>>> p.feature = 'send'
	>>> u.permissions.append(p)
	>>> element = User.to_xml(u)
	>>> et.tostring(element)
	'<xsd:retval><username xsi:type="xs:string">bill</username><lastname xsi:null="1" /><userid xsi:null="1" /><firstname xsi:null="1" /><permissions SOAP-ENC:arrayType="typens:Permission[1]"><Permission><application xsi:type="xs:string">email</application><feature xsi:type="xs:string">send</feature></Permission></permissions></xsd:retval>'
	>>> User.from_xml(element).username
	'bill'
	>>>

Attachment
----------
The `Attachment` serializer is used for transmitting binary data as base64 encoded strings.
Data in `Attachment` objects can be loaded manually, or read from file.	 All encoding of
the binary data is done just prior to the data being sent, and decoding immediately upon
receipt of the `Attachment`.

	>>> from soaplib.serializers.binary import Attachment
	>>> import cElementTree as et
	>>> a = Attachment(data='my binary data')
	>>> element = Attachment.to_xml(a)
	>>> print et.tostring(element)
	<xsd:retval>bXkgYmluYXJ5IGRhdGE=
	</xsd:retval>
	>>> print Attachment.from_xml(element)
	<soaplib.serializers.binary.Attachment object at 0x5c6d90>
	>>> print Attachment.from_xml(element).data
	my binary data
	>>> a2 = Attachment(fileName='test.data') # load from file


Any
---
The `Any` type is a serializer used to transmit unstructured xml data.	`Any` types are very
useful for handling dynamic data, and provides a very pythonic way for passing data using
soaplib.  The `Any` serializer does not perform any useful task because the data passed in
and returned are `Element` objects.	 The `Any` type's main purpose is to declare its presence
in the `wsdl`.


Custom
------
Soaplib provides a very simple interface for writing custom serializers.  Any object conforming
to the following interface can be used as a soaplib serializer.

	class MySerializer:

		def to_xml(self,value,name='retval',nsmap=None):
			pass
			
		def from_xml(self,element):
			pass

		def get_datatype(self,nsmap=None):
			pass
			
		def get_namespace_id(self):
			pass

		def add_to_schema(self,added_params,nsmap):
			pass

This feature is particularly useful when adapting soaplib to an existing project and converting existing
object to `ClassSerializers` is impractical.

Client
======
Soaplib provides a simple soap client to call remote soap implementations.	Using the `ServiceClient` object
is the simplest way to make soap client requests.  The `ServiceClient` uses an example or stub 
implementation to know how to properly construct the soap messages.

	>>> from soaplib.client import make_service_client
	>>> client = make_service_client('http://localhost:7789/',HelloWorldService())
	>>> print client.say_hello("Dave",5)

This method provides the most straightforward method of creating a SOAP client using soaplib.

Message API
===========
TODO

WSGI
====
All soaplib services can be deployed as WSGI applications, and this gives soaplib great flexibility
with how they can be deployed.	Any WSGI middleware layer can be put between the WSGI webserver and
the WSGI soap application.

Deployment
----------
Soaplib has been extensively used with [Paste](http://www.pythonpaste.org) for server configuration
and application composition.  

Hooks
-----
`WSGISoapApp`s have a set of extensible 'hooks' that can be implemented to capture different
events in the execution of the wsgi request.

	def onCall(self,environ):
		'''This is the first method called when this WSGI app is invoked'''
		pass

	def onWsdl(self,environ,wsdl):
		'''This is called when a wsdl is requested'''
		pass

	def onWsdlException(self,environ,exc,resp):
		'''Called when an exception occurs durring wsdl generation'''
		pass

	def onMethodExec(self,environ,body,py_params,soap_params):
		'''Called BEFORE the service implementing the functionality is called'''
		pass

	def onResults(self,environ,py_results,soap_results):
		'''Called AFTER the service implementing the functionality is called'''
		pass

	def onException(self,environ,exc,resp):
		'''Called when an error occurs durring execution'''
		pass

	def onReturn(self,environ,returnString):
		'''Called before the application returns'''
		pass

These hooks are useful for transaction handling, logging and measuring performance.

Servers
-------

Soaplib services can be deployed as WSGI applications, in any WSGI-compliant
web server.	 Soaplib services have been successfully run on the following web
servers:

* CherryPy 2.2
* Flup
* Twisted.web2
* WSGIUtils 0.9