flatdict.py
Go to the documentation of this file.
1 """FlatDict is a dict object that allows for single level, delimited
2 key/value pair mapping of nested dictionaries.
3 
4 """
5 import collections
6 
7 __version__ = '3.1.0'
8 
9 NO_DEFAULT = object()
10 
11 # Python 2/3 string compat
12 try:
13  basestring
14 except NameError:
15  basestring = str
16 
17 
18 class FlatDict(collections.MutableMapping):
19  """:class:`~flatdict.FlatDict` is a dictionary object that allows for
20  single level, delimited key/value pair mapping of nested dictionaries.
21  The default delimiter value is ``:`` but can be changed in the constructor
22  or by calling :meth:`FlatDict.set_delimiter`.
23 
24  """
25  _COERCE = dict
26 
27  def __init__(self, value=None, delimiter=':'):
28  super(FlatDict, self).__init__()
29  self._values = {}
30  self._delimiter = delimiter
31  self.update(value)
32 
33  def __contains__(self, key):
34  """Check to see if the key exists, checking for both delimited and
35  not delimited key values.
36 
37  :param mixed key: The key to check for
38 
39  """
40  if self._has_delimiter(key):
41  pk, ck = key.split(self._delimiter, 1)
42  return pk in self._values and ck in self._values[pk]
43  return key in self._values
44 
45  def __delitem__(self, key):
46  """Delete the item for the specified key, automatically dealing with
47  nested children.
48 
49  :param mixed key: The key to use
50  :raises: KeyError
51 
52  """
53  if key not in self:
54  raise KeyError
55  if self._has_delimiter(key):
56  pk, ck = key.split(self._delimiter, 1)
57  del self._values[pk][ck]
58  if not self._values[pk]:
59  del self._values[pk]
60  else:
61  del self._values[key]
62 
63  def __eq__(self, other):
64  """Check for equality against the other value
65 
66  :param other: The value to compare
67  :type other: FlatDict
68  :rtype: bool
69  :raises: TypeError
70 
71  """
72  if isinstance(other, dict):
73  return sorted(self.as_dict()) == sorted(other)
74  elif not isinstance(other, self.__class__):
75  raise TypeError
76  return sorted(self.as_dict()) == sorted(other.as_dict())
77 
78  def __ne__(self, other):
79  """Check for inequality against the other value
80 
81  :param other: The value to compare
82  :type other: dict or FlatDict
83  :rtype: bool
84 
85  """
86  return not self.__eq__(other)
87 
88  def __getitem__(self, key):
89  """Get an item for the specified key, automatically dealing with
90  nested children.
91 
92  :param mixed key: The key to use
93  :rtype: mixed
94  :raises: KeyError
95 
96  """
97  values = self._values
98  for part in key.split(self._delimiter):
99  values = values[part]
100  return values
101 
102  def __iter__(self):
103  """Iterate over the flat dictionary key and values
104 
105  :rtype: Iterator
106  :raises: RuntimeError
107 
108  """
109  return iter(self.keys())
110 
111  def __len__(self):
112  """Return the number of items.
113 
114  :rtype: int
115 
116  """
117  return len(self.keys())
118 
119  def __reduce__(self):
120  """Return state information for pickling
121 
122  :rtype: tuple
123 
124  """
125  return type(self), (self.as_dict(), self._delimiter)
126 
127  def __repr__(self):
128  """Return the string representation of the instance.
129 
130  :rtype: str
131 
132  """
133  return '<{} id={} {}>"'.format(
134  self.__class__.__name__, id(self), str(self))
135 
136  def __setitem__(self, key, value):
137  """Assign the value to the key, dynamically building nested
138  FlatDict items where appropriate.
139 
140  :param mixed key: The key for the item
141  :param mixed value: The value for the item
142  :raises: TypeError
143 
144  """
145  if isinstance(value, self._COERCE) and not isinstance(value, FlatDict):
146  value = self.__class__(value, self._delimiter)
147  if self._has_delimiter(key):
148  pk, ck = key.split(self._delimiter, 1)
149  if pk not in self._values:
150  self._values[pk] = self.__class__({ck: value}, self._delimiter)
151  return
152  elif not isinstance(self._values[pk], FlatDict):
153  raise TypeError(
154  'Assignment to invalid type for key {}'.format(pk))
155  self._values[pk][ck] = value
156  else:
157  self._values[key] = value
158 
159  def __str__(self):
160  """Return the string value of the instance.
161 
162  :rtype: str
163 
164  """
165  return '{{{}}}'.format(', '.join(
166  ['{!r}: {!r}'.format(k, self[k]) for k in self.keys()]))
167 
168  def as_dict(self):
169  """Return the :class:`~flatdict.FlatDict` as a :class:`dict`
170 
171  :rtype: dict
172 
173  """
174  out = dict({})
175  for key in self.keys():
176  if self._has_delimiter(key):
177  pk, ck = key.split(self._delimiter, 1)
178  if self._has_delimiter(ck):
179  ck = ck.split(self._delimiter, 1)[0]
180  if isinstance(self._values[pk], FlatDict) and pk not in out:
181  out[pk] = dict()
182  if isinstance(self._values[pk][ck], FlatDict):
183  out[pk][ck] = self._values[pk][ck].as_dict()
184  else:
185  out[pk][ck] = self._values[pk][ck]
186  else:
187  out[key] = self._values[key]
188  return out
189 
190  def clear(self):
191  """Remove all items from the flat dictionary."""
192  self._values.clear()
193 
194  def copy(self):
195  """Return a shallow copy of the flat dictionary.
196 
197  :rtype: flatdict.FlatDict
198 
199  """
200  return self.__class__(self.as_dict(), delimiter=self._delimiter)
201 
202  def get(self, key, d=None):
203  """Return the value for key if key is in the flat dictionary, else
204  default. If default is not given, it defaults to ``None``, so that this
205  method never raises :exc:`KeyError`.
206 
207  :param mixed key: The key to get
208  :param mixed d: The default value
209  :rtype: mixed
210 
211  """
212  try:
213  return self.__getitem__(key)
214  except KeyError:
215  return d
216 
217  def items(self):
218  """Return a copy of the flat dictionary's list of ``(key, value)``
219  pairs.
220 
221  .. note:: CPython implementation detail: Keys and values are listed in
222  an arbitrary order which is non-random, varies across Python
223  implementations, and depends on the flat dictionary's history of
224  insertions and deletions.
225 
226  :rtype: list
227 
228  """
229  return [(k, self.__getitem__(k)) for k in self.keys()]
230 
231  def iteritems(self):
232  """Return an iterator over the flat dictionary's (key, value) pairs.
233  See the note for :meth:`flatdict.FlatDict.items`.
234 
235  Using ``iteritems()`` while adding or deleting entries in the flat
236  dictionary may raise :exc:`RuntimeError` or fail to iterate over all
237  entries.
238 
239  :rtype: Iterator
240  :raises: RuntimeError
241 
242  """
243  for item in self.items():
244  yield item
245 
246  def iterkeys(self):
247  """Iterate over the flat dictionary's keys. See the note for
248  :meth:`flatdict.FlatDict.items`.
249 
250  Using ``iterkeys()`` while adding or deleting entries in the flat
251  dictionary may raise :exc:`RuntimeError` or fail to iterate over all
252  entries.
253 
254  :rtype: Iterator
255  :raises: RuntimeError
256 
257  """
258  for key in self.keys():
259  yield key
260 
261  def itervalues(self):
262  """Return an iterator over the flat dictionary's values. See the note
263  :meth:`flatdict.FlatDict.items`.
264 
265  Using ``itervalues()`` while adding or deleting entries in the flat
266  dictionary may raise a :exc:`RuntimeError` or fail to iterate over all
267  entries.
268 
269  :rtype: Iterator
270  :raises: RuntimeError
271 
272  """
273  for value in self.values():
274  yield value
275 
276  def keys(self):
277  """Return a copy of the flat dictionary's list of keys.
278  See the note for :meth:`flatdict.FlatDict.items`.
279 
280  :rtype: list
281 
282  """
283  keys = []
284  for key, value in self._values.items():
285  if isinstance(value, (FlatDict, dict)):
286  nested = [self._delimiter.join([key, k]) for k in value.keys()]
287  keys += nested if nested else [key]
288  else:
289  keys.append(key)
290  return sorted(keys)
291 
292  def pop(self, key, default=NO_DEFAULT):
293  """If key is in the flat dictionary, remove it and return its value,
294  else return default. If default is not given and key is not in the
295  dictionary, :exc:`KeyError` is raised.
296 
297  :param mixed key: The key name
298  :param mixed default: The default value
299  :rtype: mixed
300 
301  """
302  if key not in self and default != NO_DEFAULT:
303  return default
304  value = self[key]
305  self.__delitem__(key)
306  return value
307 
308  def setdefault(self, key, default):
309  """If key is in the flat dictionary, return its value. If not,
310  insert key with a value of default and return default.
311  default defaults to ``None``.
312 
313  :param mixed key: The key name
314  :param mixed default: The default value
315  :rtype: mixed
316 
317  """
318  if key not in self or not self.__getitem__(key):
319  self.__setitem__(key, default)
320  return self.__getitem__(key)
321 
322  def set_delimiter(self, delimiter):
323  """Override the default or passed in delimiter with a new value. If
324  the requested delimiter already exists in a key, a :exc:`ValueError`
325  will be raised.
326 
327  :param str delimiter: The delimiter to use
328  :raises: ValueError
329 
330  """
331  for key in self.keys():
332  if delimiter in key:
333  raise ValueError('Key {!r} collides with delimiter {!r}', key,
334  delimiter)
335  self._delimiter = delimiter
336  for key in self._values.keys():
337  if isinstance(self._values[key], FlatDict):
338  self._values[key].set_delimiter(delimiter)
339 
340  def update(self, other=None, **kwargs):
341  """Update the flat dictionary with the key/value pairs from other,
342  overwriting existing keys.
343 
344  ``update()`` accepts either another flat dictionary object or an
345  iterable of key/value pairs (as tuples or other iterables of length
346  two). If keyword arguments are specified, the flat dictionary is then
347  updated with those key/value pairs: ``d.update(red=1, blue=2)``.
348 
349  :param iterable other: Iterable of key, value pairs
350  :rtype: None
351 
352  """
353  [self.__setitem__(k, v) for k, v in dict(other or kwargs).items()]
354 
355  def values(self):
356  """Return a copy of the flat dictionary's list of values. See the note
357  for :meth:`flatdict.FlatDict.items`.
358 
359  :rtype: list
360 
361  """
362  return [self.__getitem__(k) for k in self.keys()]
363 
364  def _has_delimiter(self, key):
365  """Checks to see if the key contains the delimiter.
366 
367  :rtype: bool
368 
369  """
370  return isinstance(key, basestring) and self._delimiter in key
371 
372 
374  """Like :class:`~flatdict.FlatDict` but also coerces lists and sets
375  to child-dict instances with the offset as the key. Alternative to
376  the implementation added in v1.2 of FlatDict.
377 
378  """
379  _COERCE = (list, tuple, set, dict, FlatDict)
380  _ARRAYS = (list, set, tuple)
381 
382  def __init__(self, value=None, delimiter=':'):
383  self.original_type = type(value)
384  if self.original_type in self._ARRAYS:
385  value = dict([(str(i), v) for i, v in enumerate(value)])
386  super(FlatterDict, self).__init__(value, delimiter)
387 
388  def __setitem__(self, key, value):
389  """Assign the value to the key, dynamically building nested
390  FlatDict items where appropriate.
391 
392  :param mixed key: The key for the item
393  :param mixed value: The value for the item
394  :raises: TypeError
395 
396  """
397  if isinstance(value, self._COERCE) and \
398  not isinstance(value, FlatterDict):
399  value = self.__class__(value, self._delimiter)
400  if self._has_delimiter(key):
401  pk, ck = key.split(self._delimiter, 1)
402  if pk not in self._values:
403  self._values[pk] = self.__class__({ck: value}, self._delimiter)
404  return
405  if getattr(self._values[pk],
406  'original_type', None) in self._ARRAYS:
407  try:
408  int(ck)
409  except ValueError:
410  raise TypeError(
411  'Assignment to invalid type for key {}{}{}'.format(
412  pk, self._delimiter, ck))
413  elif not isinstance(self._values[pk], FlatterDict):
414  raise TypeError(
415  'Assignment to invalid type for key {}'.format(pk))
416  self._values[pk][ck] = value
417  else:
418  self._values[key] = value
419 
420  def as_dict(self):
421  """Return the :class:`~flatdict.FlatterDict` as a nested
422  :class:`dict`.
423 
424  :rtype: dict
425 
426  """
427  out = dict({})
428  for key in self.keys():
429  if self._has_delimiter(key):
430  pk, ck = key.split(self._delimiter, 1)
431  if self._has_delimiter(ck):
432  ck = ck.split(self._delimiter, 1)[0]
433  if isinstance(self._values[pk], FlatterDict) and pk not in out:
434  out[pk] = dict()
435  if isinstance(self._values[pk][ck], FlatterDict):
436  if self._values[pk][ck].original_type == tuple:
437  out[pk][ck] = tuple(self._child_as_list(pk, ck))
438  elif self._values[pk][ck].original_type == list:
439  out[pk][ck] = self._child_as_list(pk, ck)
440  elif self._values[pk][ck].original_type == set:
441  out[pk][ck] = set(self._child_as_list(pk, ck))
442  elif self._values[pk][ck].original_type == dict:
443  out[pk][ck] = self._values[pk][ck].as_dict()
444  else:
445  out[pk][ck] = self._values[pk][ck]
446  else:
447  out[key] = self._values[key]
448  return out
449 
450  def _child_as_list(self, pk, ck):
451  """Returns a list of values from the child FlatterDict instance
452  with string based integer keys.
453 
454  :param str pk: The parent key
455  :param str ck: The child key
456  :rtype: list
457 
458  """
459  return [self._values[pk][ck][k]
460  for k in sorted(self._values[pk][ck].keys(),
461  key=lambda x: int(x))]
def __init__(self, value=None, delimiter=':')
Definition: flatdict.py:382
def update(self, other=None, kwargs)
Definition: flatdict.py:340
def __setitem__(self, key, value)
Definition: flatdict.py:388
def __init__(self, value=None, delimiter=':')
Definition: flatdict.py:27
def setdefault(self, key, default)
Definition: flatdict.py:308
def __contains__(self, key)
Definition: flatdict.py:33
def set_delimiter(self, delimiter)
Definition: flatdict.py:322
def __eq__(self, other)
Definition: flatdict.py:63
def get(self, key, d=None)
Definition: flatdict.py:202
def __getitem__(self, key)
Definition: flatdict.py:88
def __ne__(self, other)
Definition: flatdict.py:78
def pop(self, key, default=NO_DEFAULT)
Definition: flatdict.py:292
def __delitem__(self, key)
Definition: flatdict.py:45
def _has_delimiter(self, key)
Definition: flatdict.py:364
def __setitem__(self, key, value)
Definition: flatdict.py:136
def _child_as_list(self, pk, ck)
Definition: flatdict.py:450


rosbag_pandas
Author(s): Adam Taylor
autogenerated on Mon Feb 28 2022 23:30:55