view kallithea/lib/ext_json.py @ 6938:6fde53180c50

diffs: wrap vcs repo get_diff Refactor to get a single place for diff error handling outside the vcs lib.
author Mads Kiilerich <mads@kiilerich.com>
date Sun, 22 Oct 2017 00:42:12 +0200
parents 7691290837d2
children 0a277465fddf
line wrap: on
line source

"""
Extended JSON encoder for json

json.org does not specify how date time can be represented - monkeypatch it to do something.
"""

import datetime
import functools
import decimal
import json # is re-exported after monkey patching

__all__ = ['json']


def _is_tz_aware(value):
    """
    Determines if a given datetime.time is timezone 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_tz_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


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 and export JSON encoder to use custom encoding method
json.dumps = functools.partial(json.dumps, cls=ExtendedEncoder)
json.dump = functools.partial(json.dump, cls=ExtendedEncoder)