diff --git a/rhodecode/lib/compat.py b/rhodecode/lib/compat.py --- a/rhodecode/lib/compat.py +++ b/rhodecode/lib/compat.py @@ -25,16 +25,93 @@ # along with this program. If not, see . import os +import datetime +import functools +import decimal from rhodecode import __platform__, PLATFORM_WIN #============================================================================== # json #============================================================================== + + +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 isinstance(obj, OrderedDict): + return obj.as_dict() + elif hasattr(obj, '__json__'): + if callable(obj.__json__): + return obj.__json__() + else: + return obj.__json__ + else: + raise NotImplementedError + try: import json + + # extended JSON encoder for json + class ExtendedEncoder(json.JSONEncoder): + def default(self, obj): + try: + return _obj_dump(obj) + except NotImplementedError: + pass + return json.JSONEncoder.default(self, obj) + # monkey-patch JSON encoder to use extended version + json.dumps = functools.partial(json.dumps, cls=ExtendedEncoder) except ImportError: import simplejson as json + def extended_encode(obj): + try: + return _obj_dump(obj) + except NotImplementedError: + pass + raise TypeError("%r is not JSON serializable" % (obj,)) + json.dumps = functools.partial(json.dumps, default=extended_encode) + #============================================================================== # izip_longest @@ -44,11 +121,11 @@ try: except ImportError: import itertools - def izip_longest(*args, **kwds): # noqa + def izip_longest(*args, **kwds): fillvalue = kwds.get("fillvalue") def sentinel(counter=([fillvalue] * (len(args) - 1)).pop): - yield counter() # yields the fillvalue, or raises IndexError + yield counter() # yields the fillvalue, or raises IndexError fillers = itertools.repeat(fillvalue) iters = [itertools.chain(it, sentinel(), fillers)