Remove turbogears integration
No one in OpenStack that's still using this is using turbogears. Kill it. Change-Id: Ifeff1f40834289093dd997ce34fc3b42c3abbc13 Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
parent
f4d26d9863
commit
a9faca002b
@ -62,14 +62,13 @@ Main features
|
|||||||
- Extensible : easy to add more protocols or more base types.
|
- Extensible : easy to add more protocols or more base types.
|
||||||
- Framework independence : adapters are provided to easily integrate
|
- Framework independence : adapters are provided to easily integrate
|
||||||
your API in any web framework, for example a wsgi container,
|
your API in any web framework, for example a wsgi container,
|
||||||
Pecan_, TurboGears_, Flask_, cornice_...
|
Pecan_, Flask_, cornice_...
|
||||||
- Very few runtime dependencies: webob, simplegeneric. Optionnaly lxml and
|
- Very few runtime dependencies: webob, simplegeneric. Optionnaly lxml and
|
||||||
simplejson if you need better performances.
|
simplejson if you need better performances.
|
||||||
- Integration in `Sphinx`_ for making clean documentation with
|
- Integration in `Sphinx`_ for making clean documentation with
|
||||||
``wsmeext.sphinxext``.
|
``wsmeext.sphinxext``.
|
||||||
|
|
||||||
.. _Pecan: http://pecanpy.org/
|
.. _Pecan: http://pecanpy.org/
|
||||||
.. _TurboGears: http://www.turbogears.org/
|
|
||||||
.. _Flask: http://flask.pocoo.org/
|
.. _Flask: http://flask.pocoo.org/
|
||||||
.. _cornice: http://pypi.python.org/pypi/cornice
|
.. _cornice: http://pypi.python.org/pypi/cornice
|
||||||
|
|
||||||
|
@ -1,11 +1,26 @@
|
|||||||
Changes
|
Changes
|
||||||
=======
|
=======
|
||||||
|
|
||||||
0.9.4 (future)
|
1.0.0 (future)
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
* SQLAlchemy support is deprecated and will be removed in one of the next
|
* Remove support for turbogears
|
||||||
releases. It has never actually worked to begin with.
|
* Remove SQLAlchemy support. It has never actually worked to begin with.
|
||||||
|
|
||||||
|
0.9.2 (2017-02-14)
|
||||||
|
------------------
|
||||||
|
|
||||||
|
TODO.
|
||||||
|
|
||||||
|
0.9.1 (2017-01-04)
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Fix packaging issues.
|
||||||
|
|
||||||
|
0.9.0 (2017-01-04)
|
||||||
|
------------------
|
||||||
|
|
||||||
|
TODO.
|
||||||
|
|
||||||
0.8.0 (2015-08-25)
|
0.8.0 (2015-08-25)
|
||||||
------------------
|
------------------
|
||||||
|
@ -241,74 +241,6 @@ The `example <http://pecan.readthedocs.org/en/latest/rest.html#nesting-restcontr
|
|||||||
|
|
||||||
.. _adapter-tg1:
|
.. _adapter-tg1:
|
||||||
|
|
||||||
Turbogears 1.x
|
|
||||||
--------------
|
|
||||||
|
|
||||||
The TG adapters have an api very similar to TGWebServices. Migrating from it
|
|
||||||
should be straightforward (a little howto migrate would not hurt though, and it
|
|
||||||
will be written as soon as possible).
|
|
||||||
|
|
||||||
:mod:`wsmeext.tg11` -- TG 1.1 adapter
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. module:: wsmeext.tg11
|
|
||||||
|
|
||||||
.. function:: wsexpose(return_type, \*arg_types, \*\*options)
|
|
||||||
|
|
||||||
See @\ :func:`signature` for parameters documentation.
|
|
||||||
|
|
||||||
Can be used on any function of a controller
|
|
||||||
instead of the expose decorator from TG.
|
|
||||||
|
|
||||||
.. function:: wsvalidate(\*arg_types)
|
|
||||||
|
|
||||||
Set the argument types of an exposed function. This decorator is provided
|
|
||||||
so that WSME is an almost drop-in replacement for TGWebServices. If
|
|
||||||
starting from scratch you can use \ :func:`wsexpose` only
|
|
||||||
|
|
||||||
.. function:: adapt(wsroot)
|
|
||||||
|
|
||||||
Returns a TG1 controller instance that publish a :class:`wsme.WSRoot`.
|
|
||||||
It can then be mounted on a TG1 controller.
|
|
||||||
|
|
||||||
Because the adapt function modifies the cherrypy filters of the controller
|
|
||||||
the 'webpath' of the WSRoot instance must be consistent with the path it
|
|
||||||
will be mounted on.
|
|
||||||
|
|
||||||
:mod:`wsmeext.tg15` -- TG 1.5 adapter
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. module:: wsmeext.tg15
|
|
||||||
|
|
||||||
This adapter has the exact same api as :mod:`wsmeext.tg11`.
|
|
||||||
|
|
||||||
Example
|
|
||||||
~~~~~~~
|
|
||||||
|
|
||||||
In a freshly quickstarted tg1 application (let's say, wsmedemo), you can add
|
|
||||||
REST-ish functions anywhere in your controller tree. Here directly on the root,
|
|
||||||
in controllers.py:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
# ...
|
|
||||||
|
|
||||||
# For tg 1.5, import from wsmeext.tg15 instead :
|
|
||||||
from wsmeext.tg11 import wsexpose, WSRoot
|
|
||||||
|
|
||||||
class Root(controllers.RootController):
|
|
||||||
# Having a WSRoot on /ws is only required to enable additional
|
|
||||||
# protocols. For REST-only services, it can be ignored.
|
|
||||||
ws = adapt(
|
|
||||||
WSRoot(webpath='/ws', protocols=['soap'])
|
|
||||||
)
|
|
||||||
|
|
||||||
@wsexpose(int, int, int)
|
|
||||||
def multiply(self, a, b):
|
|
||||||
return a * b
|
|
||||||
|
|
||||||
.. _TurboGears: http://www.turbogears.org/
|
|
||||||
|
|
||||||
Other frameworks
|
Other frameworks
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
|
@ -14,8 +14,6 @@ be done :
|
|||||||
|
|
||||||
- Implement adapters for other frameworks :
|
- Implement adapters for other frameworks :
|
||||||
|
|
||||||
- TurboGears 2
|
|
||||||
|
|
||||||
- Pylons
|
- Pylons
|
||||||
|
|
||||||
- CherryPy
|
- CherryPy
|
||||||
|
@ -1,196 +0,0 @@
|
|||||||
import wsmeext.tg11
|
|
||||||
from wsme import WSRoot
|
|
||||||
from wsmeext.tg11 import wsexpose, wsvalidate
|
|
||||||
import wsmeext.tg1
|
|
||||||
|
|
||||||
from turbogears.controllers import RootController
|
|
||||||
import cherrypy
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import simplejson
|
|
||||||
|
|
||||||
|
|
||||||
from wsmeext.tests import test_soap
|
|
||||||
|
|
||||||
|
|
||||||
class WSController(WSRoot):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Subcontroller(object):
|
|
||||||
@wsexpose(int, int, int)
|
|
||||||
def add(self, a, b):
|
|
||||||
return a + b
|
|
||||||
|
|
||||||
|
|
||||||
class Root(RootController):
|
|
||||||
class UselessSubClass:
|
|
||||||
# This class is here only to make sure wsmeext.tg1.scan_api
|
|
||||||
# does its job properly
|
|
||||||
pass
|
|
||||||
|
|
||||||
ws = WSController(webpath='/ws')
|
|
||||||
ws.addprotocol(
|
|
||||||
'soap',
|
|
||||||
tns=test_soap.tns,
|
|
||||||
typenamespace=test_soap.typenamespace,
|
|
||||||
baseURL='/ws/'
|
|
||||||
)
|
|
||||||
ws = wsmeext.tg11.adapt(ws)
|
|
||||||
|
|
||||||
@wsexpose(int)
|
|
||||||
@wsvalidate(int, int)
|
|
||||||
def multiply(self, a, b):
|
|
||||||
return a * b
|
|
||||||
|
|
||||||
@wsexpose(int)
|
|
||||||
@wsvalidate(int, int)
|
|
||||||
def divide(self, a, b):
|
|
||||||
if b == 0:
|
|
||||||
raise cherrypy.HTTPError(400, 'Cannot divide by zero!')
|
|
||||||
return a / b
|
|
||||||
|
|
||||||
sub = Subcontroller()
|
|
||||||
|
|
||||||
from turbogears import testutil, config, startup
|
|
||||||
|
|
||||||
|
|
||||||
class TestController(unittest.TestCase):
|
|
||||||
root = Root
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
"Tests the output of the index method"
|
|
||||||
self.app = testutil.make_app(self.root)
|
|
||||||
testutil.start_server()
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
# implementation copied from turbogears.testutil.stop_server.
|
|
||||||
# The only change is that cherrypy.root is set to None
|
|
||||||
# AFTER stopTurbogears has been called so that wsmeext.tg11
|
|
||||||
# can correctly uninstall its filter.
|
|
||||||
if config.get("cp_started"):
|
|
||||||
cherrypy.server.stop()
|
|
||||||
config.update({"cp_started": False})
|
|
||||||
|
|
||||||
if config.get("server_started"):
|
|
||||||
startup.stopTurboGears()
|
|
||||||
config.update({"server_started": False})
|
|
||||||
|
|
||||||
def test_restcall(self):
|
|
||||||
response = self.app.post("/multiply",
|
|
||||||
simplejson.dumps({'a': 5, 'b': 10}),
|
|
||||||
{'Content-Type': 'application/json'}
|
|
||||||
)
|
|
||||||
print response
|
|
||||||
assert simplejson.loads(response.body) == 50
|
|
||||||
|
|
||||||
response = self.app.post("/sub/add",
|
|
||||||
simplejson.dumps({'a': 5, 'b': 10}),
|
|
||||||
{'Content-Type': 'application/json'}
|
|
||||||
)
|
|
||||||
print response
|
|
||||||
assert simplejson.loads(response.body) == 15
|
|
||||||
|
|
||||||
response = self.app.post("/multiply",
|
|
||||||
simplejson.dumps({'a': 5, 'b': 10}),
|
|
||||||
{'Content-Type': 'application/json', 'Accept': 'application/json'}
|
|
||||||
)
|
|
||||||
print response
|
|
||||||
assert simplejson.loads(response.body) == 50
|
|
||||||
|
|
||||||
response = self.app.post("/multiply",
|
|
||||||
simplejson.dumps({'a': 5, 'b': 10}),
|
|
||||||
{'Content-Type': 'application/json', 'Accept': 'text/javascript'}
|
|
||||||
)
|
|
||||||
print response
|
|
||||||
assert simplejson.loads(response.body) == 50
|
|
||||||
|
|
||||||
response = self.app.post("/multiply",
|
|
||||||
simplejson.dumps({'a': 5, 'b': 10}),
|
|
||||||
{'Content-Type': 'application/json',
|
|
||||||
'Accept': 'text/xml'}
|
|
||||||
)
|
|
||||||
print response
|
|
||||||
assert response.body == "<result>50</result>"
|
|
||||||
|
|
||||||
def test_custom_clientside_error(self):
|
|
||||||
response = self.app.post(
|
|
||||||
"/divide",
|
|
||||||
simplejson.dumps({'a': 5, 'b': 0}),
|
|
||||||
{'Content-Type': 'application/json', 'Accept': 'application/json'},
|
|
||||||
expect_errors=True
|
|
||||||
)
|
|
||||||
assert response.status_int == 400
|
|
||||||
assert simplejson.loads(response.body) == {
|
|
||||||
"debuginfo": None,
|
|
||||||
"faultcode": "Server",
|
|
||||||
"faultstring": "(400, 'Cannot divide by zero!')"
|
|
||||||
}
|
|
||||||
|
|
||||||
response = self.app.post(
|
|
||||||
"/divide",
|
|
||||||
simplejson.dumps({'a': 5, 'b': 0}),
|
|
||||||
{'Content-Type': 'application/json', 'Accept': 'text/xml'},
|
|
||||||
expect_errors=True
|
|
||||||
)
|
|
||||||
assert response.status_int == 400
|
|
||||||
assert response.body == ("<error><faultcode>Server</faultcode>"
|
|
||||||
"<faultstring>(400, 'Cannot divide by zero!')"
|
|
||||||
"</faultstring><debuginfo /></error>")
|
|
||||||
|
|
||||||
def test_soap_wsdl(self):
|
|
||||||
ts = test_soap.TestSOAP('test_wsdl')
|
|
||||||
ts.app = self.app
|
|
||||||
ts.ws_path = '/ws/'
|
|
||||||
ts.run()
|
|
||||||
#wsdl = self.app.get('/ws/api.wsdl').body
|
|
||||||
#print wsdl
|
|
||||||
#assert 'multiply' in wsdl
|
|
||||||
|
|
||||||
def test_soap_call(self):
|
|
||||||
ts = test_soap.TestSOAP('test_wsdl')
|
|
||||||
ts.app = self.app
|
|
||||||
ts.ws_path = '/ws/'
|
|
||||||
|
|
||||||
print ts.ws_path
|
|
||||||
assert ts.call('multiply', a=5, b=10, _rt=int) == 50
|
|
||||||
|
|
||||||
def test_scan_api_loops(self):
|
|
||||||
class MyRoot(object):
|
|
||||||
pass
|
|
||||||
|
|
||||||
MyRoot.loop = MyRoot()
|
|
||||||
|
|
||||||
root = MyRoot()
|
|
||||||
|
|
||||||
api = list(wsmeext.tg1._scan_api(root))
|
|
||||||
print(api)
|
|
||||||
|
|
||||||
self.assertEqual(len(api), 0)
|
|
||||||
|
|
||||||
def test_scan_api_maxlen(self):
|
|
||||||
class ARoot(object):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def make_subcontrollers(n):
|
|
||||||
c = type('Controller%s' % n, (object,), {})
|
|
||||||
return c
|
|
||||||
|
|
||||||
c = ARoot
|
|
||||||
for n in range(55):
|
|
||||||
subc = make_subcontrollers(n)
|
|
||||||
c.sub = subc()
|
|
||||||
c = subc
|
|
||||||
root = ARoot()
|
|
||||||
self.assertRaises(ValueError, list, wsmeext.tg1._scan_api(root))
|
|
||||||
|
|
||||||
def test_templates_content_type(self):
|
|
||||||
self.assertEqual(
|
|
||||||
"application/json",
|
|
||||||
wsmeext.tg1.AutoJSONTemplate().get_content_type('dummy')
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
"text/xml",
|
|
||||||
wsmeext.tg1.AutoXMLTemplate().get_content_type('dummy')
|
|
||||||
)
|
|
@ -1,177 +0,0 @@
|
|||||||
import wsmeext.tg15
|
|
||||||
from wsme import WSRoot
|
|
||||||
|
|
||||||
from turbogears.controllers import RootController
|
|
||||||
import cherrypy
|
|
||||||
|
|
||||||
from wsmeext.tests import test_soap
|
|
||||||
|
|
||||||
import simplejson
|
|
||||||
|
|
||||||
|
|
||||||
class Subcontroller(object):
|
|
||||||
@wsmeext.tg15.wsexpose(int, int, int)
|
|
||||||
def add(self, a, b):
|
|
||||||
return a + b
|
|
||||||
|
|
||||||
|
|
||||||
class Root(RootController):
|
|
||||||
class UselessSubClass:
|
|
||||||
# This class is here only to make sure wsmeext.tg1.scan_api
|
|
||||||
# does its job properly
|
|
||||||
pass
|
|
||||||
|
|
||||||
sub = Subcontroller()
|
|
||||||
|
|
||||||
ws = WSRoot(webpath='/ws')
|
|
||||||
ws.addprotocol('soap',
|
|
||||||
tns=test_soap.tns,
|
|
||||||
typenamespace=test_soap.typenamespace,
|
|
||||||
baseURL='/ws/'
|
|
||||||
)
|
|
||||||
ws = wsmeext.tg15.adapt(ws)
|
|
||||||
|
|
||||||
@wsmeext.tg15.wsexpose(int)
|
|
||||||
@wsmeext.tg15.wsvalidate(int, int)
|
|
||||||
def multiply(self, a, b):
|
|
||||||
return a * b
|
|
||||||
|
|
||||||
@wsmeext.tg15.wsexpose(int)
|
|
||||||
@wsmeext.tg15.wsvalidate(int, int)
|
|
||||||
def divide(self, a, b):
|
|
||||||
if b == 0:
|
|
||||||
raise cherrypy.HTTPError(400, 'Cannot divide by zero!')
|
|
||||||
return a / b
|
|
||||||
|
|
||||||
|
|
||||||
from turbogears import testutil
|
|
||||||
|
|
||||||
|
|
||||||
class TestController(testutil.TGTest):
|
|
||||||
root = Root
|
|
||||||
|
|
||||||
# def setUp(self):
|
|
||||||
# "Tests the output of the index method"
|
|
||||||
# self.app = testutil.make_app(self.root)
|
|
||||||
# #print cherrypy.root
|
|
||||||
# testutil.start_server()
|
|
||||||
|
|
||||||
# def tearDown(self):
|
|
||||||
# # implementation copied from turbogears.testutil.stop_server.
|
|
||||||
# # The only change is that cherrypy.root is set to None
|
|
||||||
# # AFTER stopTurbogears has been called so that wsmeext.tg15
|
|
||||||
# # can correctly uninstall its filter.
|
|
||||||
# if config.get("cp_started"):
|
|
||||||
# cherrypy.server.stop()
|
|
||||||
# config.update({"cp_started": False})
|
|
||||||
#
|
|
||||||
# if config.get("server_started"):
|
|
||||||
# startup.stopTurboGears()
|
|
||||||
# config.update({"server_started": False})
|
|
||||||
|
|
||||||
def test_restcall(self):
|
|
||||||
response = self.app.post("/multiply",
|
|
||||||
simplejson.dumps({'a': 5, 'b': 10}),
|
|
||||||
{'Content-Type': 'application/json'}
|
|
||||||
)
|
|
||||||
print response
|
|
||||||
assert simplejson.loads(response.body) == 50
|
|
||||||
|
|
||||||
response = self.app.post("/multiply",
|
|
||||||
simplejson.dumps({'a': 5, 'b': 10}),
|
|
||||||
{'Content-Type': 'application/json', 'Accept': 'application/json'}
|
|
||||||
)
|
|
||||||
print response
|
|
||||||
assert simplejson.loads(response.body) == 50
|
|
||||||
|
|
||||||
response = self.app.post("/multiply",
|
|
||||||
simplejson.dumps({'a': 5, 'b': 10}),
|
|
||||||
{'Content-Type': 'application/json', 'Accept': 'text/javascript'}
|
|
||||||
)
|
|
||||||
print response
|
|
||||||
assert simplejson.loads(response.body) == 50
|
|
||||||
|
|
||||||
response = self.app.post("/multiply",
|
|
||||||
simplejson.dumps({'a': 5, 'b': 10}),
|
|
||||||
{'Content-Type': 'application/json',
|
|
||||||
'Accept': 'text/xml'}
|
|
||||||
)
|
|
||||||
print response
|
|
||||||
assert response.body == "<result>50</result>"
|
|
||||||
|
|
||||||
def test_custom_clientside_error(self):
|
|
||||||
response = self.app.post(
|
|
||||||
"/divide",
|
|
||||||
simplejson.dumps({'a': 5, 'b': 0}),
|
|
||||||
{'Content-Type': 'application/json', 'Accept': 'application/json'},
|
|
||||||
expect_errors=True
|
|
||||||
)
|
|
||||||
assert response.status_int == 400
|
|
||||||
assert simplejson.loads(response.body) == {
|
|
||||||
"debuginfo": None,
|
|
||||||
"faultcode": "Client",
|
|
||||||
"faultstring": "(400, 'Cannot divide by zero!')"
|
|
||||||
}
|
|
||||||
|
|
||||||
response = self.app.post(
|
|
||||||
"/divide",
|
|
||||||
simplejson.dumps({'a': 5, 'b': 0}),
|
|
||||||
{'Content-Type': 'application/json', 'Accept': 'text/xml'},
|
|
||||||
expect_errors=True
|
|
||||||
)
|
|
||||||
assert response.status_int == 400
|
|
||||||
assert response.body == ("<error><faultcode>Client</faultcode>"
|
|
||||||
"<faultstring>(400, 'Cannot divide by zero!')"
|
|
||||||
"</faultstring><debuginfo /></error>")
|
|
||||||
|
|
||||||
def test_soap_wsdl(self):
|
|
||||||
wsdl = self.app.get('/ws/api.wsdl').body
|
|
||||||
print wsdl
|
|
||||||
assert 'multiply' in wsdl
|
|
||||||
|
|
||||||
def test_soap_call(self):
|
|
||||||
ts = test_soap.TestSOAP('test_wsdl')
|
|
||||||
ts.app = self.app
|
|
||||||
ts.ws_path = '/ws/'
|
|
||||||
|
|
||||||
print ts.ws_path
|
|
||||||
assert ts.call('multiply', a=5, b=10, _rt=int) == 50
|
|
||||||
|
|
||||||
def test_scan_api_loops(self):
|
|
||||||
class MyRoot(object):
|
|
||||||
pass
|
|
||||||
|
|
||||||
MyRoot.loop = MyRoot()
|
|
||||||
|
|
||||||
root = MyRoot()
|
|
||||||
|
|
||||||
api = list(wsmeext.tg1._scan_api(root))
|
|
||||||
print(api)
|
|
||||||
|
|
||||||
self.assertEqual(len(api), 0)
|
|
||||||
|
|
||||||
def test_scan_api_maxlen(self):
|
|
||||||
class ARoot(object):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def make_subcontrollers(n):
|
|
||||||
c = type('Controller%s' % n, (object,), {})
|
|
||||||
return c
|
|
||||||
|
|
||||||
c = ARoot
|
|
||||||
for n in range(55):
|
|
||||||
subc = make_subcontrollers(n)
|
|
||||||
c.sub = subc()
|
|
||||||
c = subc
|
|
||||||
root = ARoot()
|
|
||||||
self.assertRaises(ValueError, list, wsmeext.tg1._scan_api(root))
|
|
||||||
|
|
||||||
def test_templates_content_type(self):
|
|
||||||
self.assertEqual(
|
|
||||||
"application/json",
|
|
||||||
wsmeext.tg1.AutoJSONTemplate().get_content_type('dummy')
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
"text/xml",
|
|
||||||
wsmeext.tg1.AutoXMLTemplate().get_content_type('dummy')
|
|
||||||
)
|
|
173
wsmeext/tg1.py
173
wsmeext/tg1.py
@ -1,173 +0,0 @@
|
|||||||
try:
|
|
||||||
import json
|
|
||||||
except ImportError:
|
|
||||||
import simplejson as json # noqa
|
|
||||||
|
|
||||||
import functools
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import cherrypy
|
|
||||||
import webob
|
|
||||||
from turbogears import expose, util
|
|
||||||
import turbogears.view
|
|
||||||
|
|
||||||
from wsme.rest import validate as wsvalidate
|
|
||||||
import wsme.api
|
|
||||||
import wsme.rest
|
|
||||||
import wsme.rest.args
|
|
||||||
import wsme.rest.json
|
|
||||||
from wsme.utils import is_valid_code
|
|
||||||
|
|
||||||
import inspect
|
|
||||||
|
|
||||||
APIPATH_MAXLEN = 50
|
|
||||||
|
|
||||||
__all__ = ['wsexpose', 'wsvalidate']
|
|
||||||
|
|
||||||
|
|
||||||
def wsexpose(*args, **kwargs):
|
|
||||||
tg_json_expose = expose(
|
|
||||||
'wsmejson:',
|
|
||||||
accept_format='application/json',
|
|
||||||
content_type='application/json',
|
|
||||||
tg_format='json'
|
|
||||||
)
|
|
||||||
tg_altjson_expose = expose(
|
|
||||||
'wsmejson:',
|
|
||||||
accept_format='text/javascript',
|
|
||||||
content_type='application/json'
|
|
||||||
)
|
|
||||||
tg_xml_expose = expose(
|
|
||||||
'wsmexml:',
|
|
||||||
accept_format='text/xml',
|
|
||||||
content_type='text/xml',
|
|
||||||
tg_format='xml'
|
|
||||||
)
|
|
||||||
sig = wsme.signature(*args, **kwargs)
|
|
||||||
|
|
||||||
def decorate(f):
|
|
||||||
sig(f)
|
|
||||||
funcdef = wsme.api.FunctionDefinition.get(f)
|
|
||||||
|
|
||||||
@functools.wraps(f)
|
|
||||||
def callfunction(self, *args, **kwargs):
|
|
||||||
args, kwargs = wsme.rest.args.get_args(
|
|
||||||
funcdef, args, kwargs,
|
|
||||||
cherrypy.request.params, None,
|
|
||||||
cherrypy.request.body,
|
|
||||||
cherrypy.request.headers['Content-Type']
|
|
||||||
)
|
|
||||||
if funcdef.pass_request:
|
|
||||||
kwargs[funcdef.pass_request] = cherrypy.request
|
|
||||||
try:
|
|
||||||
result = f(self, *args, **kwargs)
|
|
||||||
except Exception:
|
|
||||||
try:
|
|
||||||
exception_info = sys.exc_info()
|
|
||||||
orig_exception = exception_info[1]
|
|
||||||
if isinstance(orig_exception, cherrypy.HTTPError):
|
|
||||||
orig_code = getattr(orig_exception, 'status', None)
|
|
||||||
else:
|
|
||||||
orig_code = getattr(orig_exception, 'code', None)
|
|
||||||
data = wsme.api.format_exception(exception_info)
|
|
||||||
finally:
|
|
||||||
del exception_info
|
|
||||||
|
|
||||||
cherrypy.response.status = 500
|
|
||||||
if data['faultcode'] == 'client':
|
|
||||||
cherrypy.response.status = 400
|
|
||||||
elif orig_code and is_valid_code(orig_code):
|
|
||||||
cherrypy.response.status = orig_code
|
|
||||||
|
|
||||||
accept = cherrypy.request.headers.get('Accept', "").lower()
|
|
||||||
accept = util.simplify_http_accept_header(accept)
|
|
||||||
|
|
||||||
decorators = {'text/xml': wsme.rest.xml.encode_error}
|
|
||||||
return decorators.get(
|
|
||||||
accept,
|
|
||||||
wsme.rest.json.encode_error
|
|
||||||
)(None, data)
|
|
||||||
|
|
||||||
return dict(
|
|
||||||
datatype=funcdef.return_type,
|
|
||||||
result=result
|
|
||||||
)
|
|
||||||
|
|
||||||
callfunction = tg_xml_expose(callfunction)
|
|
||||||
callfunction = tg_altjson_expose(callfunction)
|
|
||||||
callfunction = tg_json_expose(callfunction)
|
|
||||||
callfunction._wsme_original_function = f
|
|
||||||
return callfunction
|
|
||||||
|
|
||||||
return decorate
|
|
||||||
|
|
||||||
|
|
||||||
class AutoJSONTemplate(object):
|
|
||||||
def __init__(self, extra_vars_func=None, options=None):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def render(self, info, format="json", fragment=False, template=None):
|
|
||||||
"Renders the template to a string using the provided info."
|
|
||||||
return wsme.rest.json.encode_result(
|
|
||||||
info['result'], info['datatype']
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_content_type(self, user_agent):
|
|
||||||
return "application/json"
|
|
||||||
|
|
||||||
|
|
||||||
class AutoXMLTemplate(object):
|
|
||||||
def __init__(self, extra_vars_func=None, options=None):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def render(self, info, format="json", fragment=False, template=None):
|
|
||||||
"Renders the template to a string using the provided info."
|
|
||||||
return wsme.rest.xml.encode_result(
|
|
||||||
info['result'], info['datatype']
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_content_type(self, user_agent):
|
|
||||||
return "text/xml"
|
|
||||||
|
|
||||||
|
|
||||||
turbogears.view.engines['wsmejson'] = AutoJSONTemplate(turbogears.view.stdvars)
|
|
||||||
turbogears.view.engines['wsmexml'] = AutoXMLTemplate(turbogears.view.stdvars)
|
|
||||||
|
|
||||||
|
|
||||||
class Controller(object):
|
|
||||||
def __init__(self, wsroot):
|
|
||||||
self._wsroot = wsroot
|
|
||||||
|
|
||||||
@expose()
|
|
||||||
def default(self, *args, **kw):
|
|
||||||
req = webob.Request(cherrypy.request.wsgi_environ)
|
|
||||||
res = self._wsroot._handle_request(req)
|
|
||||||
cherrypy.response.header_list = res.headerlist
|
|
||||||
cherrypy.response.status = res.status
|
|
||||||
return res.body
|
|
||||||
|
|
||||||
|
|
||||||
def _scan_api(controller, path=[], objects=[]):
|
|
||||||
"""
|
|
||||||
Recursively iterate a controller api entries.
|
|
||||||
"""
|
|
||||||
for name in dir(controller):
|
|
||||||
if name.startswith('_'):
|
|
||||||
continue
|
|
||||||
a = getattr(controller, name)
|
|
||||||
if a in objects:
|
|
||||||
continue
|
|
||||||
if inspect.ismethod(a):
|
|
||||||
if wsme.api.iswsmefunction(a):
|
|
||||||
yield path + [name], a._wsme_original_function, [controller]
|
|
||||||
elif inspect.isclass(a):
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
if len(path) > APIPATH_MAXLEN:
|
|
||||||
raise ValueError("Path is too long: " + str(path))
|
|
||||||
for i in _scan_api(a, path + [name], objects + [a]):
|
|
||||||
yield i
|
|
||||||
|
|
||||||
|
|
||||||
def scan_api(root=None):
|
|
||||||
return _scan_api(cherrypy.root)
|
|
@ -1,40 +0,0 @@
|
|||||||
from turbogears import config
|
|
||||||
import cherrypy
|
|
||||||
from cherrypy.filters.basefilter import BaseFilter
|
|
||||||
from turbogears.startup import call_on_startup, call_on_shutdown
|
|
||||||
from wsmeext.tg1 import wsexpose, wsvalidate
|
|
||||||
import wsmeext.tg1
|
|
||||||
|
|
||||||
__all__ = ['adapt', 'wsexpose', 'wsvalidate']
|
|
||||||
|
|
||||||
|
|
||||||
class WSMECherrypyFilter(BaseFilter):
|
|
||||||
def __init__(self, controller):
|
|
||||||
self.controller = controller
|
|
||||||
self.webpath = None
|
|
||||||
|
|
||||||
def on_start_resource(self):
|
|
||||||
path = cherrypy.request.path
|
|
||||||
if path.startswith(self.controller._wsroot._webpath):
|
|
||||||
cherrypy.request.processRequestBody = False
|
|
||||||
|
|
||||||
|
|
||||||
def adapt(wsroot):
|
|
||||||
wsroot._scan_api = wsmeext.tg1.scan_api
|
|
||||||
controller = wsmeext.tg1.Controller(wsroot)
|
|
||||||
filter_ = WSMECherrypyFilter(controller)
|
|
||||||
|
|
||||||
def install_filter():
|
|
||||||
filter_.webpath = config.get('server.webpath') or ''
|
|
||||||
controller._wsroot._webpath = \
|
|
||||||
filter_.webpath + controller._wsroot._webpath
|
|
||||||
cherrypy.root._cp_filters.append(filter_)
|
|
||||||
|
|
||||||
def uninstall_filter():
|
|
||||||
cherrypy.root._cp_filters.remove(filter_)
|
|
||||||
controller._wsroot._webpath = \
|
|
||||||
controller._wsroot._webpath[len(filter_.webpath):]
|
|
||||||
|
|
||||||
call_on_startup.append(install_filter)
|
|
||||||
call_on_shutdown.insert(0, uninstall_filter)
|
|
||||||
return controller
|
|
@ -1,20 +0,0 @@
|
|||||||
import cherrypy
|
|
||||||
|
|
||||||
from wsmeext.tg1 import wsexpose, wsvalidate
|
|
||||||
import wsmeext.tg1
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['adapt', 'wsexpose', 'wsvalidate']
|
|
||||||
|
|
||||||
|
|
||||||
def scan_api(root=None):
|
|
||||||
for baseurl, instance in cherrypy.tree.apps.items():
|
|
||||||
path = [token for token in baseurl.split('/') if token]
|
|
||||||
for i in wsmeext.tg1._scan_api(instance.root, path):
|
|
||||||
yield i
|
|
||||||
|
|
||||||
|
|
||||||
def adapt(wsroot):
|
|
||||||
wsroot._scan_api = scan_api
|
|
||||||
controller = wsmeext.tg1.Controller(wsroot)
|
|
||||||
return controller
|
|
Loading…
x
Reference in New Issue
Block a user