1#!/usr/bin/env python 2# encoding: utf8 3# 4# Copyright © Burak Arslan <burak at arskom dot com dot tr>, 5# Arskom Ltd. http://www.arskom.com.tr 6# All rights reserved. 7# 8# Redistribution and use in source and binary forms, with or without 9# modification, are permitted provided that the following conditions are met: 10# 11# 1. Redistributions of source code must retain the above copyright notice, 12# this list of conditions and the following disclaimer. 13# 2. Redistributions in binary form must reproduce the above copyright 14# notice, this list of conditions and the following disclaimer in the 15# documentation and/or other materials provided with the distribution. 16# 3. Neither the name of the owner nor the names of its contributors may be 17# used to endorse or promote products derived from this software without 18# specific prior written permission. 19# 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, 24# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 27# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 29# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30# 31 32 33""" 34This example shows how to define and use complex structures 35in spyne. This example uses an extremely simple in-memory 36dictionary to store the User objects. 37""" 38 39import logging 40import random 41 42from spyne import Application, rpc, Array, ComplexModel, Integer, String, \ 43 Service, ResourceNotFoundError 44from spyne.protocol.http import HttpRpc 45from spyne.protocol.xml import XmlDocument 46from spyne.server.wsgi import WsgiApplication 47 48user_database = {} 49userid_seq = 1 50chars = [chr(i) for i in range(ord('a'), ord('z'))] 51 52 53def randchars(n): 54 return ''.join(random.choice(chars) for _ in range(n)) 55 56 57class Permission(ComplexModel): 58 __namespace__ = "permission" 59 60 app = String(values=['library', 'delivery', 'accounting']) 61 perms = String(min_occurs=1, max_occurs=2, values=['read', 'write']) 62 63 64class User(ComplexModel): 65 __namespace__ = "user" 66 67 userid = Integer 68 username = String 69 firstname = String 70 lastname = String 71 permissions = Array(Permission) 72 73 74# add superuser to the 'database' 75 76all_permissions = ( 77 Permission(app='library', perms=['read', 'write']), 78 Permission(app='delivery', perms=['read', 'write']), 79 Permission(app='accounting', perms=['read', 'write']), 80) 81 82 83def randperms(n): 84 for p in random.sample(all_permissions, n): 85 yield Permission(app=p.app, 86 perms=random.sample(p.perms, random.randint(1, 2))) 87 88 89user_database[0] = User( 90 userid=0, 91 username='root', 92 firstname='Super', 93 lastname='User', 94 permissions=all_permissions 95) 96 97 98def add_user(user): 99 global user_database 100 global userid_seq 101 102 user.userid = userid_seq 103 userid_seq = userid_seq + 1 104 user_database[user.userid] = user 105 106 107class UserManager(Service): 108 @rpc(User, _returns=Integer) 109 def add_user(ctx, user): 110 add_user(user) 111 return user.userid 112 113 @rpc(_returns=User) 114 def super_user(ctx): 115 return user_database[0] 116 117 @rpc(_returns=User) 118 def random_user(ctx): 119 retval = User( 120 username=randchars(random.randrange(3, 12)), 121 firstname=randchars(random.randrange(3, 12)).title(), 122 lastname=randchars(random.randrange(3, 12)).title(), 123 permissions=randperms(random.randint(1, len(all_permissions))) 124 ) 125 126 add_user(retval) 127 128 return retval 129 130 @rpc(Integer, _returns=User) 131 def get_user(ctx, userid): 132 global user_database 133 134 # If you rely on dict lookup raising KeyError here, you'll return an 135 # internal error to the client, which tells the client that there's 136 # something wrong in the server. However in this case, KeyError means 137 # invalid request, so it's best to return a client error. 138 139 # For the HttpRpc case, internal error is 500 whereas 140 # ResourceNotFoundError is 404. 141 if not (userid in user_database): 142 raise ResourceNotFoundError(userid) 143 144 return user_database[userid] 145 146 @rpc(User) 147 def modify_user(ctx, user): 148 global user_database 149 150 if not (user.userid in user_database): 151 raise ResourceNotFoundError(user.userid) 152 153 user_database[user.userid] = user 154 155 @rpc(Integer) 156 def delete_user(ctx, userid): 157 global user_database 158 159 if not (userid in user_database): 160 raise ResourceNotFoundError(userid) 161 162 del user_database[userid] 163 164 @rpc(_returns=Array(User)) 165 def list_users(ctx): 166 global user_database 167 168 return user_database.values() 169 170 171if __name__ == '__main__': 172 from wsgiref.simple_server import make_server 173 174 logging.basicConfig(level=logging.DEBUG) 175 logging.getLogger('spyne.protocol.xml').setLevel(logging.DEBUG) 176 177 application = Application([UserManager], 'spyne.examples.complex', 178 in_protocol=HttpRpc(), out_protocol=XmlDocument()) 179 180 server = make_server('127.0.0.1', 8000, WsgiApplication(application)) 181 182 logging.info("listening to http://127.0.0.1:8000") 183 logging.info("wsdl is at: http://localhost:8000/?wsdl") 184 185 server.serve_forever() 186