wsme/wsme/rest/protocol.py

138 lines
4.5 KiB
Python

import os.path
import logging
from wsme.utils import OrderedDict
from wsme.protocol import CallContext, Protocol, media_type_accept
import wsme.rest
import wsme.rest.args
import wsme.runtime
log = logging.getLogger(__name__)
class RestProtocol(Protocol):
name = 'rest'
displayname = 'REST'
dataformats = ['json', 'xml']
content_types = ['application/json', 'text/xml']
def __init__(self, dataformats=None):
if dataformats is None:
dataformats = RestProtocol.dataformats
self.dataformats = OrderedDict()
self.content_types = []
for dataformat in dataformats:
__import__('wsme.rest.' + dataformat + '_utils')
dfmod = getattr(wsme.rest, dataformat + '_utils')
self.dataformats[dataformat] = dfmod
self.content_types.extend(dfmod.accept_content_types)
def accept(self, request):
for dataformat in self.dataformats:
if request.path.endswith('.' + dataformat):
return True
return media_type_accept(request, self.content_types)
def iter_calls(self, request):
context = CallContext(request)
context.outformat = None
ext = os.path.splitext(request.path.split('/')[-1])[1]
inmime = request.content_type
try:
offers = request.accept.acceptable_offers(self.content_types)
outmime = offers[0][0]
except IndexError:
outmime = None
outformat = None
informat = None
for dfname, df in self.dataformats.items():
if ext == '.' + dfname:
outformat = df
if not inmime:
informat = df
if outformat is None and request.accept:
for dfname, df in self.dataformats.items():
if outmime in df.accept_content_types:
outformat = df
if not inmime:
informat = df
if outformat is None:
for dfname, df in self.dataformats.items():
if inmime == df.content_type:
outformat = df
context.outformat = outformat
context.outformat_options = {
'nest_result': getattr(self, 'nest_result', False)
}
if not inmime and informat:
inmime = informat.content_type
log.debug("Inferred input type: %s" % inmime)
context.inmime = inmime
yield context
def extract_path(self, context):
path = context.request.path
assert path.startswith(self.root._webpath)
path = path[len(self.root._webpath):]
path = path.strip('/').split('/')
for dataformat in self.dataformats:
if path[-1].endswith('.' + dataformat):
path[-1] = path[-1][:-len(dataformat) - 1]
# Check if the path is actually a function, and if not
# see if the http method make a difference
# TODO Re-think the function lookup phases. Here we are
# doing the job that will be done in a later phase, which
# is sub-optimal
for p, fdef in self.root.getapi():
if p == path:
return path
# No function at this path. Now check for function that have
# this path as a prefix, and declared an http method
for p, fdef in self.root.getapi():
if len(p) == len(path) + 1 and p[:len(path)] == path and \
fdef.extra_options.get('method') == context.request.method:
return p
return path
def read_arguments(self, context):
request = context.request
funcdef = context.funcdef
body = None
if request.content_length not in (None, 0, '0'):
body = request.body
if not body and '__body__' in request.params:
body = request.params['__body__']
args, kwargs = wsme.rest.args.combine_args(
funcdef,
(wsme.rest.args.args_from_params(funcdef, request.params),
wsme.rest.args.args_from_body(funcdef, body, context.inmime))
)
wsme.runtime.check_arguments(funcdef, args, kwargs)
return kwargs
def encode_result(self, context, result):
out = context.outformat.encode_result(
result, context.funcdef.return_type,
**context.outformat_options
)
return out
def encode_error(self, context, errordetail):
out = context.outformat.encode_error(
context, errordetail
)
return out