~libra-core/libra/master

« back to all changes in this revision

Viewing changes to libra/openstack/common/jsonutils.py

  • Committer: Monty Taylor
  • Date: 2015-10-17 20:03:27 UTC
  • Revision ID: git-v1:c7082fa72ac73b23b48ce63fc82aa7da2d3e5d6a
Retire stackforge/libra

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
 
 
3
 
# Copyright 2010 United States Government as represented by the
4
 
# Administrator of the National Aeronautics and Space Administration.
5
 
# Copyright 2011 Justin Santa Barbara
6
 
# All Rights Reserved.
7
 
#
8
 
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
9
 
#    not use this file except in compliance with the License. You may obtain
10
 
#    a copy of the License at
11
 
#
12
 
#         http://www.apache.org/licenses/LICENSE-2.0
13
 
#
14
 
#    Unless required by applicable law or agreed to in writing, software
15
 
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16
 
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17
 
#    License for the specific language governing permissions and limitations
18
 
#    under the License.
19
 
 
20
 
'''
21
 
JSON related utilities.
22
 
 
23
 
This module provides a few things:
24
 
 
25
 
    1) A handy function for getting an object down to something that can be
26
 
    JSON serialized.  See to_primitive().
27
 
 
28
 
    2) Wrappers around loads() and dumps().  The dumps() wrapper will
29
 
    automatically use to_primitive() for you if needed.
30
 
 
31
 
    3) This sets up anyjson to use the loads() and dumps() wrappers if anyjson
32
 
    is available.
33
 
'''
34
 
 
35
 
 
36
 
import datetime
37
 
import functools
38
 
import inspect
39
 
import itertools
40
 
import json
41
 
try:
42
 
    import xmlrpclib
43
 
except ImportError:
44
 
    # NOTE(jd): xmlrpclib is not shipped with Python 3
45
 
    xmlrpclib = None
46
 
 
47
 
import six
48
 
 
49
 
from libra.openstack.common import gettextutils
50
 
from libra.openstack.common import importutils
51
 
from libra.openstack.common import timeutils
52
 
 
53
 
netaddr = importutils.try_import("netaddr")
54
 
 
55
 
_nasty_type_tests = [inspect.ismodule, inspect.isclass, inspect.ismethod,
56
 
                     inspect.isfunction, inspect.isgeneratorfunction,
57
 
                     inspect.isgenerator, inspect.istraceback, inspect.isframe,
58
 
                     inspect.iscode, inspect.isbuiltin, inspect.isroutine,
59
 
                     inspect.isabstract]
60
 
 
61
 
_simple_types = (six.string_types + six.integer_types
62
 
                 + (type(None), bool, float))
63
 
 
64
 
 
65
 
def to_primitive(value, convert_instances=False, convert_datetime=True,
66
 
                 level=0, max_depth=3):
67
 
    """Convert a complex object into primitives.
68
 
 
69
 
    Handy for JSON serialization. We can optionally handle instances,
70
 
    but since this is a recursive function, we could have cyclical
71
 
    data structures.
72
 
 
73
 
    To handle cyclical data structures we could track the actual objects
74
 
    visited in a set, but not all objects are hashable. Instead we just
75
 
    track the depth of the object inspections and don't go too deep.
76
 
 
77
 
    Therefore, convert_instances=True is lossy ... be aware.
78
 
 
79
 
    """
80
 
    # handle obvious types first - order of basic types determined by running
81
 
    # full tests on nova project, resulting in the following counts:
82
 
    # 572754 <type 'NoneType'>
83
 
    # 460353 <type 'int'>
84
 
    # 379632 <type 'unicode'>
85
 
    # 274610 <type 'str'>
86
 
    # 199918 <type 'dict'>
87
 
    # 114200 <type 'datetime.datetime'>
88
 
    #  51817 <type 'bool'>
89
 
    #  26164 <type 'list'>
90
 
    #   6491 <type 'float'>
91
 
    #    283 <type 'tuple'>
92
 
    #     19 <type 'long'>
93
 
    if isinstance(value, _simple_types):
94
 
        return value
95
 
 
96
 
    if isinstance(value, datetime.datetime):
97
 
        if convert_datetime:
98
 
            return timeutils.strtime(value)
99
 
        else:
100
 
            return value
101
 
 
102
 
    # value of itertools.count doesn't get caught by nasty_type_tests
103
 
    # and results in infinite loop when list(value) is called.
104
 
    if type(value) == itertools.count:
105
 
        return six.text_type(value)
106
 
 
107
 
    # FIXME(vish): Workaround for LP bug 852095. Without this workaround,
108
 
    #              tests that raise an exception in a mocked method that
109
 
    #              has a @wrap_exception with a notifier will fail. If
110
 
    #              we up the dependency to 0.5.4 (when it is released) we
111
 
    #              can remove this workaround.
112
 
    if getattr(value, '__module__', None) == 'mox':
113
 
        return 'mock'
114
 
 
115
 
    if level > max_depth:
116
 
        return '?'
117
 
 
118
 
    # The try block may not be necessary after the class check above,
119
 
    # but just in case ...
120
 
    try:
121
 
        recursive = functools.partial(to_primitive,
122
 
                                      convert_instances=convert_instances,
123
 
                                      convert_datetime=convert_datetime,
124
 
                                      level=level,
125
 
                                      max_depth=max_depth)
126
 
        if isinstance(value, dict):
127
 
            return dict((k, recursive(v)) for k, v in value.iteritems())
128
 
        elif isinstance(value, (list, tuple)):
129
 
            return [recursive(lv) for lv in value]
130
 
 
131
 
        # It's not clear why xmlrpclib created their own DateTime type, but
132
 
        # for our purposes, make it a datetime type which is explicitly
133
 
        # handled
134
 
        if xmlrpclib and isinstance(value, xmlrpclib.DateTime):
135
 
            value = datetime.datetime(*tuple(value.timetuple())[:6])
136
 
 
137
 
        if convert_datetime and isinstance(value, datetime.datetime):
138
 
            return timeutils.strtime(value)
139
 
        elif isinstance(value, gettextutils.Message):
140
 
            return value.data
141
 
        elif hasattr(value, 'iteritems'):
142
 
            return recursive(dict(value.iteritems()), level=level + 1)
143
 
        elif hasattr(value, '__iter__'):
144
 
            return recursive(list(value))
145
 
        elif convert_instances and hasattr(value, '__dict__'):
146
 
            # Likely an instance of something. Watch for cycles.
147
 
            # Ignore class member vars.
148
 
            return recursive(value.__dict__, level=level + 1)
149
 
        elif netaddr and isinstance(value, netaddr.IPAddress):
150
 
            return six.text_type(value)
151
 
        else:
152
 
            if any(test(value) for test in _nasty_type_tests):
153
 
                return six.text_type(value)
154
 
            return value
155
 
    except TypeError:
156
 
        # Class objects are tricky since they may define something like
157
 
        # __iter__ defined but it isn't callable as list().
158
 
        return six.text_type(value)
159
 
 
160
 
 
161
 
def dumps(value, default=to_primitive, **kwargs):
162
 
    return json.dumps(value, default=default, **kwargs)
163
 
 
164
 
 
165
 
def loads(s):
166
 
    return json.loads(s)
167
 
 
168
 
 
169
 
def load(s):
170
 
    return json.load(s)
171
 
 
172
 
 
173
 
try:
174
 
    import anyjson
175
 
except ImportError:
176
 
    pass
177
 
else:
178
 
    anyjson._modules.append((__name__, 'dumps', TypeError,
179
 
                                       'loads', ValueError, 'load'))
180
 
    anyjson.force_implementation(__name__)