00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 """Tools for creating and manipulating SON, the Serialized Ocument Notation.
00016
00017 Regular dictionaries can be used instead of SON objects, but not when the order
00018 of keys is important. A SON object can be used just like a normal Python
00019 dictionary."""
00020
00021 import copy
00022
00023 class SON(dict):
00024 """SON data.
00025
00026 A subclass of dict that maintains ordering of keys and provides a
00027 few extra niceties for dealing with SON. SON objects can be
00028 converted to and from BSON.
00029
00030 The mapping from Python types to BSON types is as follows:
00031
00032 =================================== ============= ===================
00033 Python Type BSON Type Supported Direction
00034 =================================== ============= ===================
00035 None null both
00036 bool boolean both
00037 int number (int) both
00038 float number (real) both
00039 string string py -> bson
00040 unicode string both
00041 list array both
00042 dict / `SON` object both
00043 datetime.datetime [#dt]_ [#dt2]_ date both
00044 compiled re regex both
00045 `bson.binary.Binary` binary both
00046 `bson.objectid.ObjectId` oid both
00047 `bson.dbref.DBRef` dbref both
00048 None undefined bson -> py
00049 unicode code bson -> py
00050 `bson.code.Code` code py -> bson
00051 unicode symbol bson -> py
00052 =================================== ============= ===================
00053
00054 Note that to save binary data it must be wrapped as an instance of
00055 `bson.binary.Binary`. Otherwise it will be saved as a BSON string
00056 and retrieved as unicode.
00057
00058 .. [#dt] datetime.datetime instances will be rounded to the nearest
00059 millisecond when saved
00060 .. [#dt2] all datetime.datetime instances are treated as *naive*. clients
00061 should always use UTC.
00062 """
00063
00064 def __init__(self, data=None, **kwargs):
00065 self.__keys = []
00066 dict.__init__(self)
00067 self.update(data)
00068 self.update(kwargs)
00069
00070 def __repr__(self):
00071 result = []
00072 for key in self.__keys:
00073 result.append("(%r, %r)" % (key, self[key]))
00074 return "SON([%s])" % ", ".join(result)
00075
00076 def __setitem__(self, key, value):
00077 if key not in self:
00078 self.__keys.append(key)
00079 dict.__setitem__(self, key, value)
00080
00081 def __delitem__(self, key):
00082 self.__keys.remove(key)
00083 dict.__delitem__(self, key)
00084
00085 def keys(self):
00086 return list(self.__keys)
00087
00088 def copy(self):
00089 other = SON()
00090 other.update(self)
00091 return other
00092
00093
00094
00095
00096 def __iter__(self):
00097 for k in self.keys():
00098 yield k
00099
00100 def has_key(self, key):
00101 return key in self.keys()
00102
00103 def __contains__(self, key):
00104 return key in self.keys()
00105
00106
00107 def iteritems(self):
00108 for k in self:
00109 yield (k, self[k])
00110
00111 def iterkeys(self):
00112 return self.__iter__()
00113
00114
00115 def itervalues(self):
00116 for _, v in self.iteritems():
00117 yield v
00118
00119 def values(self):
00120 return [v for _, v in self.iteritems()]
00121
00122 def items(self):
00123 return list(self.iteritems())
00124
00125 def clear(self):
00126 for key in self.keys():
00127 del self[key]
00128
00129 def setdefault(self, key, default=None):
00130 try:
00131 return self[key]
00132 except KeyError:
00133 self[key] = default
00134 return default
00135
00136 def pop(self, key, *args):
00137 if len(args) > 1:
00138 raise TypeError("pop expected at most 2 arguments, got "\
00139 + repr(1 + len(args)))
00140 try:
00141 value = self[key]
00142 except KeyError:
00143 if args:
00144 return args[0]
00145 raise
00146 del self[key]
00147 return value
00148
00149 def popitem(self):
00150 try:
00151 k, v = self.iteritems().next()
00152 except StopIteration:
00153 raise KeyError('container is empty')
00154 del self[k]
00155 return (k, v)
00156
00157 def update(self, other=None, **kwargs):
00158
00159 if other is None:
00160 pass
00161 elif hasattr(other, 'iteritems'):
00162 for k, v in other.iteritems():
00163 self[k] = v
00164 elif hasattr(other, 'keys'):
00165 for k in other.keys():
00166 self[k] = other[k]
00167 else:
00168 for k, v in other:
00169 self[k] = v
00170 if kwargs:
00171 self.update(kwargs)
00172
00173 def get(self, key, default=None):
00174 try:
00175 return self[key]
00176 except KeyError:
00177 return default
00178
00179 def __cmp__(self, other):
00180 if isinstance(other, SON):
00181 return cmp((dict(self.iteritems()), self.keys()),
00182 (dict(other.iteritems()), other.keys()))
00183 return cmp(dict(self.iteritems()), other)
00184
00185 def __len__(self):
00186 return len(self.keys())
00187
00188 def to_dict(self):
00189 """Convert a SON document to a normal Python dictionary instance.
00190
00191 This is trickier than just *dict(...)* because it needs to be
00192 recursive.
00193 """
00194
00195 def transform_value(value):
00196 if isinstance(value, list):
00197 return [transform_value(v) for v in value]
00198 if isinstance(value, SON):
00199 value = dict(value)
00200 if isinstance(value, dict):
00201 for k, v in value.iteritems():
00202 value[k] = transform_value(v)
00203 return value
00204
00205 return transform_value(dict(self))
00206
00207 def __deepcopy__(self, memo):
00208 out = SON()
00209 for k, v in self.iteritems():
00210 out[k] = copy.deepcopy(v, memo)
00211 return out