├── demos ├── __init__.py ├── HelloWorldService.py ├── HelloWorldService2.py ├── CurrentTempService.py ├── HelloWorldAsyncService.py ├── MathService.py ├── CertService.py ├── RepositoryService.py ├── UserRolesService.py ├── RegisterService.py ├── DemoServices2.py ├── DemoServices.py ├── ProductListService2.py ├── DemoServicesHostname.py ├── ProductService.py ├── ProductListService.py └── README.md ├── .gitignore ├── tornadows ├── __init__.py ├── soap.py ├── webservices.py ├── xmltypes.py ├── wsdl.py ├── soaphandler.py └── complextypes.py ├── README.md └── setup.py /demos/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | *.sw[po] 3 | *.bak 4 | *.orig 5 | *~ 6 | *.log 7 | *.egg-info 8 | .project 9 | .pydevproject 10 | venv/ 11 | virtenv/ 12 | dist/ 13 | build/ 14 | tornadows.egg-info/ 15 | -------------------------------------------------------------------------------- /tornadows/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tornado Web Services: 2 | ===================== 3 | This is an implementation of SOAP Web Service API, to be used in a Tornado Web Server, 4 | taking advantage of the great features of that server. 5 | 6 | tornado-webservice is licensed user the Apache License, Version 2.0 7 | (http://www.apache.org/licenses/LICENSE-2.0). 8 | 9 | Prerequisities: 10 | --------------- 11 | 12 | You need to have tornado installed, you must download and install from: 13 | 14 | http://www.tornadoweb.org/ 15 | 16 | Installation: 17 | ------------- 18 | - Using PIP: 19 | ``` 20 | $ pip install tornado-webservices 21 | ``` 22 | - Using easy_install: 23 | ``` 24 | $ easy_install tornado-webservices 25 | ``` 26 | - Manually: 27 | ``` 28 | $ tar xvzf tornado-webservices-version.tar.gz 29 | $ cd tornado-webservices-version 30 | $ python setup.py install 31 | ``` 32 | 33 | 34 | On Linux systems (like ubuntu or similar) you need to use the sudo command...don't forget ;-) 35 | 36 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | from distutils.core import setup 18 | 19 | try: 20 | from setuptools import setup 21 | except ImportError: 22 | pass 23 | 24 | 25 | setup( 26 | name='tornado-webservices', 27 | version = '0.9.5.0', 28 | packages=['tornadows','demos'], 29 | author='Innovaser', 30 | author_email='rancavil@innovaser.cl', 31 | install_requires=['tornado',], 32 | ) 33 | -------------------------------------------------------------------------------- /demos/HelloWorldService.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import tornado.httpserver 18 | import tornado.ioloop 19 | from tornadows import soaphandler 20 | from tornadows import webservices 21 | from tornadows import xmltypes 22 | from tornadows.soaphandler import webservice 23 | 24 | class HelloWorldService(soaphandler.SoapHandler): 25 | """ Service that return Hello World!!!, not uses input parameters """ 26 | @webservice(_params=None,_returns=xmltypes.String) 27 | def sayHello(self): 28 | return "Hello World!!!" 29 | 30 | if __name__ == '__main__': 31 | service = [('HelloWorldService',HelloWorldService)] 32 | app = webservices.WebService(service) 33 | ws = tornado.httpserver.HTTPServer(app) 34 | ws.listen(8080) 35 | tornado.ioloop.IOLoop.instance().start() 36 | -------------------------------------------------------------------------------- /demos/HelloWorldService2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import tornado.httpserver 18 | import tornado.ioloop 19 | from tornadows import soaphandler 20 | from tornadows import webservices 21 | from tornadows import xmltypes 22 | from tornadows.soaphandler import webservice 23 | 24 | class HelloWorldService2(soaphandler.SoapHandler): 25 | """ Service that return an list with Hello and World str elements, not uses input parameters """ 26 | @webservice(_params=None,_returns=xmltypes.Array(xmltypes.String)) 27 | def sayHello(self): 28 | return ["Hello","World"] 29 | 30 | if __name__ == '__main__': 31 | service = [('HelloWorldService2',HelloWorldService2)] 32 | app = webservices.WebService(service) 33 | ws = tornado.httpserver.HTTPServer(app) 34 | ws.listen(8080) 35 | tornado.ioloop.IOLoop.instance().start() 36 | -------------------------------------------------------------------------------- /demos/CurrentTempService.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import tornado.httpserver 18 | import tornado.ioloop 19 | from tornadows import soaphandler 20 | from tornadows import webservices 21 | from tornadows import xmltypes 22 | from tornadows.soaphandler import webservice 23 | 24 | class CurrentTempService(soaphandler.SoapHandler): 25 | """ Service that return the current temperature, not uses input parameters """ 26 | @webservice(_params=None,_returns=xmltypes.Integer) 27 | def getCurrentTemperature(self): 28 | c = 29 29 | return c 30 | 31 | if __name__ == '__main__': 32 | service = [('CurrentTempService',CurrentTempService)] 33 | app = webservices.WebService(service) 34 | ws = tornado.httpserver.HTTPServer(app) 35 | ws.listen(8080) 36 | tornado.ioloop.IOLoop.instance().start() 37 | -------------------------------------------------------------------------------- /demos/HelloWorldAsyncService.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | #!/usr/bin/env python 3 | # 4 | # Copyright 2014 Jonatan Alexis Anauati 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | # not use this file except in compliance with the License. You may obtain 8 | # a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | # License for the specific language governing permissions and limitations 16 | # under the License. 17 | import tornado.gen 18 | import tornado.httpserver 19 | import tornado.ioloop 20 | 21 | from tornadows import soaphandler, webservices, xmltypes 22 | from tornadows.soaphandler import webservice 23 | 24 | class HelloWorldAsyncService(soaphandler.SoapHandler): 25 | """ Async service that returns 'Hello World!!!' """ 26 | @tornado.gen.coroutine 27 | @webservice(_params=None,_returns=xmltypes.String) 28 | def sayHello(self): 29 | raise tornado.gen.Return('Hello World!!!') 30 | 31 | if __name__ == '__main__': 32 | service = [('HelloWorldAsyncService', HelloWorldAsyncService)] 33 | app = webservices.WebService(service) 34 | ws = tornado.httpserver.HTTPServer(app) 35 | ws.listen(8080) 36 | tornado.ioloop.IOLoop.instance().start() 37 | -------------------------------------------------------------------------------- /demos/MathService.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import tornado.httpserver 18 | import tornado.ioloop 19 | from tornadows import soaphandler 20 | from tornadows import webservices 21 | from tornadows import xmltypes 22 | from tornadows.soaphandler import webservice 23 | 24 | class MathService(soaphandler.SoapHandler): 25 | """ Service that provides math operations of two float numbers """ 26 | @webservice(_params=[float,float],_returns=float) 27 | def add(self, a, b): 28 | result = a+b 29 | return result 30 | @webservice(_params=[float,float],_returns=float) 31 | def sub(self, a, b): 32 | result = a-b 33 | return result 34 | @webservice(_params=[float,float],_returns=float) 35 | def mult(self, a, b): 36 | result = a*b 37 | return result 38 | @webservice(_params=[float,float],_returns=float) 39 | def div(self, a, b): 40 | result = a/b 41 | return result 42 | 43 | if __name__ == '__main__': 44 | service = [('MathService',MathService)] 45 | app = webservices.WebService(service) 46 | ws = tornado.httpserver.HTTPServer(app) 47 | ws.listen(8080) 48 | tornado.ioloop.IOLoop.instance().start() 49 | -------------------------------------------------------------------------------- /demos/CertService.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import tornado.ioloop 18 | from tornadows import soaphandler, webservices, complextypes 19 | from tornadows.soaphandler import webservice 20 | import datetime 21 | 22 | """ This example uses python datetime module. 23 | python datetime.date is equivalent to xml type: xsd:date 24 | python datetime.datetime is equivalent to xml type: xsd:dateTime 25 | python datetime.time is equivalent to xml type: xsd:time 26 | """ 27 | class InputRequest(complextypes.ComplexType): 28 | idperson = str 29 | 30 | class CertificateResponse(complextypes.ComplexType): 31 | numcert = int 32 | idperson = str 33 | nameperson = str 34 | birthday = datetime.date 35 | datetimecert = datetime.datetime 36 | isvalid = bool 37 | 38 | class CertService(soaphandler.SoapHandler): 39 | @webservice(_params=InputRequest, _returns=CertificateResponse) 40 | def getCertificate(self, input): 41 | idperson = input.idperson 42 | 43 | cert = CertificateResponse() 44 | cert.numcert = 1 45 | cert.idperson = idperson 46 | cert.nameperson = 'Steve J' 47 | cert.birthday = datetime.date(1973,12,11) 48 | cert.datetimecert = datetime.datetime.now() 49 | cert.isvalid = True 50 | 51 | return cert 52 | 53 | if __name__ == '__main__': 54 | service = [('CertService',CertService)] 55 | app = webservices.WebService(service) 56 | app.listen(8080) 57 | tornado.ioloop.IOLoop.instance().start() 58 | -------------------------------------------------------------------------------- /demos/RepositoryService.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import tornado.ioloop 18 | from tornadows import soaphandler 19 | from tornadows import webservices 20 | from tornadows import xmltypes 21 | from tornadows import complextypes 22 | from tornadows.soaphandler import webservice 23 | 24 | import datetime 25 | 26 | # This dictionary emulate a documental repository 27 | repo = {} 28 | 29 | class Document(complextypes.ComplexType): 30 | number = int 31 | theme = str 32 | author = str 33 | text = str 34 | created = datetime.date 35 | 36 | class Message(complextypes.ComplexType): 37 | doc = Document 38 | msg = str 39 | 40 | class Repository(soaphandler.SoapHandler): 41 | """ Service of repository, store documents (Document) """ 42 | @webservice(_params=Message,_returns=str) 43 | def save(self, msg): 44 | global repo 45 | repo[msg.doc.number] = msg.doc 46 | return 'Save document number : %d'%msg.doc.number 47 | 48 | @webservice(_params=int,_returns=Message) 49 | def find(self, num): 50 | global repo 51 | response = Message() 52 | try: 53 | doc = Document() 54 | d = repo[num] 55 | doc.number = d.number 56 | doc.theme = d.theme 57 | doc.author = d.author 58 | doc.text = d.text 59 | doc.created = d.created 60 | response.doc = doc 61 | response.msg = 'OK' 62 | except: 63 | response.doc = Document() 64 | response.msg = 'Document number %d dont exist'%num 65 | return response 66 | 67 | if __name__ == '__main__': 68 | service = [('RepositoryService',Repository)] 69 | app = webservices.WebService(service).listen(8080) 70 | tornado.ioloop.IOLoop.instance().start() 71 | -------------------------------------------------------------------------------- /demos/UserRolesService.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import tornado.httpserver 18 | import tornado.ioloop 19 | from tornadows import soaphandler 20 | from tornadows import xmltypes 21 | from tornadows import webservices 22 | from tornadows import complextypes 23 | from tornadows.soaphandler import webservice 24 | 25 | """ This web services shows how uses complextypes and classes python. 26 | 27 | User is a python class with two attributes: 28 | username : is a python str type with the username. 29 | roles : is a python list of str (roles for the username). 30 | 31 | ListOfUser is another class with two attributes: 32 | idlist : is a python str type. 33 | roles : is a python list of User (python class). 34 | 35 | """ 36 | class User(complextypes.ComplexType): 37 | username = str 38 | roles = [str] 39 | 40 | class ListOfUser(complextypes.ComplexType): 41 | idlist = int 42 | list = [User] 43 | 44 | class UserRolesService(soaphandler.SoapHandler): 45 | @webservice(_params=xmltypes.Integer,_returns=ListOfUser) 46 | def getUsers(self, idlist): 47 | user1 = User() 48 | user1.username = 'steve' 49 | user1.roles = ['ceo','admin'] 50 | 51 | user2 = User() 52 | user2.username = 'billy' 53 | user2.roles = ['developer'] 54 | 55 | listusers = ListOfUser() 56 | listusers.idlist = idlist 57 | listusers.list = [user1, user2] 58 | 59 | return listusers 60 | 61 | if __name__ == '__main__': 62 | service = [('UserRolesService',UserRolesService)] 63 | app = webservices.WebService(service) 64 | ws = tornado.httpserver.HTTPServer(app) 65 | ws.listen(8080) 66 | tornado.ioloop.IOLoop.instance().start() 67 | -------------------------------------------------------------------------------- /demos/RegisterService.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import tornado.ioloop 18 | from tornadows import soaphandler, webservices, complextypes 19 | from tornadows.soaphandler import webservice 20 | import datetime 21 | 22 | """ This example uses python datetime module and xml complex datatypes. 23 | This example simulates a service of register of users in a system 24 | 25 | python datetime.date is equivalent to xml type: xsd:date 26 | python datetime.datetime is equivalent to xml type: xsd:dateTime 27 | python datetime.time is equivalent to xml type: xsd:time 28 | """ 29 | class RegisterRequest(complextypes.ComplexType): 30 | iduser = str 31 | names = str 32 | birthdate = datetime.date 33 | email = str 34 | 35 | class RegisterResponse(complextypes.ComplexType): 36 | idregister = int 37 | names = str 38 | datetimeregister = datetime.datetime 39 | isvalid = bool 40 | message = str 41 | 42 | class RegisterService(soaphandler.SoapHandler): 43 | @webservice(_params=RegisterRequest, _returns=RegisterResponse) 44 | def register(self, register): 45 | iduser = register.iduser 46 | names = register.names 47 | birthdate = register.birthdate 48 | email = register.email 49 | 50 | # Here you can insert the user in a database 51 | response = RegisterResponse() 52 | response.idregister = 1 53 | response.names = names 54 | response.datetimeregister = datetime.datetime.now() 55 | response.isvalid = True 56 | response.message = 'Your register for email : %s'%email 57 | 58 | return response 59 | 60 | if __name__ == '__main__': 61 | service = [('RegisterService',RegisterService)] 62 | app = webservices.WebService(service) 63 | app.listen(8080) 64 | tornado.ioloop.IOLoop.instance().start() 65 | -------------------------------------------------------------------------------- /demos/DemoServices2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import tornado.httpserver 18 | import tornado.ioloop 19 | from tornadows import soaphandler 20 | from tornadows import webservices 21 | from tornadows import xmltypes 22 | from tornadows.soaphandler import webservice 23 | 24 | class EchoService(soaphandler.SoapHandler): 25 | """ Echo Service """ 26 | @webservice(_params=str,_returns=str) 27 | def echo(self, message): 28 | return 'Echo say : %s' % message 29 | 30 | class EchoTargetnsService(soaphandler.SoapHandler): 31 | """ Service to test the use of an overrided target namespace address """ 32 | targetns_address = '192.168.0.102' # IP of your machine 33 | @webservice(_params=str, _returns=str) 34 | def echo(self, message): 35 | return 'Echo say : %s' % message 36 | 37 | class CountService(soaphandler.SoapHandler): 38 | """ Service that counts the number of items in a list """ 39 | @webservice(_params=xmltypes.Array(str),_returns=int) 40 | def count(self, list_of_values): 41 | length = len(list_of_values) 42 | return length 43 | 44 | class DivService(soaphandler.SoapHandler): 45 | """ Service that provides the division operation of two float numbers """ 46 | @webservice(_params=[float,float],_returns=float) 47 | def div(self, a, b): 48 | result = a/b 49 | return result 50 | 51 | class FibonacciService(soaphandler.SoapHandler): 52 | """ Service that provides Fibonacci numbers """ 53 | @webservice(_params=int,_returns=xmltypes.Array(int)) 54 | def fib(self,n): 55 | a, b = 0, 1 56 | result = [] 57 | while b < n: 58 | result.append(b) 59 | a, b = b, a + b 60 | return result 61 | 62 | if __name__ == '__main__': 63 | service = [('EchoService',EchoService), 64 | ('EchoTargetnsService', EchoTargetnsService), 65 | ('CountService',CountService), 66 | ('DivService',DivService), 67 | ('FibonacciService',FibonacciService)] 68 | app = webservices.WebService(service) 69 | ws = tornado.httpserver.HTTPServer(app) 70 | ws.listen(8080) 71 | tornado.ioloop.IOLoop.instance().start() 72 | -------------------------------------------------------------------------------- /demos/DemoServices.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import tornado.httpserver 18 | import tornado.ioloop 19 | from tornadows import soaphandler 20 | from tornadows import webservices 21 | from tornadows import xmltypes 22 | from tornadows.soaphandler import webservice 23 | 24 | class EchoService(soaphandler.SoapHandler): 25 | """ Echo Service """ 26 | @webservice(_params=xmltypes.String,_returns=xmltypes.String) 27 | def echo(self, message): 28 | return 'Echo say : %s' % message 29 | 30 | class EchoTargetnsService(soaphandler.SoapHandler): 31 | """ Service to test the use of an overrided target namespace address """ 32 | targetns_address = '192.168.0.103' 33 | @webservice(_params=xmltypes.String, _returns=xmltypes.String) 34 | def echo(self, message): 35 | return 'Echo say : %s' % message 36 | 37 | class CountService(soaphandler.SoapHandler): 38 | """ Service that counts the number of items in a list """ 39 | @webservice(_params=xmltypes.Array(xmltypes.String),_returns=xmltypes.Integer) 40 | def count(self, list_of_values): 41 | length = len(list_of_values) 42 | return length 43 | 44 | class DivService(soaphandler.SoapHandler): 45 | """ Service that provides the division operation of two float numbers """ 46 | @webservice(_params=[xmltypes.Float,xmltypes.Float],_returns=xmltypes.Float) 47 | def div(self, a, b): 48 | result = a/b 49 | return result 50 | 51 | class FibonacciService(soaphandler.SoapHandler): 52 | """ Service that provides Fibonacci numbers """ 53 | @webservice(_params=xmltypes.Integer,_returns=xmltypes.Array(xmltypes.Integer)) 54 | def fib(self,n): 55 | a, b = 0, 1 56 | result = [] 57 | while b < n: 58 | result.append(b) 59 | a, b = b, a + b 60 | return result 61 | 62 | if __name__ == '__main__': 63 | service = [('EchoService',EchoService), 64 | ('EchoTargetnsService', EchoTargetnsService), 65 | ('CountService',CountService), 66 | ('DivService',DivService), 67 | ('FibonacciService',FibonacciService)] 68 | app = webservices.WebService(service) 69 | ws = tornado.httpserver.HTTPServer(app) 70 | ws.listen(8080) 71 | tornado.ioloop.IOLoop.instance().start() 72 | -------------------------------------------------------------------------------- /demos/ProductListService2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import tornado.httpserver 18 | import tornado.ioloop 19 | from tornadows import soaphandler 20 | from tornadows import webservices 21 | from tornadows import complextypes 22 | from tornadows.soaphandler import webservice 23 | 24 | """ This web service implements two python classes as two xml complextypes. 25 | 26 | THIS IS AN ALTERNATIVE IMPLEMENTATION TO ProductService.py. THIS USES 27 | PYTHON TYPES FOR THE ATTRIBUTES OF THE CLASS. 28 | 29 | Product is a class that represents Product data structure. 30 | 31 | id: Is a int python type. Is the product id 32 | name: Is a str python type. Is the name of product. 33 | price: Is a float python type. Is the price of product. 34 | stock: Is a int python type. Is the stock of product. 35 | 36 | List is a class that represents the response of the web services. 37 | This is a list of Product. 38 | 39 | product: Is a python list with a set of product (Product class). 40 | 41 | The operation have not input parameters. 42 | 43 | """ 44 | 45 | class Product(complextypes.ComplexType): 46 | id = int 47 | name = str 48 | price = float 49 | stock = int 50 | 51 | class List(complextypes.ComplexType): 52 | product = [Product] 53 | 54 | class ProductListService2(soaphandler.SoapHandler): 55 | @webservice(_params=None,_returns=List) 56 | def getProductList(self): 57 | 58 | listOfProduct = List() 59 | 60 | for i in [1,2,3,4,5,6,7]: 61 | reg = self.database(i) 62 | output = Product() 63 | output.id = i 64 | output.name = reg[0] 65 | output.price = reg[1] 66 | output.stock = reg[2] 67 | 68 | listOfProduct.product.append(output) 69 | 70 | return listOfProduct 71 | 72 | def database(self,id): 73 | """ This method simulates a database of products """ 74 | db = {1:('COMPUTER',1000.5,100), 75 | 2:('MOUSE',10.0,300), 76 | 3:('PENCIL BLUE',0.50,500), 77 | 4:('PENCIL RED',0.50,600), 78 | 5:('PENCIL WHITE',0.50,900), 79 | 6:('HEADPHONES',15.7,500), 80 | 7:('MACBOOK',80.78,300), 81 | } 82 | row = (None,0.0,0) 83 | try: 84 | row = db[id] 85 | except: 86 | None 87 | return row 88 | 89 | if __name__ == '__main__': 90 | service = [('ProductListService2',ProductListService2)] 91 | app = webservices.WebService(service) 92 | ws = tornado.httpserver.HTTPServer(app) 93 | ws.listen(8080) 94 | tornado.ioloop.IOLoop.instance().start() 95 | -------------------------------------------------------------------------------- /demos/DemoServicesHostname.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import tornado.httpserver 18 | import tornado.ioloop 19 | 20 | from tornado.options import define, options 21 | from tornadows import soaphandler 22 | from tornadows import webservices 23 | from tornadows import xmltypes 24 | from tornadows.soaphandler import webservice 25 | 26 | # If you plan server this webservice in many machines, please set up your 27 | # main domain here. Useful to work in a round-robin way or reverse proxy. 28 | define("wsdl_hostname", default="mydomain.com", help="WSDL Hostname") 29 | 30 | class EchoService(soaphandler.SoapHandler): 31 | """ Echo Service """ 32 | @webservice(_params=xmltypes.String,_returns=xmltypes.String) 33 | def echo(self, message): 34 | return 'Echo say : %s' % message 35 | 36 | class EchoTargetnsService(soaphandler.SoapHandler): 37 | """ Service to test the use of an overrided target namespace address """ 38 | targetns_address = '192.168.0.103' 39 | @webservice(_params=xmltypes.String, _returns=xmltypes.String) 40 | def echo(self, message): 41 | return 'Echo say : %s' % message 42 | 43 | class CountService(soaphandler.SoapHandler): 44 | """ Service that counts the number of items in a list """ 45 | @webservice(_params=xmltypes.Array(xmltypes.String),_returns=xmltypes.Integer) 46 | def count(self, list_of_values): 47 | length = len(list_of_values) 48 | return length 49 | 50 | class DivService(soaphandler.SoapHandler): 51 | """ Service that provides the division operation of two float numbers """ 52 | @webservice(_params=[xmltypes.Float,xmltypes.Float],_returns=xmltypes.Float) 53 | def div(self, a, b): 54 | result = a/b 55 | return result 56 | 57 | class FibonacciService(soaphandler.SoapHandler): 58 | """ Service that provides Fibonacci numbers """ 59 | @webservice(_params=xmltypes.Integer,_returns=xmltypes.Array(xmltypes.Integer)) 60 | def fib(self,n): 61 | a, b = 0, 1 62 | result = [] 63 | while b < n: 64 | result.append(b) 65 | a, b = b, a + b 66 | return result 67 | 68 | if __name__ == '__main__': 69 | service = [('EchoService',EchoService), 70 | ('EchoTargetnsService', EchoTargetnsService), 71 | ('CountService',CountService), 72 | ('DivService',DivService), 73 | ('FibonacciService',FibonacciService)] 74 | app = webservices.WebService(service) 75 | ws = tornado.httpserver.HTTPServer(app) 76 | ws.listen(8080) 77 | tornado.ioloop.IOLoop.instance().start() 78 | -------------------------------------------------------------------------------- /demos/ProductService.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf8 -*- 3 | # 4 | # Copyright 2011 Rodrigo Ancavil del Pino 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | # not use this file except in compliance with the License. You may obtain 8 | # a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | # License for the specific language governing permissions and limitations 16 | # under the License. 17 | 18 | import tornado.httpserver 19 | import tornado.ioloop 20 | from tornadows import soaphandler 21 | from tornadows import webservices 22 | from tornadows import complextypes 23 | from tornadows.soaphandler import webservice 24 | 25 | """ This web service implements two python classes as two xml complextypes. 26 | 27 | Input is a class that represents the input parameter (request) for the 28 | web service: 29 | 30 | idProduct : is a instance of IntegerProperty(). This create a subclass 31 | of Property with value attribute (for store the value) and 32 | type attribute for store the xmltype. 33 | 34 | Product is a class that represents the response of the web service. 35 | 36 | id: Is a instance of IntegerProperty() that store the id of product 37 | name: Is a instance of StringProperty() that store the name of product 38 | price: Is a instance of FloatProperty() that store the price of product 39 | stock: Is a instance of IntegerProperty() that store the stock of product 40 | 41 | """ 42 | 43 | class Input(complextypes.ComplexType): 44 | idProduct = complextypes.IntegerProperty() 45 | 46 | class Product(complextypes.ComplexType): 47 | id = complextypes.IntegerProperty() 48 | name = complextypes.StringProperty() 49 | price = complextypes.FloatProperty() 50 | stock = complextypes.IntegerProperty() 51 | 52 | class ProductService(soaphandler.SoapHandler): 53 | @webservice(_params=Input,_returns=Product) 54 | def getProduct(self, input): 55 | id = input.idProduct.value 56 | 57 | reg = self.database(id) 58 | 59 | output = Product() 60 | 61 | output.id.value = id 62 | output.name.value = reg[0] 63 | output.price.value = reg[1] 64 | output.stock.value = reg[2] 65 | 66 | return output 67 | 68 | def database(self,id): 69 | """ This method simulates a database of products """ 70 | db = {1:('COMPUTER',1000.5,100), 71 | 2:('MOUSE',10.0,300), 72 | 3:('PENCIL BLUE',0.50,500), 73 | 4:('PENCIL RED',0.50,600), 74 | 5:('PENCIL WHITE',0.50,900), 75 | 6:('HEADPHONES',15.7,500), 76 | 7:(u'Japanses Noodles (ラーメン)',1.1,500), 77 | } 78 | row = (None,0.0,0) 79 | try: 80 | row = db[id] 81 | except: 82 | None 83 | return row 84 | 85 | if __name__ == '__main__': 86 | service = [('ProductService',ProductService)] 87 | app = webservices.WebService(service) 88 | ws = tornado.httpserver.HTTPServer(app) 89 | ws.listen(8080) 90 | tornado.ioloop.IOLoop.instance().start() 91 | -------------------------------------------------------------------------------- /demos/ProductListService.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import tornado.httpserver 18 | import tornado.ioloop 19 | from tornadows import soaphandler 20 | from tornadows import webservices 21 | from tornadows import complextypes 22 | from tornadows.soaphandler import webservice 23 | 24 | """ This web service implements two python classes as two xml complextypes. 25 | 26 | THIS IS AN ALTERNATIVE IMPLEMENTATION TO ProductService.py. THIS USES 27 | PYTHON TYPES FOR THE ATTRIBUTES OF THE CLASS. 28 | 29 | Input is a class that represents the input parameter (request) for the 30 | web service: 31 | 32 | idList : is a int python type. 33 | 34 | Product is a class that represents Product data structure. 35 | 36 | id: Is a int python type. Is the product id 37 | name: Is a str python type. Is the name of product. 38 | price: Is a float python type. Is the price of product. 39 | stock: Is a int python type. Is the stock of product. 40 | 41 | List is a class that represents the response of the web services. 42 | 43 | idList: IS a int python type. Is a id for the list. 44 | product: Is a python list with a set of product (Product class). 45 | 46 | """ 47 | 48 | class Input(complextypes.ComplexType): 49 | idList = int 50 | 51 | class Product(complextypes.ComplexType): 52 | id = int 53 | name = str 54 | price = float 55 | stock = int 56 | 57 | class List(complextypes.ComplexType): 58 | idList = int 59 | product = [Product] 60 | 61 | class ProductListService(soaphandler.SoapHandler): 62 | @webservice(_params=Input,_returns=List) 63 | def getProductList(self, input): 64 | id = input.idList 65 | 66 | listOfProduct = List() 67 | listOfProduct.idList = id 68 | 69 | for i in [1,2,3,4,5,6]: 70 | reg = self.database(i) 71 | output = Product() 72 | output.id = i 73 | output.name = reg[0] 74 | output.price = reg[1] 75 | output.stock = reg[2] 76 | 77 | listOfProduct.product.append(output) 78 | 79 | return listOfProduct 80 | 81 | def database(self,id): 82 | """ This method simulates a database of products """ 83 | db = {1:('COMPUTER',1000.5,100), 84 | 2:('MOUSE',10.0,300), 85 | 3:('PENCIL BLUE',0.50,500), 86 | 4:('PENCIL RED',0.50,600), 87 | 5:('PENCIL WHITE',0.50,900), 88 | 6:('HEADPHONES',15.7,500), 89 | } 90 | row = (None,0.0,0) 91 | try: 92 | row = db[id] 93 | except: 94 | None 95 | return row 96 | 97 | if __name__ == '__main__': 98 | service = [('ProductListService',ProductListService)] 99 | app = webservices.WebService(service) 100 | ws = tornado.httpserver.HTTPServer(app) 101 | ws.listen(8080) 102 | tornado.ioloop.IOLoop.instance().start() 103 | -------------------------------------------------------------------------------- /tornadows/soap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | """ Implementation of a envelope soap 1.1 """ 18 | 19 | import xml.dom.minidom 20 | 21 | class SoapMessage: 22 | """ Implementation of a envelope soap 1.1 with minidom API 23 | 24 | import tornadows.soap 25 | import xml.dom.minidom 26 | 27 | soapenvelope = tornadows.soap.SoapMessage() 28 | xmlDoc = xml.dom.minidom.parseString('Hello, world!!!') 29 | soapenvelope.setBody(xmlDoc) 30 | for s in soapenvelope.getBody(): 31 | print s.toxml() 32 | 33 | """ 34 | def __init__(self): 35 | self._soap = xml.dom.minidom.Document() 36 | envurl = 'http://schemas.xmlsoap.org/soap/envelope/' 37 | self._envelope = self._soap.createElementNS(envurl, 'soapenv:Envelope') 38 | self._envelope.setAttribute('xmlns:soapenv', envurl) 39 | self._envelope.setAttribute('xmlns:xsi', 40 | "http://www.w3.org/2001/XMLSchema-instance") 41 | self._envelope.setAttribute('xsi:schemaLocation', 42 | ' '.join((envurl, envurl))) 43 | self._soap.appendChild(self._envelope) 44 | self._header = self._soap.createElement('soapenv:Header') 45 | self._body = self._soap.createElement('soapenv:Body') 46 | self._envelope.appendChild(self._header) 47 | self._envelope.appendChild(self._body) 48 | 49 | def getSoap(self): 50 | """ Return the soap envelope as xml.dom.minidom.Document 51 | getSoap() return a xml.dom.minidom.Document object 52 | """ 53 | return self._soap 54 | 55 | def getHeader(self): 56 | """ Return the child elements of Header element 57 | getHeader() return a list with xml.dom.minidom.Element objects 58 | """ 59 | return self._header.childNodes 60 | 61 | def getBody(self): 62 | """ Return the child elements of Body element 63 | getBody() return a list with xml.dom.minidom.Element objects 64 | """ 65 | return self._body.childNodes 66 | 67 | def setHeader(self, header): 68 | """ Set the child content to Header element 69 | setHeader(header), header is a xml.dom.minidom.Document object 70 | """ 71 | if isinstance(header,xml.dom.minidom.Document): 72 | self._header.appendChild(header.documentElement) 73 | elif isinstance(header,xml.dom.minidom.Element): 74 | self._header.appendChild(header) 75 | 76 | def setBody(self,body): 77 | """ Set the child content to Body element 78 | setBody(body), body is a xml.dom.minidom.Document object or 79 | a xml.dom.minidom.Element 80 | """ 81 | if isinstance(body,xml.dom.minidom.Document): 82 | self._body.appendChild(body.documentElement) 83 | elif isinstance(body,xml.dom.minidom.Element): 84 | self._body.appendChild(body) 85 | 86 | def removeHeader(self): 87 | """ Remove the last child elements from Header element """ 88 | lastElement = self._header.lastChild 89 | if lastElement != None: 90 | self._header.removeChild(lastElement) 91 | 92 | def removeBody(self): 93 | """ Remove last child elements from Body element """ 94 | lastElement = self._body.lastChild 95 | if lastElement != None: 96 | self._body.removeChild(lastElement) 97 | -------------------------------------------------------------------------------- /tornadows/webservices.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | """ Implementation of webservices API 0.9 """ 18 | 19 | import tornado.httpserver 20 | import tornado.ioloop 21 | import tornado.web 22 | import tornado.wsgi 23 | 24 | class WebService(tornado.web.Application): 25 | """ A implementation of web services for tornado web server. 26 | 27 | import tornado.httpserver 28 | import tornado.ioloop 29 | from tornadows import webservices 30 | from tornadows import xmltypes 31 | from tornadows import soaphandler 32 | from tornadows.soaphandler import webservice 33 | 34 | class MyService(soaphandler.SoapHandler): 35 | @webservice(_params=[xmltypes.Integer, xmltypes.Integer],_returns=xmltypes.Integer) 36 | def sum(self, value1, value2): 37 | result = value1 + value2 38 | 39 | return result 40 | 41 | if __name__ == "__main__": 42 | app = webservices.WebService("MyService",MyService) 43 | ws_server = tornado.httpserver.HTTPServer(app) 44 | ws_server.listen(8080) 45 | tornado.ioloop.IOLoop.instance().start() 46 | 47 | """ 48 | def __init__(self,services,object=None,wsdl=None): 49 | """ Initializes the application for web services 50 | 51 | Instances of this class are callable and can be passed to 52 | HTTPServer of tornado to serve the web services. 53 | 54 | The constructor for this class takes the name for the web 55 | service (service), the class with the web service (object) 56 | and wsdl with the wsdl file path (if this exist). 57 | """ 58 | if isinstance(services,list) and object == None: 59 | srvs = [] 60 | for s in services: 61 | srv = s[0] 62 | obj = s[1] 63 | srvs.append((r"/"+str(srv),obj)) 64 | srvs.append((r"/"+str(srv)+"/",obj)) 65 | tornado.web.Application.__init__(self,srvs) 66 | else: 67 | self._service = services 68 | self._object = object 69 | self._services = [(r"/"+str(self._service),self._object), 70 | (r"/"+str(self._service)+"/",self._object),] 71 | tornado.web.Application.__init__(self,self._services) 72 | 73 | class WSGIWebService(tornado.wsgi.WSGIApplication): 74 | """ A implementation of web services for tornado web server. 75 | 76 | import tornado.httpserver 77 | import tornado.ioloop 78 | from tornadows import webservices 79 | from tornadows import xmltypes 80 | from tornadows import soaphandler 81 | from tornadows.soaphandler import webservice 82 | import wsgiref.simple_server 83 | 84 | class MyService(soaphandler.SoapHandler): 85 | @webservice(_params=[xmltypes.Integer, xmltypes.Integer],_returns=xmltypes.Integer) 86 | def sum(self, value1, value2): 87 | result = value1 + value2 88 | 89 | return result 90 | 91 | if __name__ == "__main__": 92 | app = webservices.WSGIWebService("MyService",MyService) 93 | server = wsgiref.simple_server.make_server('',8888,app) 94 | server.serve_forever() 95 | """ 96 | def __init__(self,services,object=None,wsdl=None, default_host="", **settings): 97 | """ Initializes the application for web services 98 | 99 | Instances of this class are callable and can be passed to 100 | HTTPServer of tornado to serve the web services. 101 | 102 | The constructor for this class takes the name for the web 103 | service (service), the class with the web service (object) 104 | and wsdl with the wsdl file path (if this exist). 105 | """ 106 | if isinstance(services,list) and object == None: 107 | srvs = [] 108 | for s in services: 109 | srv = s[0] 110 | obj = s[1] 111 | srvs.append((r"/"+str(srv),obj)) 112 | srvs.append((r"/"+str(srv)+"/",obj)) 113 | tornado.wsgi.WSGIApplication.__init__(self,srvs,default_host, **settings) 114 | else: 115 | self._service = services 116 | self._object = object 117 | self._services = [(r"/"+str(self._service),self._object), 118 | (r"/"+str(self._service)+"/",self._object),] 119 | tornado.wsgi.WSGIApplication.__init__(self,self._services,default_host, **settings) 120 | -------------------------------------------------------------------------------- /tornadows/xmltypes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | """ 18 | Are incorporated the primitive datatypes defined by XML. 19 | Array is defined for the use of array of elements and his respective datatype. 20 | """ 21 | 22 | import inspect 23 | from tornadows import complextypes 24 | 25 | def createElementXML(name,type,prefix='xsd'): 26 | """ Function used for the creation of xml elements. """ 27 | return '<%s:element name="%s" type="%s:%s"/>'%(prefix,name,prefix,type) 28 | 29 | def createArrayXML(name,type,prefix='xsd',maxoccurs=None): 30 | """ Function used for the creation of xml complexElements """ 31 | complexType = '<%s:complexType name="%sParams">\n'%(prefix,name) 32 | complexType += '<%s:sequence>\n'%prefix 33 | if maxoccurs == None: 34 | complexType += '<%s:element name="value" type="%s:%s" maxOccurs="unbounded"/>\n'%(prefix,prefix,type) 35 | else: 36 | complexType += '<%s:element name="value" type="%s:%s" maxOccurs="%d"/>\n'%(prefix,prefix,type,maxoccurs) 37 | complexType += '\n'%prefix 38 | complexType += '\n'%prefix 39 | complexType += '<%s:element name="%s" type="tns:%sParams"/>\n'%(prefix,name,name) 40 | return complexType 41 | 42 | class Array: 43 | """ Create arrays of xml elements. 44 | 45 | Here an example: 46 | 47 | @webservices(_params=xmltypes.Array(xmltypes.Integer),_returns=xmltypes.Integer) 48 | def function(sefl, list_of_elements): 49 | for e in list_of_elements: 50 | # Do something with the element 51 | return len(list_of_elements) 52 | 53 | xmltypes.Array(xmltype.Integer) generate an xml element into schema definition: 54 | 55 | 56 | this make the parameter of the function list_of_elements is a python list. 57 | 58 | if you specify xmltypes.Array(xmltypes.Integer,10), is generated: 59 | 60 | """ 61 | def __init__(self,type,maxOccurs=None): 62 | self._type = type 63 | self._n = maxOccurs 64 | 65 | def createArray(self,name): 66 | type = None 67 | if inspect.isclass(self._type) and not issubclass(self._type,PrimitiveType): 68 | type = complextypes.createPythonType2XMLType(self._type.__name__) 69 | else: 70 | type = self._type.getType(self._type) 71 | return createArrayXML(name,type,'xsd',self._n) 72 | 73 | def createType(self,name): 74 | prefix = 'xsd' 75 | type = None 76 | if inspect.isclass(self._type) and not issubclass(self._type,PrimitiveType): 77 | type = complextypes.createPythonType2XMLType(self._type.__name__) 78 | else: 79 | type = self._type.getType(self._type) 80 | maxoccurs = self._n 81 | complexType = '' 82 | if self._n == None: 83 | complexType += '<%s:element name="%s" type="%s:%s" maxOccurs="unbounded"/>\n'%(prefix,name,prefix,type) 84 | else: 85 | complexType += '<%s:element name="%s" type="%s:%s" maxOccurs="%d"/>\n'%(prefix,name,prefix,type,maxoccurs) 86 | return complexType 87 | 88 | def genType(self,v): 89 | value = None 90 | if inspect.isclass(self._type) and issubclass(self._type,PrimitiveType): 91 | value = self._type.genType(v) 92 | elif hasattr(self._type,'__name__'): 93 | value = complextypes.convert(self._type.__name__,v) 94 | # Convert str to bool 95 | if value == 'true': 96 | value = True 97 | elif value == 'false': 98 | value = False 99 | return value 100 | 101 | class PrimitiveType: 102 | """ Class father for all derived types. """ 103 | pass 104 | 105 | class Integer(PrimitiveType): 106 | """ 1. XML primitive type : integer """ 107 | @staticmethod 108 | def createElement(name,prefix='xsd'): 109 | return createElementXML(name,'integer') 110 | @staticmethod 111 | def getType(self): 112 | return 'integer' 113 | @classmethod 114 | def genType(self,v): 115 | return int(v) 116 | 117 | class Decimal(PrimitiveType): 118 | """ 2. XML primitive type : decimal """ 119 | @staticmethod 120 | def createElement(name,prefix='xsd'): 121 | return createElementXML(name,'decimal') 122 | @staticmethod 123 | def getType(self): 124 | return 'decimal' 125 | @classmethod 126 | def genType(self,v): 127 | return float(v) 128 | 129 | class Double(PrimitiveType): 130 | """ 3. XML primitive type : double """ 131 | @staticmethod 132 | def createElement(name,prefix='xsd'): 133 | return createElementXML(name,'double') 134 | @staticmethod 135 | def getType(self): 136 | return 'double' 137 | @classmethod 138 | def genType(self,v): 139 | return float(v) 140 | 141 | class Float(PrimitiveType): 142 | """ 4. XML primitive type : float """ 143 | @staticmethod 144 | def createElement(name,prefix='xsd'): 145 | return createElementXML(name,'float') 146 | @staticmethod 147 | def getType(self): 148 | return 'float' 149 | @classmethod 150 | def genType(self,v): 151 | return float(v) 152 | 153 | class Duration(PrimitiveType): 154 | """ 5. XML primitive type : duration """ 155 | @staticmethod 156 | def createElement(name,prefix='xsd'): 157 | return createElementXML(name,'duration') 158 | @staticmethod 159 | def getType(self): 160 | return 'duration' 161 | @classmethod 162 | def genType(self,v): 163 | return str(v) 164 | 165 | class Date(PrimitiveType): 166 | """ 6. XML primitive type : date """ 167 | @staticmethod 168 | def createElement(name,prefix='xsd'): 169 | return createElementXML(name,'date') 170 | @staticmethod 171 | def getType(self): 172 | return 'date' 173 | @classmethod 174 | def genType(self,v): 175 | return str(v) 176 | 177 | class Time(PrimitiveType): 178 | """ 7. XML primitive type : time """ 179 | @staticmethod 180 | def createElement(name,prefix='xsd'): 181 | return createElementXML(name,'time') 182 | @staticmethod 183 | def getType(self): 184 | return 'time' 185 | @classmethod 186 | def genType(self,v): 187 | return str(v) 188 | 189 | class DateTime(PrimitiveType): 190 | """ 8. XML primitive type : dateTime """ 191 | @staticmethod 192 | def createElement(name,prefix='xsd'): 193 | return createElementXML(name,'dateTime') 194 | @staticmethod 195 | def getType(self): 196 | return 'dateTime' 197 | @classmethod 198 | def genType(self,v): 199 | return str(v) 200 | 201 | class String(PrimitiveType): 202 | """ 9. XML primitive type : string """ 203 | @staticmethod 204 | def createElement(name,prefix='xsd'): 205 | return createElementXML(name,'string') 206 | @staticmethod 207 | def getType(self): 208 | return 'string' 209 | @classmethod 210 | def genType(self,v): 211 | return str(v) 212 | 213 | class Boolean(PrimitiveType): 214 | """ 10. XML primitive type : boolean """ 215 | @staticmethod 216 | def createElement(name,prefix='xsd'): 217 | return createElementXML(name,'boolean') 218 | @staticmethod 219 | def getType(self): 220 | return 'boolean' 221 | @classmethod 222 | def genType(self,v): 223 | return str(v).lower() 224 | -------------------------------------------------------------------------------- /tornadows/wsdl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | """ Class Wsdl to generate WSDL Document """ 18 | import xml.dom.minidom 19 | import inspect 20 | from tornadows import xmltypes 21 | from tornadows import complextypes 22 | 23 | class Wsdl: 24 | """ ToDO: 25 | - Incorporate exceptions for parameters inputs. 26 | - When elementInput and/or elementOutput are empty trigger a exception. 27 | """ 28 | def __init__(self,nameservice=None,targetNamespace=None,methods=None,location=None): 29 | self._nameservice = nameservice 30 | self._namespace = targetNamespace 31 | self._methods = methods 32 | self._location = location 33 | 34 | def createWsdl(self): 35 | """ Method that allows create the wsdl file """ 36 | typeInput = None 37 | typeOutput = None 38 | types = '\n' 39 | types += '\n'%self._namespace 40 | 41 | namespace = 'xsd' 42 | types_list = [] 43 | ltype = [] 44 | for wsdl_data in self._methods: 45 | self._arguments = wsdl_data['args'] 46 | self._elementNameInput = wsdl_data['input'][0] 47 | self._elementInput = wsdl_data['input'][1] 48 | self._elementNameOutput = wsdl_data['output'][0] 49 | self._elementOutput = wsdl_data['output'][1] 50 | self._operation = wsdl_data['operation'] 51 | 52 | method = self._operation 53 | 54 | if len(self._methods) == 1: 55 | method = '' 56 | 57 | if inspect.isclass(self._elementInput) and issubclass(self._elementInput,complextypes.ComplexType): 58 | typeInput = self._elementInput.getName()+method 59 | 60 | if ltype.count(self._elementInput.getName()) == 0: 61 | ltype.append(self._elementInput.getName()) 62 | types += self._elementInput.toXSD(method=method,ltype=ltype) 63 | 64 | types += '<%s:element name="%s" type="tns:%s"/>'%(namespace,typeInput,self._elementInput.getName()) 65 | 66 | elif isinstance(self._elementInput,dict): 67 | typeInput = self._elementNameInput+method 68 | types += self._createComplexTypes(self._elementNameInput+method, self._arguments, self._elementInput) 69 | elif isinstance(self._elementInput,xmltypes.Array): 70 | typeInput = self._elementNameInput+method 71 | types += self._elementInput.createArray(typeInput) 72 | elif isinstance(self._elementInput,list) or inspect.isclass(self._elementInput) and issubclass(self._elementInput,xmltypes.PrimitiveType): 73 | typeInput = self._elementNameInput+method 74 | types += self._createTypes(typeInput,self._elementInput) 75 | else: # In case if _elementNameInput is a datatype of python (str, int, float, datetime, etc.) or None 76 | typeInput = self._elementNameInput+method 77 | types += self._createTypes(typeInput,self._elementInput) 78 | 79 | if inspect.isclass(self._elementOutput) and issubclass(self._elementOutput,complextypes.ComplexType): 80 | typeOutput = self._elementOutput.getName()+method 81 | 82 | if ltype.count(self._elementOutput.getName()) == 0: 83 | ltype.append(self._elementOutput.getName()) 84 | types += self._elementOutput.toXSD(method=method,ltype=ltype) 85 | 86 | types += '<%s:element name="%s" type="tns:%s"/>'%(namespace,typeOutput,self._elementOutput.getName()) 87 | 88 | elif isinstance(self._elementOutput,xmltypes.Array): 89 | typeOutput = self._elementNameOutput+method 90 | types += self._elementOutput.createArray(typeOutput) 91 | elif isinstance(self._elementOutput,list) or inspect.isclass(self._elementOutput) and issubclass(self._elementOutput,xmltypes.PrimitiveType): 92 | typeOutput = self._elementNameOutput+method 93 | types += self._createTypes(typeOutput,self._elementOutput) 94 | else: # In case if _elementNameOutput is a datatype of python (str, int, float, datetime, etc.) or None 95 | typeOutput = self._elementNameOutput+method 96 | types += self._createTypes(typeOutput,self._elementOutput) 97 | 98 | types_list.append({'typeInput':typeInput,'typeOutput':typeOutput,'method':method}) 99 | 100 | types += '\n' 101 | types += '\n' 102 | 103 | messages = '' 104 | 105 | for t in types_list: 106 | typeInput = t['typeInput'] 107 | typeOutput = t['typeOutput'] 108 | method = t['method'] 109 | 110 | if len(types_list) == 1: 111 | method = '' 112 | 113 | messages += '\n'%(self._nameservice,method) 114 | messages += '\n'%(method,typeInput) 115 | messages += '\n' 116 | 117 | messages += '\n'%(self._nameservice,method) 118 | messages += '\n'%(method,typeOutput) 119 | messages += '\n' 120 | 121 | portType = '\n'%self._nameservice 122 | 123 | for wsdl_data in self._methods: 124 | self._operation = wsdl_data['operation'] 125 | 126 | method = self._operation 127 | if len(self._methods) == 1: 128 | method = '' 129 | 130 | portType += '\n'%self._operation 131 | portType += '\n'%(self._nameservice,method) 132 | portType += '\n'%(self._nameservice,method) 133 | portType += '\n' 134 | 135 | portType += '\n' 136 | 137 | binding = '\n'%(self._nameservice,self._nameservice) 138 | binding += '\n' 139 | 140 | for wsdl_data in self._methods: 141 | self._operation = wsdl_data['operation'] 142 | 143 | binding += '\n'%self._operation 144 | binding += '\n'%(self._location,self._operation) 145 | binding += '\n' 146 | binding += '\n' 147 | binding += '\n' 148 | 149 | binding += '\n' 150 | 151 | service = '\n'%self._nameservice 152 | service += '\n'%(self._nameservice,self._nameservice) 153 | service += '\n'%self._location 154 | service += '\n' 155 | service += '\n' 156 | 157 | definitions = '\n' 189 | elem += '\n' 190 | elem += '\n'%(name,name) 191 | elif inspect.isclass(elements) and issubclass(elements,xmltypes.PrimitiveType): 192 | elem = elements.createElement(name)+'\n' 193 | elif hasattr(elements,'__name__'): 194 | elem += '\n'%(name,complextypes.createPythonType2XMLType(elements.__name__)) 195 | 196 | return elem 197 | 198 | def _createComplexTypes(self, name, arguments, elements): 199 | """ Private method that creates complex types for wsdl """ 200 | elem = '' 201 | if isinstance(elements,dict): 202 | elem = '\n'%name 203 | elem += '\n' 204 | elems = '' 205 | for e in arguments: 206 | if isinstance(elements[e],xmltypes.Array): 207 | elems += elements[e].createType(e) 208 | elif issubclass(elements[e],xmltypes.PrimitiveType): 209 | elems += elements[e].createElement(e)+'\n' 210 | else: 211 | elems += '\n'%(e,complextypes.createPythonType2XMLType(elements[e].__name__)) 212 | elem += elems+'\n' 213 | elem += '\n' 214 | elem += '\n'%(name,name) 215 | elif issubclass(elements,xmltypes.PrimitiveType): 216 | elem = elements.createElement(name)+'\n' 217 | 218 | return elem 219 | -------------------------------------------------------------------------------- /tornadows/soaphandler.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | """ Implementation of soaphandler for webservices API 0.9.4.2 (Beta) """ 18 | 19 | import tornado.httpserver 20 | import tornado.web 21 | import xml.dom.minidom 22 | import string 23 | import inspect 24 | from tornado.options import options 25 | from tornadows import soap 26 | from tornadows import xmltypes 27 | from tornadows import complextypes 28 | from tornadows import wsdl 29 | 30 | """ Global variable. If you want use your own wsdl file """ 31 | wsdl_path = None 32 | 33 | def webservice(*params,**kwparams): 34 | """ Decorator method for web services operators """ 35 | def method(f): 36 | _input = None 37 | _output = None 38 | _inputArray = False 39 | _outputArray = False 40 | _args = None 41 | if len(kwparams): 42 | _params = kwparams['_params'] 43 | if inspect.isclass(_params) and issubclass(_params,complextypes.ComplexType): 44 | _args = inspect.getargspec(f).args[1:] 45 | _input = _params 46 | elif isinstance(_params,list): 47 | _args = inspect.getargspec(f).args[1:] 48 | _input = {} 49 | i = 0 50 | for arg in _args: 51 | _input[arg] = _params[i] 52 | i+=1 53 | else: 54 | _args = inspect.getargspec(f).args[1:] 55 | _input = {} 56 | for arg in _args: 57 | _input[arg] = _params 58 | if isinstance(_params,xmltypes.Array): 59 | _inputArray = True 60 | 61 | _returns = kwparams['_returns'] 62 | if isinstance(_returns,xmltypes.Array): 63 | _output = _returns 64 | _outputArray = True 65 | elif isinstance(_returns,list) or issubclass(_returns,xmltypes.PrimitiveType) or issubclass(_returns,complextypes.ComplexType): 66 | _output = _returns 67 | else: 68 | _output = _returns 69 | def operation(*args,**kwargs): 70 | return f(*args,**kwargs) 71 | 72 | operation.__name__ = f.__name__ 73 | operation._is_operation = True 74 | operation._args = _args 75 | operation._input = _input 76 | operation._output = _output 77 | operation._operation = f.__name__ 78 | operation._inputArray = _inputArray 79 | operation._outputArray = _outputArray 80 | 81 | return operation 82 | return method 83 | 84 | def soapfault(faultstring): 85 | """ Method for generate a soap fault 86 | soapfault() return a SoapMessage() object with a message 87 | for Soap Envelope 88 | """ 89 | fault = soap.SoapMessage() 90 | faultmsg = '\n' 91 | faultmsg += '\n' 92 | faultmsg += '%s\n'%faultstring 93 | faultmsg += '\n' 94 | fault.setBody(xml.dom.minidom.parseString(faultmsg)) 95 | return fault 96 | 97 | class SoapHandler(tornado.web.RequestHandler): 98 | """ This subclass extends tornado.web.RequestHandler class, defining the 99 | methods get() and post() for handle a soap message (request and response). 100 | """ 101 | def get(self): 102 | """ Method get() returned the WSDL. If wsdl_path is null, the 103 | WSDL is generated dinamically. 104 | """ 105 | if hasattr(options,'wsdl_hostname') and type(options.wsdl_hostname) is str: 106 | address = options.wsdl_hostname 107 | else: 108 | address = getattr(self, 'targetns_address',tornado.httpserver.socket.gethostbyname(tornado.httpserver.socket.gethostname())) 109 | 110 | port = 80 # if you are using the port 80 111 | if len(self.request.headers['Host'].split(':')) >= 2: 112 | port = self.request.headers['Host'].split(':')[1] 113 | wsdl_nameservice = self.request.uri.replace('/','').replace('?wsdl','').replace('?WSDL','') 114 | wsdl_input = None 115 | wsdl_output = None 116 | wsdl_operation = None 117 | wsdl_args = None 118 | wsdl_methods = [] 119 | 120 | for operations in dir(self): 121 | operation = getattr(self,operations) 122 | if callable(operation) and hasattr(operation,'_input') and hasattr(operation,'_output') and hasattr(operation,'_operation') \ 123 | and hasattr(operation,'_args') and hasattr(operation,'_is_operation'): 124 | wsdl_input = getattr(operation,'_input') 125 | wsdl_output = getattr(operation,'_output') 126 | wsdl_operation = getattr(operation,'_operation') 127 | wsdl_args = getattr(operation,'_args') 128 | wsdl_data = {'args':wsdl_args,'input':('params',wsdl_input),'output':('returns',wsdl_output),'operation':wsdl_operation} 129 | wsdl_methods.append(wsdl_data) 130 | 131 | wsdl_targetns = 'http://%s:%s/%s'%(address,port,wsdl_nameservice) 132 | wsdl_location = 'http://%s:%s/%s'%(address,port,wsdl_nameservice) 133 | query = self.request.query 134 | self.set_header('Content-Type','application/xml; charset=UTF-8') 135 | if query.upper() == 'WSDL': 136 | if wsdl_path == None: 137 | wsdlfile = wsdl.Wsdl(nameservice=wsdl_nameservice, 138 | targetNamespace=wsdl_targetns, 139 | methods=wsdl_methods, 140 | location=wsdl_location) 141 | 142 | self.finish(wsdlfile.createWsdl().toxml()) 143 | else: 144 | fd = open(str(wsdl_path),'r') 145 | xmlWSDL = '' 146 | for line in fd: 147 | xmlWSDL += line 148 | fd.close() 149 | self.finish(xmlWSDL) 150 | 151 | @tornado.web.asynchronous 152 | def post(self): 153 | """ Method post() to process of requests and responses SOAP messages """ 154 | def done(response): 155 | soapmsg = response.getSoap().toxml() 156 | self.write(soapmsg) 157 | self.finish() 158 | try: 159 | self._request = self._parseSoap(self.request.body) 160 | soapaction = self.request.headers['SOAPAction'].replace('"','') 161 | self.set_header('Content-Type','text/xml') 162 | for operations in dir(self): 163 | operation = getattr(self,operations) 164 | method = '' 165 | if callable(operation) and hasattr(operation,'_is_operation'): 166 | num_methods = self._countOperations() 167 | if hasattr(operation,'_operation') and soapaction.endswith(getattr(operation,'_operation')) and num_methods > 1: 168 | method = getattr(operation,'_operation') 169 | self._executeOperation(operation, done, method=method) 170 | break 171 | elif num_methods == 1: 172 | self._executeOperation(operation, done, method='') 173 | break 174 | except Exception as detail: 175 | fault = soapfault('Error in web service : %s'%detail) 176 | self.write(fault.getSoap().toxml()) 177 | 178 | def _countOperations(self): 179 | """ Private method that counts the operations on the web services """ 180 | c = 0 181 | for operations in dir(self): 182 | operation = getattr(self,operations) 183 | if callable(operation) and hasattr(operation,'_is_operation'): 184 | c += 1 185 | return c 186 | 187 | def _executeOperation(self, operation, callback, method=''): 188 | """ Private method that executes operations of web service """ 189 | params = [] 190 | response = None 191 | res = None 192 | typesinput = getattr(operation,'_input') 193 | args = getattr(operation,'_args') 194 | if inspect.isclass(typesinput) and issubclass(typesinput,complextypes.ComplexType): 195 | obj = self._parseComplexType(typesinput,self._request.getBody()[0],method=method) 196 | response = operation(obj) 197 | elif hasattr(operation,'_inputArray') and getattr(operation,'_inputArray'): 198 | params = self._parseParams(self._request.getBody()[0],typesinput,args) 199 | response = operation(params) 200 | else: 201 | params = self._parseParams(self._request.getBody()[0],typesinput,args) 202 | response = operation(*params) 203 | is_array = None 204 | if hasattr(operation,'_outputArray') and getattr(operation,'_outputArray'): 205 | is_array = getattr(operation,'_outputArray') 206 | def done(response): 207 | response = response() 208 | typesoutput = getattr(operation, '_output') 209 | if inspect.isclass(typesoutput) and issubclass(typesoutput,complextypes.ComplexType): 210 | res = self._createReturnsComplexType(response, method=method) 211 | else: 212 | res = self._createReturns(response,is_array) 213 | callback(res) 214 | 215 | if isinstance(response, tornado.concurrent.Future): 216 | response.add_done_callback(lambda p: done(response.result)) 217 | else: 218 | done(lambda: response) 219 | return res 220 | 221 | def _parseSoap(self,xmldoc): 222 | """ Private method parse a message soap from a xmldoc like string 223 | _parseSoap() return a soap.SoapMessage(). 224 | """ 225 | xmldoc = bytes.decode(xmldoc) 226 | xmldoc = xmldoc.replace('\n',' ').replace('\t',' ').replace('\r',' ') 227 | document = xml.dom.minidom.parseString(xmldoc) 228 | prefix = document.documentElement.prefix 229 | namespace = document.documentElement.namespaceURI 230 | 231 | header = self._getElementFromMessage('Header',document) 232 | body = self._getElementFromMessage('Body',document) 233 | 234 | header_elements = self._parseXML(header) 235 | body_elements = self._parseXML(body) 236 | 237 | soapMsg = soap.SoapMessage() 238 | for h in header_elements: 239 | soapMsg.setHeader(h) 240 | for b in body_elements: 241 | soapMsg.setBody(b) 242 | return soapMsg 243 | 244 | def _getElementFromMessage(self,name,document): 245 | """ Private method to search and return elements from XML """ 246 | list_of_elements = [] 247 | for e in document.documentElement.childNodes: 248 | if e.nodeType == e.ELEMENT_NODE and e.nodeName.count(name) >= 1: 249 | list_of_elements.append(e) 250 | return list_of_elements 251 | 252 | def _parseXML(self,elements): 253 | """ Private method parse and digest the xml.dom.minidom.Element 254 | finding the childs of Header and Body from soap message. 255 | Return a list object with all of child Elements. 256 | """ 257 | elem_list = [] 258 | if len(elements) <= 0: 259 | return elem_list 260 | if elements[0].childNodes.length <= 0: 261 | return elem_list 262 | for element in elements[0].childNodes: 263 | if element.nodeType == element.ELEMENT_NODE: 264 | prefix = element.prefix 265 | namespace = element.namespaceURI 266 | if prefix != None and namespace != None: 267 | element.setAttribute('xmlns:'+prefix,namespace) 268 | else: 269 | element.setAttribute('xmlns:xsd',"http://www.w3.org/2001/XMLSchema") 270 | element.setAttribute('xmlns:xsi',"http://www.w3.org/2001/XMLSchema-instance") 271 | elem_list.append(xml.dom.minidom.parseString(element.toxml())) 272 | return elem_list 273 | 274 | def _parseComplexType(self,complex,xmld,method=''): 275 | """ Private method for generate an instance of class nameclass. """ 276 | xsdd = '' 277 | xsdd += complex.toXSD(method=method,ltype=[]) 278 | xsdd += '' 279 | xsd = xml.dom.minidom.parseString(xsdd) 280 | obj = complextypes.xml2object(xmld.toxml(),xsd,complex,method=method) 281 | 282 | return obj 283 | 284 | def _parseParams(self,elements,types=None,args=None): 285 | """ Private method to parse a Body element of SOAP Envelope and extract 286 | the values of the request document like parameters for the soapmethod, 287 | this method return a list values of parameters. 288 | """ 289 | values = [] 290 | for tagname in args: 291 | type = types[tagname] 292 | values += self._findValues(tagname,type,elements) 293 | return values 294 | 295 | def _findValues(self,name,type,xml): 296 | """ Private method to find the values of elements in the XML of input """ 297 | elems = xml.getElementsByTagName(name) 298 | values = [] 299 | for e in elems: 300 | if e.hasChildNodes and len(e.childNodes) > 0: 301 | v = None 302 | if inspect.isclass(type) and (issubclass(type,xmltypes.PrimitiveType) or isinstance(type,xmltypes.Array)): 303 | v = type.genType(e.childNodes[0].nodeValue) 304 | elif hasattr(type,'__name__') and (not issubclass(type,xmltypes.PrimitiveType) or not isinstance(type,xmltypes.Array)): 305 | v = complextypes.convert(type.__name__,e.childNodes[0].nodeValue) 306 | values.append(v) 307 | else: 308 | values.append(None) 309 | return values 310 | 311 | def _createReturnsComplexType(self,result,method=''): 312 | """ Private method to generate the xml document with the response. 313 | Return an SoapMessage() with XML document. 314 | """ 315 | response = xml.dom.minidom.parseString(result.toXML(method=method)) 316 | 317 | soapResponse = soap.SoapMessage() 318 | soapResponse.setBody(response) 319 | return soapResponse 320 | 321 | def _createReturns(self,result,is_array): 322 | """ Private method to generate the xml document with the response. 323 | Return an SoapMessage(). 324 | """ 325 | xmlresponse = '' 326 | if isinstance(result,list): 327 | xmlresponse = '\n' 328 | i = 1 329 | for r in result: 330 | if is_array == True: 331 | xmlresponse += '%s\n'%str(r) 332 | else: 333 | xmlresponse += '%s\n'%(i,str(r),i) 334 | i+=1 335 | xmlresponse += '\n' 336 | else: 337 | xmlresponse = '%s\n'%str(result) 338 | 339 | response = xml.dom.minidom.parseString(xmlresponse) 340 | 341 | soapResponse = soap.SoapMessage() 342 | soapResponse.setBody(response) 343 | return soapResponse 344 | -------------------------------------------------------------------------------- /demos/README.md: -------------------------------------------------------------------------------- 1 | This document describes how to run the demos web services. 2 | 3 | Running the DemoService.py example: 4 | ----------------------------------- 5 | 6 | Running: 7 | 8 | $ python DemoServices.py 9 | 10 | The example display four separate services. 11 | 12 | EchoService (http://localhost:8080/EchoService?wsdl): 13 | 14 | For an input string responds with an echo message. 15 | 16 | CountService (http://localhost:8080/CountService?wsdl): 17 | 18 | The operation receives as input an python list an returns the numbers of 19 | elements in the list. 20 | 21 | This service used _params=xmltypes.Array(xmltypes.String) like input, 22 | what mean his input parameter is a array of string elements. 23 | 24 | xmltypes.Array(xmltypes.String) create an xml element with maxOccurs 25 | property with the value "unbounded" for wsdl. 26 | 27 | DivService (http://localhost:8080/DivService?wsdl): 28 | 29 | The operation receives as input two float numbers and returns the result 30 | of the deivision of this numbers. 31 | 32 | Finally, FibonacciService (http://localhost:8080/FibonacciService?wsdl): 33 | 34 | Returns a list with the fibonacci's numbers. 35 | 36 | This services return an _return=xmltypes.Array(xmltypes.Integer) what 37 | mean the operation of service return an list of integer elements. 38 | 39 | xmltypes.Array(xmltypes.Integer), create an xml element with maxOccurs 40 | property with the value "unbounded" for wsdl. 41 | 42 | You can probe this services with SoapUI or creating a web services client with 43 | your favorite programming language. 44 | 45 | Running the DemoServiceHostname.py example: 46 | ------------------------------------------- 47 | 48 | The results are the same as above showed, except that in this demo you can 49 | set the hostname to be answered in the WSDL. It's useful when you need to 50 | run the services in distributed servers architecture. It's gonna be useful 51 | operating in a round-robin or reverse proxy configuration. 52 | 53 | Running the DemoService2.py example: 54 | ------------------------------------ 55 | 56 | Running: 57 | 58 | $ python DemoServices2.py 59 | 60 | The example display the same four separate services of DemoService.py. 61 | The differences are in the types. Here we used python types (int, str, 62 | float, etc.). See the code. 63 | 64 | Running the ProducService.py example: 65 | ------------------------------------- 66 | 67 | Running: 68 | 69 | $ python ProductService.py 70 | 71 | ProductService (http://localhost:8080/ProductService?wsdl): 72 | 73 | This web service uses complextypes by transforming a python class in an 74 | xml schema within the definition of the schemas of WSDL. 75 | 76 | For example: 77 | 78 | from tornadows import complextypes 79 | 80 | class Product(complextypes.ComplexType): 81 | id = complextypes.IntegerPropert() 82 | name = complextypes.StringProperty() 83 | price = complextypes.FloatProperty() 84 | stock = complextypes.IntegerProperty() 85 | 86 | The class is tranformed into the following xml schema. 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | In the WSDL, xml schema is seen as follows. 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | In the example, the SOAP envelope is seen as follows. 114 | 115 | The request is: 116 | 117 | 118 | 119 | 120 | 121 | 1 122 | 123 | 124 | 125 | 126 | The response of the web service is: 127 | 128 | 129 | 130 | 131 | 132 | 1 133 | COMPUTER 134 | 1000.5 135 | 100 136 | 137 | 138 | 139 | 140 | You can use the SoapUI for testing. See the code in the ProductService.py 141 | 142 | Running the ProducListService.py example: 143 | ----------------------------------------- 144 | 145 | Running: 146 | 147 | $ python ProductListService.py 148 | 149 | ProductListService (http://localhost:8080/ProductListService?wsdl): 150 | 151 | The web service implements an operation to retrieve a list of products. 152 | Python types are used to define the attributes of the class; and python 153 | list to store a set of Product. 154 | 155 | Input is a class that represents the input parameter (request) for the 156 | web service: 157 | 158 | idList : is a int python type. 159 | 160 | Product is a class that represents Product data structure. 161 | 162 | id : Is a int python type. Is the product id 163 | name : Is a str python type. Is the name of product. 164 | price : Is a float python type. Is the price of product. 165 | stock : Is a int python type. Is the stock of product. 166 | 167 | List is a class that represents the response of the web services. 168 | 169 | idList : IS a int python type. Is a id for the list. 170 | product : Is a python list with a set of product (Product class). 171 | 172 | Running the ProducListService2.py example: 173 | ------------------------------------------ 174 | 175 | Running: 176 | 177 | $ python ProductListService2.py 178 | 179 | ProductListService (http://localhost:8080/ProductListService2?wsdl): 180 | 181 | The web service implements an operation to retrieve a list of products. 182 | Python types are used to define the attributes of the class; and python 183 | list to store a set of Product. 184 | 185 | Product is a class that represents Product data structure. 186 | 187 | id : Is a int python type. Is the product id 188 | name : Is a str python type. Is the name of product. 189 | price : Is a float python type. Is the price of product. 190 | stock : Is a int python type. Is the stock of product. 191 | 192 | List is a class that represents the response of the web services. 193 | This is a list of Product. 194 | 195 | product : Is a python list with a set of product (Product class). 196 | 197 | The operation have not input parameters. 198 | 199 | Running the UserRolesService.py example: 200 | ---------------------------------------- 201 | 202 | Running: 203 | 204 | $ python UserRolesService.py 205 | 206 | UserRolesService (http://localhost:8080/UserRolesService?wsdl): 207 | 208 | This web service uses python list into xml complextypes. This allows create data structures more 209 | complex. 210 | 211 | The service receive a idlist (integer) and returns a list of users with username (str) and another 212 | python list with roles. 213 | 214 | For example: 215 | 216 | from tornadows import complextypes 217 | 218 | class User(complextypes.ComplexType): 219 | username = str 220 | roles = [str] 221 | 222 | class ListOfUser(complextypes.ComplexType): 223 | idlist = int 224 | list = [User] 225 | 226 | The classes are tranformed into the following xml schema. 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 | Python lists are represented like xml elements with the maxOccurs attribute unbounded. 253 | 254 | The soap request is: 255 | 256 | 257 | 258 | 259 | 260 | 9010 261 | 262 | 263 | 264 | 265 | The soap response is: 266 | 267 | 268 | 269 | 270 | 271 | 9010 272 | 273 | ceo 274 | admin 275 | steve 276 | 277 | 278 | developer 279 | billy 280 | 281 | 282 | 283 | 284 | 285 | Running the CertService.py example: 286 | ---------------------------------------- 287 | 288 | Running: 289 | 290 | $ python CertService.py 291 | 292 | CertService (http://localhost:8080/CertService?wsdl): 293 | 294 | This web service returns an certificate of birth, this service uses the module datetime 295 | of python. 296 | 297 | The service receives an idperson parameter like input request, and returns an xml with 298 | the information of the "certificate": 299 | 300 | 301 | 1973-12-11 302 | 2011-10-04T00:25:55 303 | 1 304 | true 305 | Steve J 306 | 1 307 | 308 | 309 | See the code of CertService.py for more details. 310 | 311 | Running the RegisterService.py example: 312 | ---------------------------------------- 313 | 314 | Running: 315 | 316 | $ python RegisterService.py 317 | 318 | 1.- RegisterService (http://localhost:8080/RegisterService?wsdl): 319 | 320 | This example is similar to CertService.py. 321 | 322 | The example uses a RegisterRequest like input to the web service. 323 | The operation register() of the web service simulates the service of register. 324 | The operation response a RegisterResponse with information of the register. 325 | 326 | See the code of RegisterService.py for more details. 327 | 328 | 329 | Running the CurrentTempService.py example: 330 | ------------------------------------------ 331 | 332 | Running: 333 | 334 | $ python CurrentTempService.py 335 | 336 | CurrentTempService (http://localhost:8080/CurrentTempService?wsdl): 337 | 338 | This example simulates a service that gives the current temperature (in a unknown location). 339 | The operation or method getTemperature() does not receive any parameters. 340 | The operation ever return the same temperature (29). 341 | 342 | See the code of CurrentTempService.py for more details. 343 | 344 | Running the HelloWorldService.py example: 345 | ----------------------------------------- 346 | 347 | Running: 348 | 349 | $ python HelloWorldService.py 350 | 351 | HelloWorldService (http://localhost:8080/HelloWorldService?wsdl): 352 | 353 | This service is the classic hello world!!!. The operation sayHello() does not receive any parameters 354 | this method just return a string with the classic "Hello World!!!". 355 | 356 | See the code of HelloWorldService.py for more details. 357 | 358 | Running the HelloWorldService2.py example: 359 | ------------------------------------------ 360 | 361 | Running: 362 | 363 | $ python HelloWorldService2.py 364 | 365 | HelloWorldService2 (http://localhost:8080/HelloWorldService2?wsdl): 366 | 367 | This service is the classic hello world. The operation sayHello() does not receive any parameters 368 | this method just return a string with the classic hello world in a array or list in python 369 | ['Hello','World']. 370 | 371 | See the code of HelloWorldService2.py for more details. 372 | 373 | Running the MathService.py example (a new feature): 374 | --------------------------------------------------- 375 | 376 | Running: 377 | 378 | $ python MathService.py 379 | 380 | MathService (http://localhost:8080/MathService?wsdl): 381 | 382 | This service displays four math operations in a single service (python class): 383 | addition (add), subtraction (sub), multiplication (mult) and division (div). 384 | 385 | See the code of MathService.py for more details. 386 | 387 | This example made use of a new feature of the API, allow multiple operations 388 | on the service, throught the use of @webservice decorator. 389 | 390 | Running the RepositoryService.py example (a new feature): 391 | --------------------------------------------------------- 392 | 393 | Running: 394 | 395 | $ python RepositoryService.py 396 | 397 | RepositoryService (http://localhost:8080/RepositoryService?wsdl): 398 | 399 | This service display two operations to save a document in a repository and 400 | find a document from the same repository. 401 | 402 | See the code of RepositoryService.py for more details. 403 | 404 | The service uses the ComplexTypes to define the documents stored in the 405 | repository. 406 | 407 | We define two subclass of ComplexTypes: 408 | 409 | Document class represents a document. 410 | Message class defines message and transports documents. 411 | 412 | To store the documents, the service uses python dictionaries. 413 | -------------------------------------------------------------------------------- /tornadows/complextypes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2011 Rodrigo Ancavil del Pino 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | """ Implementation of module with classes and functions for transform python 18 | classes in xml schema: 19 | 20 | See the next example: 21 | 22 | from tornadows.complextypes import ComplexType, StringProperty, IntegerProperty 23 | 24 | class Person(ComplexType): 25 | name = StringProperty() 26 | age = IntegerProperty() 27 | 28 | or you can use some python types 29 | 30 | class Person(ComplexType): 31 | name = str 32 | age = int 33 | 34 | is equivalent to: 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | """ 44 | 45 | import tornadows.xmltypes 46 | import xml.dom.minidom 47 | import inspect 48 | from datetime import date, datetime, time 49 | 50 | class Property: 51 | """ Class base for definition of properties of the attributes of a python class """ 52 | pass 53 | 54 | class IntegerProperty(Property): 55 | """ Class for definitions of Integer Property """ 56 | def __init__(self): 57 | self.type = tornadows.xmltypes.Integer 58 | self.value = None 59 | 60 | class DecimalProperty(Property): 61 | """ Class for definitions of Decimal Property """ 62 | def __init__(self): 63 | self.type = tornadows.xmltypes.Decimal 64 | self.value = None 65 | 66 | class DoubleProperty(Property): 67 | """ Class for definitions of Double Property """ 68 | def __init__(self): 69 | self.type = tornadows.xmltypes.Double 70 | self.value = None 71 | 72 | class FloatProperty(Property): 73 | """ Class for definitions of Float Property """ 74 | def __init__(self): 75 | self.type = tornadows.xmltypes.Float 76 | self.value = None 77 | 78 | class DurationProperty(Property): 79 | """ Class for definitions of Duration Property """ 80 | def __init__(self): 81 | self.type = tornadows.xmltypes.Duration 82 | self.value = None 83 | 84 | class DateProperty(Property): 85 | """ Class for definitions of Date Property """ 86 | def __init__(self): 87 | self.type = tornadows.xmltypes.Date 88 | self.value = None 89 | 90 | class TimeProperty(Property): 91 | """ Class for definitions of Time Property """ 92 | def __init__(self): 93 | self.type = tornadows.xmltypes.Time 94 | self.value = None 95 | 96 | class DateTimeProperty(Property): 97 | """ Class for definitions of DateTime Property """ 98 | def __init__(self): 99 | self.type = tornadows.xmltypes.DateTime 100 | self.value = None 101 | 102 | class StringProperty(Property): 103 | """ Class for definitions of String Property """ 104 | def __init__(self): 105 | self.type = tornadows.xmltypes.String 106 | self.value = None 107 | 108 | class BooleanProperty(Property): 109 | """ Class for definitions of Boolean Property """ 110 | def __init__(self): 111 | self.type = tornadows.xmltypes.Boolean 112 | self.value = None 113 | 114 | class ArrayProperty(list): 115 | """ For create a list of classes """ 116 | def __init__(self, object, minOccurs = 1, maxOccurs=None, data=[]): 117 | list.__init__(self,data) 118 | self._minOccurs = minOccurs 119 | self._maxOccurs = maxOccurs 120 | self._object = object 121 | self.append(self._object) 122 | 123 | def toXSD(self,namespace='xsd',nameelement=None): 124 | """ Create xml complex type for ArrayProperty """ 125 | xsd = self._object.toXSD() 126 | if self._maxOccurs == None: 127 | xsd += '<%s:element name="%s" type="tns:%s" minOccurs="%s"/>'%(namespace,nameelement,self._object.getName(),self._minOccurs) 128 | elif self._maxOccurs != None: 129 | xsd += '<%s:element name="%s" type="tns:%s" minOccurs="%s" maxOccurs="%s"/>'%(namespace,nameelement,self._object.getName(),str(self._minOccurs),str(self._maxOccurs)) 130 | return xsd 131 | 132 | class ComplexType(object): 133 | """ Base class for definitions of python class like xml document and schema: 134 | 135 | from tornadows.complextypes import ComplexType,StringProperty, IntegerProperty 136 | 137 | class Person(ComplexType): 138 | name = StringProperty 139 | age = IntegerProperty 140 | 141 | if __name__ == '__main__': 142 | print 'XML Schema : ' 143 | print(Person.toXSD()) 144 | 145 | p = Person() 146 | p.name.value = 'Steve J' 147 | p.age.value = 38 148 | 149 | print('XML Document : ') 150 | print(p.toXML()) 151 | 152 | or you if you want to use some python types (int, str, float, bool) 153 | 154 | from tornadows.complextypes import ComplexType 155 | 156 | class Person(ComplexType): 157 | name = str 158 | age = int 159 | 160 | if __name__ == '__main__': 161 | print('XML Schema : ') 162 | print(Person.toXSD()) 163 | 164 | p = Person() 165 | p.name.value = 'Steve J' 166 | p.age.value = 38 167 | 168 | print('XML Document : ') 169 | print(p.toXML()) 170 | 171 | """ 172 | def __init__(self): 173 | """ Class constructor for ComplexType """ 174 | default_attr = dir(type('default',(object,),{})) 175 | for attr in self.__class__.__dict__.keys(): 176 | if default_attr.count(attr) > 0 or callable(attr): 177 | continue 178 | else: 179 | element = self.__class__.__dict__[attr] 180 | typeobj = self._createAttributeType(element) 181 | setattr(self,attr,typeobj) 182 | 183 | def toXML(self,name=None,method=''): 184 | """ Method that creates the XML document for the instance of python class. 185 | Return a string with the xml document. 186 | """ 187 | nameroot = None 188 | 189 | if name == None: 190 | nameroot = self.__class__.__name__ 191 | else: 192 | nameroot = name 193 | nameroot += method 194 | 195 | xml = '<%s>'%nameroot 196 | default_attr = dir(type('default',(object,),{})) 197 | for key in dir(self): 198 | if default_attr.count(key) > 0: 199 | continue 200 | element = findElementFromDict(self.__dict__,key) 201 | if element == None: 202 | continue 203 | if isinstance(element,list): 204 | for e in element: 205 | if isinstance(e,ComplexType): 206 | xml += e.toXML(name=key) 207 | else: 208 | xml += '<%s>%s'%(key,e,key) 209 | elif isinstance(element,Property): 210 | xml += '<%s>%s'%(key,element.value,key) 211 | elif isinstance(element,ComplexType): 212 | xml += element.toXML(name=key) 213 | else: 214 | xml += '<%s>%s'%(key,convert(type(element).__name__,element),key) 215 | xml += ''%nameroot 216 | return str(xml) 217 | 218 | @classmethod 219 | def toXSD(cls,xmlns='http://www.w3.org/2001/XMLSchema',namespace='xsd',method='', ltype=[]): 220 | """ Class method that creates the XSD document for the python class. 221 | Return a string with the xml schema. 222 | """ 223 | name = cls.__name__ 224 | xsd = cls._generateXSD(ltype=ltype) 225 | return xsd 226 | 227 | @classmethod 228 | def _generateXSD(cls,xmlns='http://www.w3.org/2001/XMLSchema',namespace='xsd', ltype=[]): 229 | """ Class method for get the xml schema with the document definition. 230 | Return a string with the xsd document. 231 | """ 232 | default_attr = dir(type('default',(object,),{})) 233 | name = cls.__name__ 234 | xsd = '<%s:complexType name="%s" xmlns:%s="%s">'%(namespace,name,namespace,xmlns) 235 | xsd += '<%s:sequence>'%namespace 236 | complextype = [] 237 | 238 | for key in dir(cls): 239 | if default_attr.count(key) > 0: 240 | continue 241 | element = findElementFromDict(cls.__dict__,key) 242 | if element == None: 243 | continue 244 | if isinstance(element,Property): 245 | xsd += element.type.createElement(str(key)) 246 | 247 | elif isinstance(element,ComplexType): 248 | nameinstance = key 249 | 250 | if ltype.count(self._elementInput.getName()) == 0: 251 | ltype.append(self._elementInput.getName()) 252 | complextype.append(element._generateXSD(ltype=[])) 253 | 254 | xsd += '<%s:element name="%s" type="tns:%s"/>'%(namespace,nameinstance,element.getName()) 255 | elif inspect.isclass(element) and issubclass(element,ComplexType): 256 | nameinstance = key 257 | 258 | if ltype.count(element.getName()) == 0: 259 | ltype.append(element.getName()) 260 | complextype.append(element._generateXSD(ltype=[])) 261 | 262 | xsd += '<%s:element name="%s" type="tns:%s"/>'%(namespace,nameinstance,element.getName()) 263 | elif isinstance(element,ArrayProperty): 264 | if isinstance(element[0],ComplexType) or issubclass(element[0],ComplexType): 265 | complextype.append(element[0]._generateXSD(ltype=[])) 266 | xsd += '<%s:element name="%s" type="tns:%s" maxOccurs="unbounded"/>'%(namespace,key,element[0].__name__) 267 | else: 268 | typeelement = createPythonType2XMLType(element[0].__name__) 269 | xsd += '<%s:element name="%s" type="%s:%s" maxOccurs="unbounded"/>'%(namespace,key,namespace,typeelement) 270 | 271 | elif isinstance(element,list): 272 | if isinstance(element[0],ComplexType) or issubclass(element[0],ComplexType): 273 | 274 | if ltype.count(element[0].__name__) == 0: 275 | ltype.append(element[0].__name__) 276 | complextype.append(element[0]._generateXSD(ltype=[])) 277 | 278 | xsd += '<%s:element name="%s" type="tns:%s" maxOccurs="unbounded"/>'%(namespace,key,element[0].__name__) 279 | else: 280 | typeelement = createPythonType2XMLType(element[0].__name__) 281 | xsd += '<%s:element name="%s" type="%s:%s" maxOccurs="unbounded"/>'%(namespace,key,namespace,typeelement) 282 | elif hasattr(element,'__name__'): 283 | typeelement = createPythonType2XMLType(element.__name__) 284 | xsd += '<%s:element name="%s" type="%s:%s"/>'%(namespace,str(key),namespace,typeelement) 285 | 286 | xsd += ''%namespace 287 | xsd += ''%namespace 288 | 289 | if len(complextype) > 0: 290 | for ct in complextype: 291 | xsd += ct 292 | 293 | return xsd 294 | 295 | @classmethod 296 | def getName(cls): 297 | """ Class method return the name of the class """ 298 | return cls.__name__ 299 | 300 | @classmethod 301 | def _createAttributeType(self,element): 302 | """ Class method to create the types of the attributes of a ComplexType """ 303 | if isinstance(element,list): 304 | return list() 305 | elif isinstance(element,IntegerProperty): 306 | return IntegerProperty() 307 | elif isinstance(element,DecimalProperty): 308 | return DecimalProperty() 309 | elif isinstance(element,DoubleProperty): 310 | return DoubleProperty() 311 | elif isinstance(element,FloatProperty): 312 | return FloatProperty() 313 | elif isinstance(element,DurationProperty): 314 | return DurationProperty() 315 | elif isinstance(element,DateProperty): 316 | return DateProperty() 317 | elif isinstance(element,TimeProperty): 318 | return TimeProperty() 319 | elif isinstance(element,DateTimeProperty): 320 | return DateTimeProperty() 321 | elif isinstance(element,StringProperty): 322 | return StringProperty() 323 | elif isinstance(element,BooleanProperty): 324 | return BooleanProperty() 325 | elif issubclass(element,ComplexType): 326 | return element() 327 | else: 328 | if element.__name__ == 'int': 329 | return int 330 | elif element.__name__ == 'decimal': 331 | return float 332 | elif element.__name__ == 'double': 333 | return float 334 | elif element.__name__ == 'float': 335 | return float 336 | elif element.__name__ == 'duration': 337 | return str 338 | elif element.__name__ == 'date': 339 | return date 340 | elif element.__name__ == 'time': 341 | return time 342 | elif element.__name__ == 'dateTime': 343 | return datetime 344 | elif element.__name__ == 'str': 345 | return str 346 | elif element.__name__ == 'bool': 347 | return bool 348 | 349 | def xml2object(xml,xsd,complex,method=''): 350 | """ Function that converts a XML document in a instance of a python class """ 351 | namecls = complex.getName() 352 | types = xsd2dict(xsd) 353 | lst = xml2list(xml,namecls,types,method=method) 354 | tps = cls2dict(complex) 355 | obj = generateOBJ(lst,namecls,tps) 356 | return obj 357 | 358 | def cls2dict(complex): 359 | """ Function that creates a dictionary from a ComplexType class with the attributes and types """ 360 | default_attr = dir(type('default',(object,),{})) 361 | dct = {} 362 | for attr in dir(complex): 363 | if default_attr.count(attr) > 0 or callable(attr): 364 | continue 365 | else: 366 | elem = findElementFromDict(complex.__dict__,attr) 367 | if elem != None: 368 | dct[attr] = elem 369 | return dct 370 | 371 | def xsd2dict(xsd,namespace='xsd'): 372 | """ Function that creates a dictionary from a xml schema with the type of element """ 373 | types = ['xsd:integer','xsd:decimal','xsd:double','xsd:float','xsd:duration','xsd:date','xsd:time','xsd:dateTime','xsd:string','xsd:boolean'] 374 | dct = {} 375 | 376 | element = '%s:element'%namespace 377 | elems = xsd.getElementsByTagName(element) 378 | for e in elems: 379 | val = 'complexType' 380 | typ = str(e.getAttribute('type')) 381 | lst = e.hasAttribute('maxOccurs') 382 | if types.count(typ) > 0: 383 | val = 'element' 384 | dct[str(e.getAttribute('name'))] = (val,typ,lst) 385 | return dct 386 | 387 | def xml2list(xmldoc,name,types,method=''): 388 | """ Function that creates a list from xml documento with a tuple element and value """ 389 | name = name+method 390 | 391 | x = xml.dom.minidom.parseString(xmldoc) 392 | c = None 393 | if x.documentElement.prefix != None: 394 | c = x.getElementsByTagName(x.documentElement.prefix+':'+name) 395 | else: 396 | c = x.getElementsByTagName(name) 397 | attrs = genattr(c) 398 | lst = [] 399 | for a in attrs: 400 | t = types[a.nodeName] 401 | typ = t[0] 402 | typxml = t[1] 403 | isarray = t[2] 404 | if typ == 'complexType' or typ == 'list': 405 | l = xml2list(a.toxml(),str(a.nodeName),types) 406 | lst.append((str(a.nodeName),l,isarray)) 407 | else: 408 | val = None 409 | if len(a.childNodes) > 0: 410 | val = convert(typxml,str(a.childNodes[0].nodeValue)) 411 | # Convert str to bool. 412 | if val == 'true': 413 | val = True 414 | elif val == 'false': 415 | val = False 416 | lst.append((str(a.nodeName),val,isarray)) 417 | return lst 418 | 419 | def generateOBJ(d,namecls,types): 420 | """ Function that creates a object from a xml document """ 421 | dct = {} 422 | lst = [] 423 | for a in d: 424 | name = a[0] 425 | value = a[1] 426 | isarray = a[2] 427 | if isinstance(value,list): 428 | o = generateOBJ(value,name,types) 429 | if isarray: 430 | lst.append(o) 431 | dct[name] = lst 432 | else: 433 | dct[name] = o 434 | else: 435 | typ = findElementFromDict(types,name) 436 | if isinstance(typ,Property): 437 | dct[name] = createProperty(typ,value) 438 | else: 439 | dct[name] = value 440 | return type(namecls,(ComplexType,),dct) 441 | 442 | def createProperty(typ,value): 443 | """ Function that creates a Property class instance, with the value """ 444 | ct = None 445 | if isinstance(typ,IntegerProperty): 446 | ct = IntegerProperty() 447 | ct.value = tornadows.xmltypes.Integer.genType(value) 448 | elif isinstance(typ,DecimalProperty): 449 | ct = DecimalProperty() 450 | ct.value = tornadows.xmltypes.Decimal.genType(value) 451 | elif isinstance(typ,DoubleProperty): 452 | ct = DoubleProperty() 453 | ct.value = tornadows.xmltypes.Double.genType(value) 454 | elif isinstance(typ,FloatProperty): 455 | ct = FloatProperty() 456 | ct.value = tornadows.xmltypes.Float.genType(value) 457 | elif isinstance(typ,DurationProperty): 458 | ct = DurationProperty() 459 | ct.value = tornadows.xmltypes.Duration.genType(value) 460 | elif isinstance(typ,DateProperty): 461 | ct = DateProperty() 462 | ct.value = tornadows.xmltypes.Date.genType(value) 463 | elif isinstance(typ,TimeProperty): 464 | ct = TimeProperty() 465 | ct.value = tornadows.xmltypes.Time.genType(value) 466 | elif isinstance(typ,DateTimeProperty): 467 | ct = DateTimeProperty() 468 | ct.value = tornadows.xmltypes.DateTime.genType(value) 469 | elif isinstance(typ,StringProperty): 470 | ct = StringProperty() 471 | ct.value = tornadows.xmltypes.String.genType(value) 472 | elif isinstance(typ,BooleanProperty): 473 | ct = BooleanProperty() 474 | ct.value = tornadows.xmltypes.Boolean.genType(value) 475 | 476 | return ct 477 | 478 | def genattr(elems): 479 | """ Function that generates a list with the childnodes of a xml element """ 480 | d = [] 481 | for e in elems[0].childNodes: 482 | if e.nodeType == e.ELEMENT_NODE: 483 | d.append(e) 484 | return d 485 | 486 | def findElementFromDict(dictionary,key): 487 | """ Function to find a element into a dictionary for the key """ 488 | element = None 489 | try: 490 | element = dictionary[key] 491 | return element 492 | except KeyError: 493 | return None 494 | 495 | def convert(typeelement,value): 496 | """ Function that converts a value depending his type """ 497 | if typeelement == 'xsd:integer' or typeelement == 'int': 498 | return int(value) 499 | elif typeelement == 'xsd:decimal': 500 | return float(value) 501 | elif typeelement == 'xsd:double': 502 | return float(value) 503 | elif typeelement == 'xsd:float' or typeelement == 'float': 504 | return float(value) 505 | elif typeelement == 'xsd:duration': 506 | return str(value) 507 | elif typeelement == 'xsd:date' or typeelement == 'date': 508 | sdate = str(value).split('-') 509 | return date(int(sdate[0]),int(sdate[1]),int(sdate[2])) 510 | elif typeelement == 'xsd:time' or typeelement == 'time': 511 | stime = str(value).split(':') 512 | hour = stime[0] 513 | min = stime[1] 514 | seg = '00' 515 | if len(stime) >= 3: 516 | seg = stime[2].split('.')[0] 517 | return time(int(hour),int(min),int(seg)) 518 | elif typeelement == 'xsd:dateTime' or typeelement == 'datetime': 519 | sdatetime = str(value).replace('T','-').replace(' ','-').replace('+','-').split('-') 520 | year = sdatetime[0] 521 | mon = sdatetime[1] 522 | day = sdatetime[2] 523 | stime = sdatetime[3].split(':') 524 | hour = stime[0] 525 | min = stime[1] 526 | seg = '00' 527 | if len(stime) >= 3: 528 | seg = stime[2].split('.')[0] 529 | return datetime(int(year),int(mon),int(day),int(hour),int(min),int(seg)).isoformat('T') 530 | elif typeelement == 'xsd:string' or typeelement == 'str' or typeelement == 'unicode': 531 | return str(value) 532 | elif typeelement == 'xsd:boolean' or typeelement == 'bool': 533 | return str(value).lower() 534 | 535 | def createPythonType2XMLType(pyType): 536 | """ Function that creates a xml type from a python type """ 537 | xmlType = None 538 | if pyType == 'int': 539 | xmlType = 'integer' 540 | elif pyType == 'decimal': 541 | xmlType = 'decimal' 542 | elif pyType == 'double': 543 | xmlType = 'float' 544 | elif pyType == 'float': 545 | xmlType = 'float' 546 | elif pyType == 'duration': 547 | xmlType = 'duration' 548 | elif pyType == 'date': 549 | xmlType = 'date' 550 | elif pyType == 'time': 551 | xmlType = 'time' 552 | elif pyType == 'datetime': 553 | xmlType = 'dateTime' 554 | elif pyType == 'str': 555 | xmlType = 'string' 556 | elif pyType == 'bool': 557 | xmlType = 'boolean' 558 | 559 | return xmlType 560 | 561 | --------------------------------------------------------------------------------