File: jsonify.py

package info (click to toggle)
turbojson 1.3.2-2.1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, buster, jessie, jessie-kfreebsd, sid, stretch
  • size: 160 kB
  • ctags: 138
  • sloc: python: 426; makefile: 10
file content (218 lines) | stat: -rw-r--r-- 6,052 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
"""JSON encoding functions using PEAK-Rules."""

from datetime import date, datetime
from decimal import Decimal

from peak.rules import abstract, around, when

# simplejson is actually available as json in the standard library
# since Python 2.6, but the externally maintained package is newer
# and can be substantially faster, so we try to import this first
try:
    from simplejson import JSONEncoder # externally maintained version
except ImportError:
    from json import JSONEncoder # standard lib version

__all__ = ['jsonify', 'encode', 'encode_iter']


# Global options

descent_bases = True


# Specific encoding functions

def jsonify(obj):
    """Generic function for converting objects to JSON.

    Specific functions should return a string or an object that can be
    serialized with JSON, i.e., it is made up of only lists, dictionaries
    (with string keys), strings, ints, and floats.

    """
    return jsonify_default(obj)

# This is for easier usage and backward compatibility:
jsonify.when = when.__get__(jsonify)
jsonify.around = around.__get__(jsonify)


# Explicit encoding function

@jsonify.around("hasattr(obj, '__json__')")
def jsonify_explicit(obj):
    """JSONify objects with explicit JSONification method."""
    return obj.__json__()


# Default encoding functions

@abstract
def jsonify_default(obj):
    """Default JSONify method for Python objects."""
    return NotImplementedError

jsonify_default.when = when.__get__(jsonify_default)
jsonify_default.around = around.__get__(jsonify_default)

@jsonify_default.when("obj is None"
    " or isinstance(obj, (basestring, bool, int, long, float, list, dict))")
def jsonify_serializable(obj):
    """JSONify simple serializable objects."""
    return obj

@jsonify_default.when("isinstance(obj, (set, frozenset, tuple))")
def jsonify_list_like(obj):
    """JSONify simple list-like objects."""
    return list(obj)

@jsonify_default.when("isinstance(obj, Decimal)")
def jsonify_decimal(obj):
    """JSONify Decimal objects."""
    return float(obj)

@jsonify.when("isinstance(obj, (date, datetime))")
def jsonify_datetime(obj):
    """JSONify date and datetime objects."""
    return str(obj)


# SQLObject support

try:
    from sqlobject import SQLObject

    def _sqlobject_attrs(obj):
        """Get all attributes of an SQLObject."""
        sm = obj.__class__.sqlmeta
        try:
            while sm is not None:
                # we need to exclude the ID-keys, as for some reason
                # this won't work for subclassed items
                for key in sm.columns:
                    if key[-2:] != 'ID':
                        yield key
                sm = descent_bases and sm.__base__ or None
        except AttributeError: # happens if we descent to <type object>
            pass

    def is_sqlobject(obj):
        return (isinstance(obj, SQLObject)
            and hasattr(obj.__class__, 'sqlmeta'))

    @jsonify_default.when("is_sqlobject(obj)")
    def jsonify_sqlobject(obj):
        """JSONify SQLObjects."""
        result = {'id': obj.id}
        for name in _sqlobject_attrs(obj):
            result[name] = getattr(obj, name)
        return result

    try:
        SelectResultsClass = SQLObject.SelectResultsClass
    except AttributeError:
        pass
    else:

        @jsonify_default.when("isinstance(obj, SelectResultsClass)")
        def jsonify_select_results(obj):
            """JSONify SQLObject.SelectResults."""
            return list(obj)

except ImportError:
    pass


# SQLAlchemy support

try:
    import sqlalchemy

    try:
        import sqlalchemy.ext.selectresults
        from sqlalchemy.util import OrderedProperties
    except ImportError: # SQLAlchemy >= 0.5

        def is_saobject(obj):
            return hasattr(obj, '_sa_class_manager')

        @jsonify_default.when("is_saobject(obj)")
        def jsonify_saobject(obj):
            """JSONify SQLAlchemy objects."""
            props = {}
            for key in obj.__dict__:
                if not key.startswith('_sa_'):
                    props[key] = getattr(obj, key)
            return props

    else: # SQLAlchemy < 0.5

        def is_saobject(obj):
            return (hasattr(obj, 'c')
                and isinstance(obj.c, OrderedProperties))

        @jsonify_default.when("is_saobject(obj)")
        def jsonify_saobject(obj):
            """JSONify SQLAlchemy objects."""
            props = {}
            for key in obj.c.keys():
                props[key] = getattr(obj, key)
            return props

        try:
            from sqlalchemy.orm.attributes import InstrumentedList
        except ImportError: # SQLAlchemy >= 0.4
            pass # normal lists are used here

        else: # SQLAlchemy < 0.4

            @jsonify_default.when("isinstance(obj, InstrumentedList)")
            def jsonify_instrumented_list(obj):
                """JSONify SQLAlchemy instrumented lists."""
                return list(obj)

    try:
        from sqlalchemy.engine.base import ResultProxy, RowProxy
    except ImportError:
        # SQLAlchemy >= 0.8
        from sqlalchemy.engine.result import ResultProxy, RowProxy

    @jsonify_default.when("isinstance(obj, ResultProxy)")
    def jsonify_saproxy(obj):
        return list(obj)

    @jsonify_default.when("isinstance(obj, RowProxy)")
    def jsonify_saproxy(obj):
        return dict(obj)

except ImportError:
    pass


# JSON encoder class

class GenericJSON(JSONEncoder):

    def __init__(self, **opts):
        opt = opts.pop('descent_bases', None)
        if opt is not None:
            global descent_bases
            descent_bases = opt
        super(GenericJSON, self).__init__(**opts)

    def default(self, obj):
        return jsonify(obj)

_instance = GenericJSON()


# General encoding functions

def encode(obj):
    """Return a JSON string representation of a Python object."""
    return _instance.encode(obj)

def encode_iter(obj):
    """Encode object, yielding each string representation as available."""
    return _instance.iterencode(obj)