ext_json.py
153 lines
| 4.4 KiB
| text/x-python
|
PythonLexer
r2 | # -*- coding: utf-8 -*- | |||
# Copyright (C) 2010-2016 RhodeCode GmbH | ||||
# | ||||
# This program is free software: you can redistribute it and/or modify | ||||
# it under the terms of the GNU Affero General Public License, version 3 | ||||
# (only), as published by the Free Software Foundation. | ||||
# | ||||
# This program is distributed in the hope that it will be useful, | ||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
# GNU General Public License for more details. | ||||
# | ||||
# You should have received a copy of the GNU Affero General Public License | ||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
# | ||||
# This program is dual-licensed. If you wish to learn more about the | ||||
# App Enlight Enterprise Edition, including its added features, Support | ||||
# services, and proprietary license terms, please see | ||||
# https://rhodecode.com/licenses/ | ||||
""" | ||||
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') | ||||