wsme/wsmeext/cornice.py
Ryan Petrello 62725bb773 Add improved support for HTTP response codes in cornice apps.
Change-Id: I35efe85794e761877edd06722952292a3cac5d85
2013-09-16 12:07:55 -04:00

169 lines
5.3 KiB
Python

"""
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:
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')