├── 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 += '%s:sequence>\n'%prefix
38 | complexType += '%s: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'%self._namespace
164 | definitions += types
165 | definitions += messages
166 | definitions += portType
167 | definitions += binding
168 | definitions += service
169 | definitions += '\n'
170 | wsdlXml = xml.dom.minidom.parseString(definitions)
171 |
172 | return wsdlXml
173 |
174 | def _createTypes(self, name, elements):
175 | """ Private method that creates the types for the elements of wsdl """
176 | elem = ''
177 | if isinstance(elements,list):
178 | elem = '\n'%name
179 | elem += '\n'
180 | elems = ''
181 | idx = 1
182 | for e in elements:
183 | if hasattr(e,'__name__'):
184 | elems += '\n'%(idx,complextypes.createPythonType2XMLType(e.__name__))
185 | else:
186 | elems += e.createElement('value%s'%idx)+'\n'
187 | idx += 1
188 | elem += elems+'\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%s>'%(key,e,key)
209 | elif isinstance(element,Property):
210 | xml += '<%s>%s%s>'%(key,element.value,key)
211 | elif isinstance(element,ComplexType):
212 | xml += element.toXML(name=key)
213 | else:
214 | xml += '<%s>%s%s>'%(key,convert(type(element).__name__,element),key)
215 | xml += '%s>'%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 += '%s:sequence>'%namespace
287 | xsd += '%s:complexType>'%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 |
--------------------------------------------------------------------------------