Files
tryton/trytond/protocols/jsonrpc.py
T

163 lines
4.7 KiB
Python
Raw Normal View History

2014-12-02 12:54:01 +01:00
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
2010-01-20 10:33:44 +01:00
import traceback
import sys
import datetime
from decimal import Decimal
2011-10-14 09:56:05 +02:00
try:
import simplejson as json
except ImportError:
import json
2011-09-17 14:43:05 +02:00
import base64
2016-03-15 16:57:26 +01:00
from werkzeug.wrappers import Response
from werkzeug.utils import cached_property
from werkzeug.exceptions import BadRequest
from trytond.protocols.wrappers import Request
from trytond.exceptions import TrytonException
2010-01-20 10:33:44 +01:00
class JSONDecoder(object):
decoders = {}
@classmethod
def register(cls, klass, decoder):
assert klass not in cls.decoders
cls.decoders[klass] = decoder
def __call__(self, dct):
if dct.get('__class__') in self.decoders:
return self.decoders[dct['__class__']](dct)
return dct
JSONDecoder.register('datetime',
lambda dct: datetime.datetime(dct['year'], dct['month'], dct['day'],
dct['hour'], dct['minute'], dct['second'], dct['microsecond']))
JSONDecoder.register('date',
lambda dct: datetime.date(dct['year'], dct['month'], dct['day']))
JSONDecoder.register('time',
lambda dct: datetime.time(dct['hour'], dct['minute'], dct['second'],
dct['microsecond']))
2015-02-09 22:58:56 +01:00
JSONDecoder.register('timedelta',
lambda dct: datetime.timedelta(seconds=dct['seconds']))
2015-03-16 19:28:34 +01:00
def _bytes_decoder(dct):
cast = bytearray if bytes == str else bytes
2016-02-07 16:46:16 +01:00
return cast(base64.decodestring(dct['base64'].encode('utf-8')))
2015-03-16 19:28:34 +01:00
JSONDecoder.register('bytes', _bytes_decoder)
JSONDecoder.register('Decimal', lambda dct: Decimal(dct['decimal']))
2010-01-20 10:33:44 +01:00
2012-04-01 23:08:15 +02:00
2010-01-20 10:33:44 +01:00
class JSONEncoder(json.JSONEncoder):
serializers = {}
2011-10-14 09:56:05 +02:00
def __init__(self, *args, **kwargs):
super(JSONEncoder, self).__init__(*args, **kwargs)
# Force to use our custom decimal with simplejson
self.use_decimal = False
@classmethod
def register(cls, klass, encoder):
assert klass not in cls.serializers
cls.serializers[klass] = encoder
2010-01-20 10:33:44 +01:00
def default(self, obj):
marshaller = self.serializers.get(type(obj),
super(JSONEncoder, self).default)
return marshaller(obj)
JSONEncoder.register(datetime.datetime,
lambda o: {
'__class__': 'datetime',
'year': o.year,
'month': o.month,
'day': o.day,
'hour': o.hour,
'minute': o.minute,
'second': o.second,
'microsecond': o.microsecond,
})
JSONEncoder.register(datetime.date,
lambda o: {
'__class__': 'date',
'year': o.year,
'month': o.month,
'day': o.day,
})
JSONEncoder.register(datetime.time,
lambda o: {
'__class__': 'time',
'hour': o.hour,
'minute': o.minute,
'second': o.second,
'microsecond': o.microsecond,
})
2015-02-09 22:58:56 +01:00
JSONEncoder.register(datetime.timedelta,
lambda o: {
'__class__': 'timedelta',
'seconds': o.total_seconds(),
})
2015-03-16 19:28:34 +01:00
_bytes_encoder = lambda o: {
'__class__': 'bytes',
2016-02-07 16:46:16 +01:00
'base64': base64.encodestring(o).decode('utf-8'),
2015-03-16 19:28:34 +01:00
}
JSONEncoder.register(bytes, _bytes_encoder)
JSONEncoder.register(bytearray, _bytes_encoder)
JSONEncoder.register(Decimal,
lambda o: {
'__class__': 'Decimal',
'decimal': str(o),
})
2010-01-20 10:33:44 +01:00
2016-03-15 16:57:26 +01:00
class JSONRequest(Request):
parsed_content_type = 'json'
2010-01-20 10:33:44 +01:00
2016-03-15 16:57:26 +01:00
@cached_property
def parsed_data(self):
if self.parsed_content_type in self.environ.get('CONTENT_TYPE', ''):
try:
return json.loads(self.decoded_data, object_hook=JSONDecoder())
except Exception:
raise BadRequest('Unable to read JSON request')
else:
raise BadRequest('Not a JSON request')
2010-01-20 10:33:44 +01:00
2016-03-15 16:57:26 +01:00
@cached_property
def method(self):
return self.parsed_data['method']
2010-01-20 10:33:44 +01:00
2016-03-15 16:57:26 +01:00
@cached_property
def params(self):
return self.parsed_data['params']
2010-01-20 10:33:44 +01:00
2016-03-15 16:57:26 +01:00
class JSONProtocol:
content_type = 'json'
2010-01-20 10:33:44 +01:00
2016-03-15 16:57:26 +01:00
@classmethod
def request(cls, environ):
return JSONRequest(environ)
@classmethod
def response(cls, data, request):
if isinstance(request, JSONRequest):
response = {'id': request.parsed_data.get('id', 0)}
else:
response = {}
if isinstance(data, TrytonException):
response['error'] = data.args
elif isinstance(data, Exception):
tb_s = ''.join(traceback.format_exception(*sys.exc_info()))
2010-01-20 10:33:44 +01:00
for path in sys.path:
tb_s = tb_s.replace(path, '')
# report exception back to server
2016-03-15 16:57:26 +01:00
response['error'] = (str(data), tb_s)
2010-01-20 10:33:44 +01:00
else:
2016-03-15 16:57:26 +01:00
response['result'] = data
return Response(json.dumps(response, cls=JSONEncoder),
content_type='application/json')