Remove cornice integration
As with turbogears, no one was using this in OpenStack and therefore we can and should remove this adaptor. Change-Id: I0d3942680c1156e57d70f334caea6b89590b46c7 Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
parent
a9faca002b
commit
21dadaef0e
@ -62,7 +62,7 @@ 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_, Flask_, cornice_...
|
Pecan_, Flask_, ...
|
||||||
- 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
|
||||||
@ -70,7 +70,6 @@ Main features
|
|||||||
|
|
||||||
.. _Pecan: http://pecanpy.org/
|
.. _Pecan: http://pecanpy.org/
|
||||||
.. _Flask: http://flask.pocoo.org/
|
.. _Flask: http://flask.pocoo.org/
|
||||||
.. _cornice: http://pypi.python.org/pypi/cornice
|
|
||||||
|
|
||||||
Install
|
Install
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
@ -5,6 +5,7 @@ Changes
|
|||||||
--------------
|
--------------
|
||||||
|
|
||||||
* Remove support for turbogears
|
* Remove support for turbogears
|
||||||
|
* Remove support for cornice
|
||||||
* Remove SQLAlchemy support. 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)
|
0.9.2 (2017-02-14)
|
||||||
|
@ -14,22 +14,22 @@ The decorators
|
|||||||
Depending on the framework you are using, you will have to use either a
|
Depending on the framework you are using, you will have to use either a
|
||||||
@\ :class:`wsme.signature` decorator or a @\ :class:`wsme.wsexpose` decorator.
|
@\ :class:`wsme.signature` decorator or a @\ :class:`wsme.wsexpose` decorator.
|
||||||
|
|
||||||
@signature
|
@signature
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
The base @\ :class:`wsme.signature` decorator defines the return and argument types
|
The base @\ :class:`wsme.signature` decorator defines the return and argument types
|
||||||
of the function, and if needed a few more options.
|
of the function, and if needed a few more options.
|
||||||
|
|
||||||
The Flask and Cornice adapters both propose a specific version of it, which
|
The Flask and adapter proposes a specific version of it, which also wrap the
|
||||||
also wrap the function so that it becomes suitable for the host framework.
|
function so that it becomes suitable for the host framework.
|
||||||
|
|
||||||
In any case, the use of @\ :class:`wsme.signature` has the same meaning: tell WSME what is the
|
In any case, the use of @\ :class:`wsme.signature` has the same meaning: tell
|
||||||
signature of the function.
|
WSME what is the signature of the function.
|
||||||
|
|
||||||
@wsexpose
|
@wsexpose
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
|
|
||||||
The native Rest implementation, and the TG and Pecan adapters add a @\ :class:`wsme.wsexpose`
|
The native Rest implementation, and the Pecan adapter add a @\ :class:`wsme.wsexpose`
|
||||||
decorator.
|
decorator.
|
||||||
|
|
||||||
It does what @\ :class:`wsme.signature` does, *and* exposes the function in the routing system
|
It does what @\ :class:`wsme.signature` does, *and* exposes the function in the routing system
|
||||||
|
@ -24,8 +24,7 @@ This decorator can have two different names depending on the adapter.
|
|||||||
|
|
||||||
Generally this decorator is provided for frameworks that expect functions
|
Generally this decorator is provided for frameworks that expect functions
|
||||||
taking a request object as a single parameter and returning a response
|
taking a request object as a single parameter and returning a response
|
||||||
object. This is the case for :ref:`adapter-cornice` and
|
object. This is the case for :ref:`adapter-flask`.
|
||||||
:ref:`adapter-flask`.
|
|
||||||
|
|
||||||
If you want to enable additional protocols, you will need to
|
If you want to enable additional protocols, you will need to
|
||||||
mount a :class:`WSRoot` instance somewhere in the application, generally
|
mount a :class:`WSRoot` instance somewhere in the application, generally
|
||||||
@ -62,73 +61,6 @@ WSME, which is the case if you write a WSME standalone application.
|
|||||||
application = root.wsgiapp()
|
application = root.wsgiapp()
|
||||||
|
|
||||||
|
|
||||||
.. _adapter-cornice:
|
|
||||||
|
|
||||||
Cornice
|
|
||||||
-------
|
|
||||||
|
|
||||||
.. _cornice: http://cornice.readthedocs.org/en/latest/
|
|
||||||
|
|
||||||
*"* Cornice_ *provides helpers to build & document REST-ish Web Services with
|
|
||||||
Pyramid, with decent default behaviors. It takes care of following the HTTP
|
|
||||||
specification in an automated way where possible."*
|
|
||||||
|
|
||||||
|
|
||||||
:mod:`wsmeext.cornice` -- Cornice adapter
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. module:: wsmeext.cornice
|
|
||||||
|
|
||||||
.. function:: signature
|
|
||||||
|
|
||||||
Declare the parameters of a function and returns a function suitable for
|
|
||||||
cornice (ie that takes a request and returns a response).
|
|
||||||
|
|
||||||
Configuration
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
To use WSME with Cornice you have to add a configuration option to your Pyramid application.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from pyramid.config import Configurator
|
|
||||||
|
|
||||||
|
|
||||||
def make_app():
|
|
||||||
config = Configurator()
|
|
||||||
config.include("cornice")
|
|
||||||
config.include("wsmeext.cornice") # This includes WSME cornice support
|
|
||||||
# ...
|
|
||||||
return config.make_wsgi_app()
|
|
||||||
|
|
||||||
Example
|
|
||||||
~~~~~~~
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from cornice import Service
|
|
||||||
from wsmeext.cornice import signature
|
|
||||||
import wsme.types
|
|
||||||
|
|
||||||
hello = Service(name='hello', path='/', description="Simplest app")
|
|
||||||
|
|
||||||
class Info(wsme.types.Base):
|
|
||||||
message = wsme.types.text
|
|
||||||
|
|
||||||
|
|
||||||
@hello.get()
|
|
||||||
@signature(Info)
|
|
||||||
def get_info():
|
|
||||||
"""Returns Hello in JSON or XML."""
|
|
||||||
return Info(message='Hello World')
|
|
||||||
|
|
||||||
|
|
||||||
@hello.post()
|
|
||||||
@signature(None, Info)
|
|
||||||
def set_info(info):
|
|
||||||
print("Got a message: %s" % info.message)
|
|
||||||
|
|
||||||
|
|
||||||
.. _adapter-flask:
|
.. _adapter-flask:
|
||||||
|
|
||||||
Flask
|
Flask
|
||||||
@ -224,7 +156,7 @@ The `example <http://pecan.readthedocs.org/en/latest/rest.html#nesting-restcontr
|
|||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from wsmeext.pecan import wsexpose
|
from wsmeext.pecan import wsexpose
|
||||||
|
|
||||||
class BooksController(RestController):
|
class BooksController(RestController):
|
||||||
@wsexpose(Book, int, int)
|
@wsexpose(Book, int, int)
|
||||||
def get(self, author_id, id):
|
def get(self, author_id, id):
|
||||||
@ -238,37 +170,3 @@ The `example <http://pecan.readthedocs.org/en/latest/rest.html#nesting-restcontr
|
|||||||
books = BooksController()
|
books = BooksController()
|
||||||
|
|
||||||
.. _Pecan: http://pecanpy.org/
|
.. _Pecan: http://pecanpy.org/
|
||||||
|
|
||||||
.. _adapter-tg1:
|
|
||||||
|
|
||||||
Other frameworks
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Bottle
|
|
||||||
~~~~~~
|
|
||||||
|
|
||||||
No adapter is provided yet but it should not be hard to write one, by taking
|
|
||||||
example on the cornice adapter.
|
|
||||||
|
|
||||||
This example only show how to mount a WSRoot inside a bottle application.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
import bottle
|
|
||||||
import wsme
|
|
||||||
|
|
||||||
class MyRoot(wsme.WSRoot):
|
|
||||||
@wsme.expose(unicode)
|
|
||||||
def helloworld(self):
|
|
||||||
return u"Hello World !"
|
|
||||||
|
|
||||||
root = MyRoot(webpath='/ws', protocols=['restjson'])
|
|
||||||
|
|
||||||
bottle.mount('/ws', root.wsgiapp())
|
|
||||||
bottle.run()
|
|
||||||
|
|
||||||
Pyramid
|
|
||||||
~~~~~~~
|
|
||||||
|
|
||||||
The recommended way of using WSME inside Pyramid is to use
|
|
||||||
:ref:`adapter-cornice`.
|
|
||||||
|
@ -1,183 +0,0 @@
|
|||||||
import unittest
|
|
||||||
import json
|
|
||||||
|
|
||||||
import webtest
|
|
||||||
|
|
||||||
from cornice import Service
|
|
||||||
from cornice import resource
|
|
||||||
from pyramid.config import Configurator
|
|
||||||
from pyramid.httpexceptions import HTTPUnauthorized
|
|
||||||
|
|
||||||
from wsme.types import text, Base, HostRequest
|
|
||||||
from wsmeext.cornice import signature
|
|
||||||
|
|
||||||
|
|
||||||
class User(Base):
|
|
||||||
id = int
|
|
||||||
name = text
|
|
||||||
|
|
||||||
users = Service(name='users', path='/users')
|
|
||||||
|
|
||||||
|
|
||||||
@users.get()
|
|
||||||
@signature([User])
|
|
||||||
def users_get():
|
|
||||||
return [User(id=1, name='first')]
|
|
||||||
|
|
||||||
|
|
||||||
@users.post()
|
|
||||||
@signature(User, body=User)
|
|
||||||
def users_create(data):
|
|
||||||
data.id = 2
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
secret = Service(name='secrets', path='/secret')
|
|
||||||
|
|
||||||
|
|
||||||
@secret.get()
|
|
||||||
@signature()
|
|
||||||
def secret_get():
|
|
||||||
raise HTTPUnauthorized()
|
|
||||||
|
|
||||||
|
|
||||||
divide = Service(name='divide', path='/divide')
|
|
||||||
|
|
||||||
|
|
||||||
@divide.get()
|
|
||||||
@signature(int, int, int)
|
|
||||||
def do_divide(a, b):
|
|
||||||
return a / b
|
|
||||||
|
|
||||||
needrequest = Service(name='needrequest', path='/needrequest')
|
|
||||||
|
|
||||||
|
|
||||||
@needrequest.get()
|
|
||||||
@signature(bool, HostRequest)
|
|
||||||
def needrequest_get(request):
|
|
||||||
assert request.path == '/needrequest', request.path
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class Author(Base):
|
|
||||||
authorId = int
|
|
||||||
name = text
|
|
||||||
|
|
||||||
|
|
||||||
@resource.resource(collection_path='/author', path='/author/{authorId}')
|
|
||||||
class AuthorResource(object):
|
|
||||||
def __init__(self, request):
|
|
||||||
self.request = request
|
|
||||||
|
|
||||||
@signature(Author, int)
|
|
||||||
def get(self, authorId):
|
|
||||||
return Author(authorId=authorId, name="Author %s" % authorId)
|
|
||||||
|
|
||||||
@signature(Author, int, body=Author)
|
|
||||||
def post(self, authorId, data):
|
|
||||||
data.authorId = authorId
|
|
||||||
return data
|
|
||||||
|
|
||||||
@signature([Author], text)
|
|
||||||
def collection_get(self, where=None):
|
|
||||||
return [
|
|
||||||
Author(authorId=1, name="Author 1"),
|
|
||||||
Author(authorId=2, name="Author 2"),
|
|
||||||
Author(authorId=3, name="Author 3")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def make_app():
|
|
||||||
config = Configurator()
|
|
||||||
config.include("cornice")
|
|
||||||
config.include("wsmeext.cornice")
|
|
||||||
config.scan("test_cornice")
|
|
||||||
return config.make_wsgi_app()
|
|
||||||
|
|
||||||
|
|
||||||
class WSMECorniceTestCase(unittest.TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.app = webtest.TestApp(make_app())
|
|
||||||
|
|
||||||
def test_get_json_list(self):
|
|
||||||
resp = self.app.get('/users')
|
|
||||||
self.assertEqual(
|
|
||||||
resp.body,
|
|
||||||
b'[{"id": 1, "name": "first"}]'
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_xml_list(self):
|
|
||||||
resp = self.app.get('/users', headers={"Accept": "text/xml"})
|
|
||||||
self.assertEqual(
|
|
||||||
resp.body,
|
|
||||||
b'<result><item><id>1</id><name>first</name></item></result>'
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_post_json_data(self):
|
|
||||||
data = json.dumps({"name": "new"})
|
|
||||||
resp = self.app.post(
|
|
||||||
'/users', data,
|
|
||||||
headers={"Content-Type": "application/json"}
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
resp.body,
|
|
||||||
b'{"id": 2, "name": "new"}'
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_post_xml_data(self):
|
|
||||||
data = '<data><name>new</name></data>'
|
|
||||||
resp = self.app.post(
|
|
||||||
'/users', data,
|
|
||||||
headers={"Content-Type": "text/xml"}
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
resp.body,
|
|
||||||
b'<result><id>2</id><name>new</name></result>'
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_pass_request(self):
|
|
||||||
resp = self.app.get('/needrequest')
|
|
||||||
assert resp.json is True
|
|
||||||
|
|
||||||
def test_resource_collection_get(self):
|
|
||||||
resp = self.app.get('/author')
|
|
||||||
assert len(resp.json) == 3
|
|
||||||
assert resp.json[0]['name'] == 'Author 1'
|
|
||||||
assert resp.json[1]['name'] == 'Author 2'
|
|
||||||
assert resp.json[2]['name'] == 'Author 3'
|
|
||||||
|
|
||||||
def test_resource_get(self):
|
|
||||||
resp = self.app.get('/author/5')
|
|
||||||
assert resp.json['name'] == 'Author 5'
|
|
||||||
|
|
||||||
def test_resource_post(self):
|
|
||||||
resp = self.app.post(
|
|
||||||
'/author/5',
|
|
||||||
json.dumps({"name": "Author 5"}),
|
|
||||||
headers={"Content-Type": "application/json"}
|
|
||||||
)
|
|
||||||
assert resp.json['authorId'] == 5
|
|
||||||
assert resp.json['name'] == 'Author 5'
|
|
||||||
|
|
||||||
def test_server_error(self):
|
|
||||||
resp = self.app.get('/divide?a=1&b=0', expect_errors=True)
|
|
||||||
self.assertEqual(resp.json['faultcode'], 'Server')
|
|
||||||
self.assertEqual(resp.status_code, 500)
|
|
||||||
|
|
||||||
def test_client_error(self):
|
|
||||||
resp = self.app.get(
|
|
||||||
'/divide?a=1&c=0',
|
|
||||||
headers={'Accept': 'application/json'},
|
|
||||||
expect_errors=True
|
|
||||||
)
|
|
||||||
self.assertEqual(resp.json['faultcode'], 'Client')
|
|
||||||
self.assertEqual(resp.status_code, 400)
|
|
||||||
|
|
||||||
def test_runtime_error(self):
|
|
||||||
resp = self.app.get(
|
|
||||||
'/secret',
|
|
||||||
headers={'Accept': 'application/json'},
|
|
||||||
expect_errors=True
|
|
||||||
)
|
|
||||||
self.assertEqual(resp.json['faultcode'], 'Client')
|
|
||||||
self.assertEqual(resp.status_code, 401)
|
|
25
tox.ini
25
tox.ini
@ -1,5 +1,5 @@
|
|||||||
[tox]
|
[tox]
|
||||||
envlist = py27,py27-nolxml,pypy,cornice,cornice-py3,coverage,py36,py35,py36-nolxml,py35-nolxml,pecan-dev27,pecan-dev35,pecan-dev36,pep8
|
envlist = py27,py27-nolxml,pypy,coverage,py36,py35,py36-nolxml,py35-nolxml,pecan-dev27,pecan-dev35,pecan-dev36,pep8
|
||||||
|
|
||||||
[common]
|
[common]
|
||||||
testtools =
|
testtools =
|
||||||
@ -20,29 +20,6 @@ basedeps =
|
|||||||
setenv =
|
setenv =
|
||||||
COVERAGE_FILE=.coverage.{envname}
|
COVERAGE_FILE=.coverage.{envname}
|
||||||
|
|
||||||
[testenv:cornice]
|
|
||||||
basepython = python2.7
|
|
||||||
usedevelop = True
|
|
||||||
deps =
|
|
||||||
pbr
|
|
||||||
nose
|
|
||||||
webtest
|
|
||||||
coverage < 3.99
|
|
||||||
cornice
|
|
||||||
commands =
|
|
||||||
{envbindir}/nosetests tests/test_cornice.py --with-xunit --xunit-file nosetests-{envname}.xml --verbose --with-coverage --cover-package wsmeext {posargs}
|
|
||||||
{envbindir}/coverage xml -o coverage-{envname}.xml wsme/*.py wsmeext/cornice.py
|
|
||||||
|
|
||||||
[testenv:cornice-py3]
|
|
||||||
basepython = python3.6
|
|
||||||
usedevelop = {[testenv:cornice]usedevelop}
|
|
||||||
deps = {[testenv:cornice]deps}
|
|
||||||
setenv =
|
|
||||||
PYTHONHASHSEED=0
|
|
||||||
commands =
|
|
||||||
{envbindir}/nosetests tests/test_cornice.py --with-xunit --xunit-file nosetests-{envname}.xml --verbose --with-coverage --cover-package wsmeext {posargs}
|
|
||||||
{envbindir}/coverage xml -o coverage-{envname}.xml wsme/*.py wsmeext/cornice.py
|
|
||||||
|
|
||||||
[testenv:pecan-dev-base]
|
[testenv:pecan-dev-base]
|
||||||
deps =
|
deps =
|
||||||
{[common]testtools}
|
{[common]testtools}
|
||||||
|
@ -1,168 +0,0 @@
|
|||||||
"""
|
|
||||||
WSME for cornice
|
|
||||||
|
|
||||||
|
|
||||||
Activate it::
|
|
||||||
|
|
||||||
config.include('wsme.cornice')
|
|
||||||
|
|
||||||
|
|
||||||
And use it::
|
|
||||||
|
|
||||||
@hello.get()
|
|
||||||
@wsexpose(Message, wsme.types.text)
|
|
||||||
def get_hello(who=u'World'):
|
|
||||||
return Message(text='Hello %s' % who)
|
|
||||||
"""
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
import inspect
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import wsme
|
|
||||||
from wsme.rest import json as restjson
|
|
||||||
from wsme.rest import xml as restxml
|
|
||||||
import wsme.runtime
|
|
||||||
import wsme.api
|
|
||||||
import functools
|
|
||||||
|
|
||||||
from wsme.rest.args import (
|
|
||||||
args_from_args, args_from_params, args_from_body, combine_args
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class WSMEJsonRenderer(object):
|
|
||||||
def __init__(self, info):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __call__(self, data, context):
|
|
||||||
response = context['request'].response
|
|
||||||
response.content_type = 'application/json'
|
|
||||||
if 'faultcode' in data:
|
|
||||||
if 'orig_code' in data:
|
|
||||||
response.status_code = data['orig_code']
|
|
||||||
elif data['faultcode'] == 'Client':
|
|
||||||
response.status_code = 400
|
|
||||||
else:
|
|
||||||
response.status_code = 500
|
|
||||||
return restjson.encode_error(None, data)
|
|
||||||
obj = data['result']
|
|
||||||
if isinstance(obj, wsme.api.Response):
|
|
||||||
response.status_code = obj.status_code
|
|
||||||
if obj.error:
|
|
||||||
return restjson.encode_error(None, obj.error)
|
|
||||||
obj = obj.obj
|
|
||||||
return restjson.encode_result(obj, data['datatype'])
|
|
||||||
|
|
||||||
|
|
||||||
class WSMEXmlRenderer(object):
|
|
||||||
def __init__(self, info):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __call__(self, data, context):
|
|
||||||
response = context['request'].response
|
|
||||||
if 'faultcode' in data:
|
|
||||||
if data['faultcode'] == 'Client':
|
|
||||||
response.status_code = 400
|
|
||||||
else:
|
|
||||||
response.status_code = 500
|
|
||||||
return restxml.encode_error(None, data)
|
|
||||||
response.content_type = 'text/xml'
|
|
||||||
return restxml.encode_result(data['result'], data['datatype'])
|
|
||||||
|
|
||||||
|
|
||||||
def get_outputformat(request):
|
|
||||||
df = None
|
|
||||||
if 'Accept' in request.headers:
|
|
||||||
if 'application/json' in request.headers['Accept']:
|
|
||||||
df = 'json'
|
|
||||||
elif 'text/xml' in request.headers['Accept']:
|
|
||||||
df = 'xml'
|
|
||||||
if df is None and 'Content-Type' in request.headers:
|
|
||||||
if 'application/json' in request.headers['Content-Type']:
|
|
||||||
df = 'json'
|
|
||||||
elif 'text/xml' in request.headers['Content-Type']:
|
|
||||||
df = 'xml'
|
|
||||||
return df if df else 'json'
|
|
||||||
|
|
||||||
|
|
||||||
def signature(*args, **kwargs):
|
|
||||||
sig = wsme.signature(*args, **kwargs)
|
|
||||||
|
|
||||||
def decorate(f):
|
|
||||||
args = inspect.getargspec(f)[0]
|
|
||||||
with_self = args[0] == 'self' if args else False
|
|
||||||
f = sig(f)
|
|
||||||
funcdef = wsme.api.FunctionDefinition.get(f)
|
|
||||||
funcdef.resolve_types(wsme.types.registry)
|
|
||||||
|
|
||||||
@functools.wraps(f)
|
|
||||||
def callfunction(*args):
|
|
||||||
if with_self:
|
|
||||||
if len(args) == 1:
|
|
||||||
self = args[0]
|
|
||||||
request = self.request
|
|
||||||
elif len(args) == 2:
|
|
||||||
self, request = args
|
|
||||||
else:
|
|
||||||
raise ValueError("Cannot do anything with these arguments")
|
|
||||||
else:
|
|
||||||
request = args[0]
|
|
||||||
request.override_renderer = 'wsme' + get_outputformat(request)
|
|
||||||
try:
|
|
||||||
args, kwargs = combine_args(funcdef, (
|
|
||||||
args_from_args(funcdef, (), request.matchdict),
|
|
||||||
args_from_params(funcdef, request.params),
|
|
||||||
args_from_body(funcdef, request.body, request.content_type)
|
|
||||||
))
|
|
||||||
wsme.runtime.check_arguments(funcdef, args, kwargs)
|
|
||||||
if funcdef.pass_request:
|
|
||||||
kwargs[funcdef.pass_request] = request
|
|
||||||
if with_self:
|
|
||||||
args.insert(0, self)
|
|
||||||
|
|
||||||
result = f(*args, **kwargs)
|
|
||||||
return {
|
|
||||||
'datatype': funcdef.return_type,
|
|
||||||
'result': result
|
|
||||||
}
|
|
||||||
except Exception:
|
|
||||||
try:
|
|
||||||
exception_info = sys.exc_info()
|
|
||||||
orig_exception = exception_info[1]
|
|
||||||
orig_code = getattr(orig_exception, 'code', None)
|
|
||||||
data = wsme.api.format_exception(exception_info)
|
|
||||||
if orig_code is not None:
|
|
||||||
data['orig_code'] = orig_code
|
|
||||||
return data
|
|
||||||
finally:
|
|
||||||
del exception_info
|
|
||||||
|
|
||||||
callfunction.wsme_func = f
|
|
||||||
return callfunction
|
|
||||||
return decorate
|
|
||||||
|
|
||||||
|
|
||||||
def scan_api(root=None):
|
|
||||||
from cornice.service import get_services
|
|
||||||
for service in get_services():
|
|
||||||
for method, func, options in service.definitions:
|
|
||||||
wsme_func = getattr(func, 'wsme_func')
|
|
||||||
basepath = service.path.split('/')
|
|
||||||
if basepath and not basepath[0]:
|
|
||||||
del basepath[0]
|
|
||||||
if wsme_func:
|
|
||||||
yield (
|
|
||||||
basepath + [method.lower()],
|
|
||||||
wsme_func._wsme_definition
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def includeme(config):
|
|
||||||
import pyramid.wsgi
|
|
||||||
wsroot = wsme.WSRoot(scan_api=scan_api, webpath='/ws')
|
|
||||||
wsroot.addprotocol('extdirect')
|
|
||||||
config.add_renderer('wsmejson', WSMEJsonRenderer)
|
|
||||||
config.add_renderer('wsmexml', WSMEXmlRenderer)
|
|
||||||
config.add_route('wsme', '/ws/*path')
|
|
||||||
config.add_view(pyramid.wsgi.wsgiapp(wsroot.wsgiapp()), route_name='wsme')
|
|
Loading…
x
Reference in New Issue
Block a user