xtquant.xtbson.bson36.son

Tools for creating and manipulating SON, the Serialized Ocument Notation.

Regular dictionaries can be used instead of SON objects, but not when the order of keys is important. A SON object can be used just like a normal Python dictionary.

  1# Copyright 2009-present MongoDB, Inc.
  2#
  3# Licensed under the Apache License, Version 2.0 (the "License");
  4# you may not use this file except in compliance with the License.
  5# You may obtain a copy of the License at
  6#
  7# http://www.apache.org/licenses/LICENSE-2.0
  8#
  9# Unless required by applicable law or agreed to in writing, software
 10# distributed under the License is distributed on an "AS IS" BASIS,
 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12# See the License for the specific language governing permissions and
 13# limitations under the License.
 14
 15"""Tools for creating and manipulating SON, the Serialized Ocument Notation.
 16
 17Regular dictionaries can be used instead of SON objects, but not when the order
 18of keys is important. A SON object can be used just like a normal Python
 19dictionary."""
 20
 21import copy
 22import re
 23from collections.abc import Mapping as _Mapping
 24
 25# This sort of sucks, but seems to be as good as it gets...
 26# This is essentially the same as re._pattern_type
 27RE_TYPE = type(re.compile(""))
 28
 29
 30class SON(dict):
 31    """SON data.
 32
 33    A subclass of dict that maintains ordering of keys and provides a
 34    few extra niceties for dealing with SON. SON provides an API
 35    similar to collections.OrderedDict.
 36    """
 37
 38    def __init__(self, data=None, **kwargs):
 39        self.__keys = []
 40        dict.__init__(self)
 41        self.update(data)
 42        self.update(kwargs)
 43
 44    def __new__(cls, *args, **kwargs):
 45        instance = super(SON, cls).__new__(cls, *args, **kwargs)
 46        instance.__keys = []
 47        return instance
 48
 49    def __repr__(self):
 50        result = []
 51        for key in self.__keys:
 52            result.append("(%r, %r)" % (key, self[key]))
 53        return "SON([%s])" % ", ".join(result)
 54
 55    def __setitem__(self, key, value):
 56        if key not in self.__keys:
 57            self.__keys.append(key)
 58        dict.__setitem__(self, key, value)
 59
 60    def __delitem__(self, key):
 61        self.__keys.remove(key)
 62        dict.__delitem__(self, key)
 63
 64    def copy(self):
 65        other = SON()
 66        other.update(self)
 67        return other
 68
 69    # TODO this is all from UserDict.DictMixin. it could probably be made more
 70    # efficient.
 71    # second level definitions support higher levels
 72    def __iter__(self):
 73        for k in self.__keys:
 74            yield k
 75
 76    def has_key(self, key):
 77        return key in self.__keys
 78
 79    def iterkeys(self):
 80        return self.__iter__()
 81
 82    # fourth level uses definitions from lower levels
 83    def itervalues(self):
 84        for _, v in self.items():
 85            yield v
 86
 87    def values(self):
 88        return [v for _, v in self.items()]
 89
 90    def clear(self):
 91        self.__keys = []
 92        super(SON, self).clear()
 93
 94    def setdefault(self, key, default=None):
 95        try:
 96            return self[key]
 97        except KeyError:
 98            self[key] = default
 99        return default
100
101    def pop(self, key, *args):
102        if len(args) > 1:
103            raise TypeError("pop expected at most 2 arguments, got " + repr(1 + len(args)))
104        try:
105            value = self[key]
106        except KeyError:
107            if args:
108                return args[0]
109            raise
110        del self[key]
111        return value
112
113    def popitem(self):
114        try:
115            k, v = next(iter(self.items()))
116        except StopIteration:
117            raise KeyError("container is empty")
118        del self[k]
119        return (k, v)
120
121    def update(self, other=None, **kwargs):
122        # Make progressively weaker assumptions about "other"
123        if other is None:
124            pass
125        elif hasattr(other, "items"):
126            for k, v in other.items():
127                self[k] = v
128        elif hasattr(other, "keys"):
129            for k in other.keys():
130                self[k] = other[k]
131        else:
132            for k, v in other:
133                self[k] = v
134        if kwargs:
135            self.update(kwargs)
136
137    def get(self, key, default=None):
138        try:
139            return self[key]
140        except KeyError:
141            return default
142
143    def __eq__(self, other):
144        """Comparison to another SON is order-sensitive while comparison to a
145        regular dictionary is order-insensitive.
146        """
147        if isinstance(other, SON):
148            return len(self) == len(other) and list(self.items()) == list(other.items())
149        return self.to_dict() == other
150
151    def __ne__(self, other):
152        return not self == other
153
154    def __len__(self):
155        return len(self.__keys)
156
157    def to_dict(self):
158        """Convert a SON document to a normal Python dictionary instance.
159
160        This is trickier than just *dict(...)* because it needs to be
161        recursive.
162        """
163
164        def transform_value(value):
165            if isinstance(value, list):
166                return [transform_value(v) for v in value]
167            elif isinstance(value, _Mapping):
168                return dict([(k, transform_value(v)) for k, v in value.items()])
169            else:
170                return value
171
172        return transform_value(dict(self))
173
174    def __deepcopy__(self, memo):
175        out = SON()
176        val_id = id(self)
177        if val_id in memo:
178            return memo.get(val_id)
179        memo[val_id] = out
180        for k, v in self.items():
181            if not isinstance(v, RE_TYPE):
182                v = copy.deepcopy(v, memo)
183            out[k] = v
184        return out
RE_TYPE = <class 're.Pattern'>
class SON(builtins.dict):
 31class SON(dict):
 32    """SON data.
 33
 34    A subclass of dict that maintains ordering of keys and provides a
 35    few extra niceties for dealing with SON. SON provides an API
 36    similar to collections.OrderedDict.
 37    """
 38
 39    def __init__(self, data=None, **kwargs):
 40        self.__keys = []
 41        dict.__init__(self)
 42        self.update(data)
 43        self.update(kwargs)
 44
 45    def __new__(cls, *args, **kwargs):
 46        instance = super(SON, cls).__new__(cls, *args, **kwargs)
 47        instance.__keys = []
 48        return instance
 49
 50    def __repr__(self):
 51        result = []
 52        for key in self.__keys:
 53            result.append("(%r, %r)" % (key, self[key]))
 54        return "SON([%s])" % ", ".join(result)
 55
 56    def __setitem__(self, key, value):
 57        if key not in self.__keys:
 58            self.__keys.append(key)
 59        dict.__setitem__(self, key, value)
 60
 61    def __delitem__(self, key):
 62        self.__keys.remove(key)
 63        dict.__delitem__(self, key)
 64
 65    def copy(self):
 66        other = SON()
 67        other.update(self)
 68        return other
 69
 70    # TODO this is all from UserDict.DictMixin. it could probably be made more
 71    # efficient.
 72    # second level definitions support higher levels
 73    def __iter__(self):
 74        for k in self.__keys:
 75            yield k
 76
 77    def has_key(self, key):
 78        return key in self.__keys
 79
 80    def iterkeys(self):
 81        return self.__iter__()
 82
 83    # fourth level uses definitions from lower levels
 84    def itervalues(self):
 85        for _, v in self.items():
 86            yield v
 87
 88    def values(self):
 89        return [v for _, v in self.items()]
 90
 91    def clear(self):
 92        self.__keys = []
 93        super(SON, self).clear()
 94
 95    def setdefault(self, key, default=None):
 96        try:
 97            return self[key]
 98        except KeyError:
 99            self[key] = default
100        return default
101
102    def pop(self, key, *args):
103        if len(args) > 1:
104            raise TypeError("pop expected at most 2 arguments, got " + repr(1 + len(args)))
105        try:
106            value = self[key]
107        except KeyError:
108            if args:
109                return args[0]
110            raise
111        del self[key]
112        return value
113
114    def popitem(self):
115        try:
116            k, v = next(iter(self.items()))
117        except StopIteration:
118            raise KeyError("container is empty")
119        del self[k]
120        return (k, v)
121
122    def update(self, other=None, **kwargs):
123        # Make progressively weaker assumptions about "other"
124        if other is None:
125            pass
126        elif hasattr(other, "items"):
127            for k, v in other.items():
128                self[k] = v
129        elif hasattr(other, "keys"):
130            for k in other.keys():
131                self[k] = other[k]
132        else:
133            for k, v in other:
134                self[k] = v
135        if kwargs:
136            self.update(kwargs)
137
138    def get(self, key, default=None):
139        try:
140            return self[key]
141        except KeyError:
142            return default
143
144    def __eq__(self, other):
145        """Comparison to another SON is order-sensitive while comparison to a
146        regular dictionary is order-insensitive.
147        """
148        if isinstance(other, SON):
149            return len(self) == len(other) and list(self.items()) == list(other.items())
150        return self.to_dict() == other
151
152    def __ne__(self, other):
153        return not self == other
154
155    def __len__(self):
156        return len(self.__keys)
157
158    def to_dict(self):
159        """Convert a SON document to a normal Python dictionary instance.
160
161        This is trickier than just *dict(...)* because it needs to be
162        recursive.
163        """
164
165        def transform_value(value):
166            if isinstance(value, list):
167                return [transform_value(v) for v in value]
168            elif isinstance(value, _Mapping):
169                return dict([(k, transform_value(v)) for k, v in value.items()])
170            else:
171                return value
172
173        return transform_value(dict(self))
174
175    def __deepcopy__(self, memo):
176        out = SON()
177        val_id = id(self)
178        if val_id in memo:
179            return memo.get(val_id)
180        memo[val_id] = out
181        for k, v in self.items():
182            if not isinstance(v, RE_TYPE):
183                v = copy.deepcopy(v, memo)
184            out[k] = v
185        return out

SON data.

A subclass of dict that maintains ordering of keys and provides a few extra niceties for dealing with SON. SON provides an API similar to collections.OrderedDict.

def copy(self):
65    def copy(self):
66        other = SON()
67        other.update(self)
68        return other

D.copy() -> a shallow copy of D

def has_key(self, key):
77    def has_key(self, key):
78        return key in self.__keys
def iterkeys(self):
80    def iterkeys(self):
81        return self.__iter__()
def itervalues(self):
84    def itervalues(self):
85        for _, v in self.items():
86            yield v
def values(self):
88    def values(self):
89        return [v for _, v in self.items()]

D.values() -> an object providing a view on D's values

def clear(self):
91    def clear(self):
92        self.__keys = []
93        super(SON, self).clear()

D.clear() -> None. Remove all items from D.

def setdefault(self, key, default=None):
 95    def setdefault(self, key, default=None):
 96        try:
 97            return self[key]
 98        except KeyError:
 99            self[key] = default
100        return default

Insert key with a value of default if key is not in the dictionary.

Return the value for key if key is in the dictionary, else default.

def pop(self, key, *args):
102    def pop(self, key, *args):
103        if len(args) > 1:
104            raise TypeError("pop expected at most 2 arguments, got " + repr(1 + len(args)))
105        try:
106            value = self[key]
107        except KeyError:
108            if args:
109                return args[0]
110            raise
111        del self[key]
112        return value

D.pop(k[,d]) -> v, remove specified key and return the corresponding value.

If the key is not found, return the default if given; otherwise, raise a KeyError.

def popitem(self):
114    def popitem(self):
115        try:
116            k, v = next(iter(self.items()))
117        except StopIteration:
118            raise KeyError("container is empty")
119        del self[k]
120        return (k, v)

Remove and return a (key, value) pair as a 2-tuple.

Pairs are returned in LIFO (last-in, first-out) order. Raises KeyError if the dict is empty.

def update(self, other=None, **kwargs):
122    def update(self, other=None, **kwargs):
123        # Make progressively weaker assumptions about "other"
124        if other is None:
125            pass
126        elif hasattr(other, "items"):
127            for k, v in other.items():
128                self[k] = v
129        elif hasattr(other, "keys"):
130            for k in other.keys():
131                self[k] = other[k]
132        else:
133            for k, v in other:
134                self[k] = v
135        if kwargs:
136            self.update(kwargs)

D.update([E, ]**F) -> None. Update D from dict/iterable E and F. If E is present and has a .keys() method, then does: for k in E: D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]

def get(self, key, default=None):
138    def get(self, key, default=None):
139        try:
140            return self[key]
141        except KeyError:
142            return default

Return the value for key if key is in the dictionary, else default.

def to_dict(self):
158    def to_dict(self):
159        """Convert a SON document to a normal Python dictionary instance.
160
161        This is trickier than just *dict(...)* because it needs to be
162        recursive.
163        """
164
165        def transform_value(value):
166            if isinstance(value, list):
167                return [transform_value(v) for v in value]
168            elif isinstance(value, _Mapping):
169                return dict([(k, transform_value(v)) for k, v in value.items()])
170            else:
171                return value
172
173        return transform_value(dict(self))

Convert a SON document to a normal Python dictionary instance.

This is trickier than just dict(...) because it needs to be recursive.

Inherited Members
builtins.dict
keys
items
fromkeys