ext_json.py
148 lines
| 4.2 KiB
| text/x-python
|
PythonLexer
r2 | # -*- coding: utf-8 -*- | |||
r112 | # Copyright 2010 - 2017 RhodeCode GmbH and the AppEnlight project authors | |||
r2 | # | |||
r112 | # Licensed under the Apache License, Version 2.0 (the "License"); | |||
# you may not use this file except in compliance with the License. | ||||
# You may obtain a copy of the License at | ||||
r2 | # | |||
r112 | # http://www.apache.org/licenses/LICENSE-2.0 | |||
r2 | # | |||
r112 | # Unless required by applicable law or agreed to in writing, software | |||
# distributed under the License is distributed on an "AS IS" BASIS, | ||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
# See the License for the specific language governing permissions and | ||||
# limitations under the License. | ||||
r2 | ||||
""" | ||||
ex-json borrowed from Marcin Kuzminski | ||||
source: https://secure.rhodecode.org/ext-json | ||||
""" | ||||
import datetime | ||||
import functools | ||||
import decimal | ||||
import imp | ||||
__all__ = ['json', 'simplejson', 'stdlibjson'] | ||||
def _is_aware(value): | ||||
""" | ||||
Determines if a given datetime.time is aware. | ||||
The logic is described in Python's docs: | ||||
http://docs.python.org/library/datetime.html#datetime.tzinfo | ||||
""" | ||||
return (value.tzinfo is not None | ||||
and value.tzinfo.utcoffset(value) is not None) | ||||
def _obj_dump(obj): | ||||
""" | ||||
Custom function for dumping objects to JSON, if obj has __json__ attribute | ||||
or method defined it will be used for serialization | ||||
:param obj: | ||||
""" | ||||
if isinstance(obj, complex): | ||||
return [obj.real, obj.imag] | ||||
# See "Date Time String Format" in the ECMA-262 specification. | ||||
# some code borrowed from django 1.4 | ||||
elif isinstance(obj, datetime.datetime): | ||||
r = obj.isoformat() | ||||
# if obj.microsecond: | ||||
# r = r[:23] + r[26:] | ||||
if r.endswith('+00:00'): | ||||
r = r[:-6] + 'Z' | ||||
return r | ||||
elif isinstance(obj, datetime.date): | ||||
return obj.isoformat() | ||||
elif isinstance(obj, decimal.Decimal): | ||||
return str(obj) | ||||
elif isinstance(obj, datetime.time): | ||||
if _is_aware(obj): | ||||
raise ValueError("JSON can't represent timezone-aware times.") | ||||
r = obj.isoformat() | ||||
if obj.microsecond: | ||||
r = r[:12] | ||||
return r | ||||
elif isinstance(obj, set): | ||||
return list(obj) | ||||
elif hasattr(obj, '__json__'): | ||||
if callable(obj.__json__): | ||||
return obj.__json__() | ||||
else: | ||||
return obj.__json__ | ||||
else: | ||||
raise NotImplementedError | ||||
# Import simplejson | ||||
try: | ||||
# import simplejson initially | ||||
_sj = imp.load_module('_sj', *imp.find_module('simplejson')) | ||||
def extended_encode(obj): | ||||
try: | ||||
return _obj_dump(obj) | ||||
except NotImplementedError: | ||||
pass | ||||
raise TypeError("%r is not JSON serializable" % (obj,)) | ||||
# we handle decimals our own it makes unified behavior of json vs | ||||
# simplejson | ||||
sj_version = [int(x) for x in _sj.__version__.split('.')] | ||||
major, minor = sj_version[0], sj_version[1] | ||||
if major < 2 or (major == 2 and minor < 1): | ||||
# simplejson < 2.1 doesnt support use_decimal | ||||
_sj.dumps = functools.partial( | ||||
_sj.dumps, default=extended_encode) | ||||
_sj.dump = functools.partial( | ||||
_sj.dump, default=extended_encode) | ||||
else: | ||||
_sj.dumps = functools.partial( | ||||
_sj.dumps, default=extended_encode, use_decimal=False) | ||||
_sj.dump = functools.partial( | ||||
_sj.dump, default=extended_encode, use_decimal=False) | ||||
simplejson = _sj | ||||
except ImportError: | ||||
# no simplejson set it to None | ||||
simplejson = None | ||||
try: | ||||
# simplejson not found try out regular json module | ||||
_json = imp.load_module('_json', *imp.find_module('json')) | ||||
# extended JSON encoder for json | ||||
class ExtendedEncoder(_json.JSONEncoder): | ||||
def default(self, obj): | ||||
try: | ||||
return _obj_dump(obj) | ||||
except NotImplementedError: | ||||
pass | ||||
raise TypeError("%r is not JSON serializable" % (obj,)) | ||||
# monkey-patch JSON encoder to use extended version | ||||
_json.dumps = functools.partial(_json.dumps, cls=ExtendedEncoder) | ||||
_json.dump = functools.partial(_json.dump, cls=ExtendedEncoder) | ||||
except ImportError: | ||||
json = None | ||||
stdlibjson = _json | ||||
# set all available json modules | ||||
if simplejson: | ||||
json = _sj | ||||
elif _json: | ||||
json = _json | ||||
else: | ||||
raise ImportError('Could not find any json modules') | ||||