Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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

# vim: tabstop=4 shiftwidth=4 softtabstop=4 

 

# Copyright 2010 United States Government as represented by the 

# Administrator of the National Aeronautics and Space Administration. 

# Copyright 2011 Justin Santa Barbara 

# All Rights Reserved. 

# 

#    Licensed under the Apache License, Version 2.0 (the "License"); you may 

#    not use this file except in compliance with the License. You may obtain 

#    a copy of the License at 

# 

#         http://www.apache.org/licenses/LICENSE-2.0 

# 

#    Unless required by applicable law or agreed to in writing, software 

#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 

#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 

#    License for the specific language governing permissions and limitations 

#    under the License. 

 

''' 

JSON related utilities. 

 

This module provides a few things: 

 

    1) A handy function for getting an object down to something that can be 

    JSON serialized.  See to_primitive(). 

 

    2) Wrappers around loads() and dumps().  The dumps() wrapper will 

    automatically use to_primitive() for you if needed. 

 

    3) This sets up anyjson to use the loads() and dumps() wrappers if anyjson 

    is available. 

''' 

 

 

import datetime 

import functools 

import inspect 

import itertools 

import json 

import xmlrpclib 

 

from keystone.openstack.common import timeutils 

 

 

def to_primitive(value, convert_instances=False, convert_datetime=True, 

                 level=0, max_depth=3): 

    """Convert a complex object into primitives. 

 

    Handy for JSON serialization. We can optionally handle instances, 

    but since this is a recursive function, we could have cyclical 

    data structures. 

 

    To handle cyclical data structures we could track the actual objects 

    visited in a set, but not all objects are hashable. Instead we just 

    track the depth of the object inspections and don't go too deep. 

 

    Therefore, convert_instances=True is lossy ... be aware. 

 

    """ 

    nasty = [inspect.ismodule, inspect.isclass, inspect.ismethod, 

             inspect.isfunction, inspect.isgeneratorfunction, 

             inspect.isgenerator, inspect.istraceback, inspect.isframe, 

             inspect.iscode, inspect.isbuiltin, inspect.isroutine, 

             inspect.isabstract] 

    for test in nasty: 

        if test(value): 

            return unicode(value) 

 

    # value of itertools.count doesn't get caught by inspects 

    # above and results in infinite loop when list(value) is called. 

    if type(value) == itertools.count: 

        return unicode(value) 

 

    # FIXME(vish): Workaround for LP bug 852095. Without this workaround, 

    #              tests that raise an exception in a mocked method that 

    #              has a @wrap_exception with a notifier will fail. If 

    #              we up the dependency to 0.5.4 (when it is released) we 

    #              can remove this workaround. 

    if getattr(value, '__module__', None) == 'mox': 

        return 'mock' 

 

    if level > max_depth: 

        return '?' 

 

    # The try block may not be necessary after the class check above, 

    # but just in case ... 

    try: 

        recursive = functools.partial(to_primitive, 

                                      convert_instances=convert_instances, 

                                      convert_datetime=convert_datetime, 

                                      level=level, 

                                      max_depth=max_depth) 

        # It's not clear why xmlrpclib created their own DateTime type, but 

        # for our purposes, make it a datetime type which is explicitly 

        # handled 

        if isinstance(value, xmlrpclib.DateTime): 

            value = datetime.datetime(*tuple(value.timetuple())[:6]) 

 

        if isinstance(value, (list, tuple)): 

            return [recursive(v) for v in value] 

        elif isinstance(value, dict): 

            return dict((k, recursive(v)) for k, v in value.iteritems()) 

        elif convert_datetime and isinstance(value, datetime.datetime): 

            return timeutils.strtime(value) 

        elif hasattr(value, 'iteritems'): 

            return recursive(dict(value.iteritems()), level=level + 1) 

        elif hasattr(value, '__iter__'): 

            return recursive(list(value)) 

        elif convert_instances and hasattr(value, '__dict__'): 

            # Likely an instance of something. Watch for cycles. 

            # Ignore class member vars. 

            return recursive(value.__dict__, level=level + 1) 

        else: 

            return value 

    except TypeError: 

        # Class objects are tricky since they may define something like 

        # __iter__ defined but it isn't callable as list(). 

        return unicode(value) 

 

 

def dumps(value, default=to_primitive, **kwargs): 

    return json.dumps(value, default=default, **kwargs) 

 

 

def loads(s): 

    return json.loads(s) 

 

 

def load(s): 

    return json.load(s) 

 

 

try: 

    import anyjson 

except ImportError: 

    pass 

else: 

    anyjson._modules.append((__name__, 'dumps', TypeError, 

                                       'loads', ValueError, 'load')) 

    anyjson.force_implementation(__name__)