2 # -*- coding: utf-8 -*-
4 from collections import MutableMapping
6 from thread import get_ident
9 from _thread import get_ident
11 from _dummy_thread import get_ident
14 def recursive_repr(fillvalue=u'...'):
15 u'Decorator to make a repr function return fillvalue for a recursive call'
17 def decorating_function(user_function):
21 key = id(self), get_ident()
22 if key in repr_running:
26 result = user_function(self)
28 repr_running.discard(key)
31 # Can't use functools.wraps() here because of bootstrap issues
32 wrapper.__module__ = getattr(user_function, u'__module__')
33 wrapper.__doc__ = getattr(user_function, u'__doc__')
34 wrapper.__name__ = getattr(user_function, u'__name__')
35 wrapper.__annotations__ = getattr(user_function, u'__annotations__', {})
38 return decorating_function
40 # from collections 3.2.1
41 class _ChainMap(MutableMapping):
42 u''' A ChainMap groups multiple dicts (or other mappings) together
43 to create a single, updateable view.
45 The underlying mappings are stored in a list. That list is public and can
46 accessed or updated using the *maps* attribute. There is no other state.
48 Lookups search the underlying mappings successively until a key is found.
49 In contrast, writes, updates, and deletions only operate on the first
54 def __init__(self, *maps):
55 u'''Initialize a ChainMap by setting *maps* to the given mappings.
56 If no mappings are provided, a single empty dictionary is used.
59 self.maps = list(maps) or [{}] # always at least one map
61 def __missing__(self, key):
64 def __getitem__(self, key):
65 for mapping in self.maps:
67 return mapping[key] # can't use 'key in mapping' with defaultdict
70 return self.__missing__(key) # support subclasses that define __missing__
72 def get(self, key, default=None):
73 return self[key] if key in self else default
76 return len(set().union(*self.maps)) # reuses stored hash values if possible
79 return iter(set().union(*self.maps))
81 def __contains__(self, key):
82 return any(key in m for m in self.maps)
86 return u'{0.__class__.__name__}({1})'.format(
87 self, u', '.join(map(repr, self.maps)))
90 def fromkeys(cls, iterable, *args):
91 u'Create a ChainMap with a single dict created from the iterable.'
92 return cls(dict.fromkeys(iterable, *args))
95 u'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]'
96 return self.__class__(self.maps[0].copy(), *self.maps[1:])
100 def new_child(self): # like Django's Context.push()
101 u'New ChainMap with a new dict followed by all previous maps.'
102 return self.__class__({}, *self.maps)
105 def parents(self): # like Django's Context.pop()
106 u'New ChainMap from maps[1:].'
107 return self.__class__(*self.maps[1:])
109 def __setitem__(self, key, value):
110 self.maps[0][key] = value
112 def __delitem__(self, key):
114 del self.maps[0][key]
116 raise KeyError(u'Key not found in the first mapping: {!r}'.format(key))
119 u'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.'
121 return self.maps[0].popitem()
123 raise KeyError(u'No keys found in the first mapping.')
125 def pop(self, key, *args):
126 u'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].'
128 return self.maps[0].pop(key, *args)
130 raise KeyError(u'Key not found in the first mapping: {!r}'.format(key))
133 u'Clear maps[0], leaving maps[1:] intact.'