multisplit
[iramuteq] / configparser_helpers.py
1 # -*- coding: utf-8 -*-
2 #modification pour python 3 : Laurent Mérat, 6x7 - mai 2020
3 #License: GNU/GPL
4
5 #------------------------------------
6 # import des modules python
7 #------------------------------------
8 from collections.abc import MutableMapping
9
10
11 try:
12     from _thread import get_ident
13 except ImportError:
14     try:
15         from _thread import get_ident
16     except ImportError:
17         from _dummy_thread import get_ident
18
19 # from reprlib 3.2.1
20
21 def recursive_repr(fillvalue='...'):
22     'Decorator to make a repr function return fillvalue for a recursive call'
23
24     def decorating_function(user_function):
25         repr_running = set()
26
27         def wrapper(self):
28             key = id(self), get_ident()
29             if key in repr_running:
30                 return fillvalue
31             repr_running.add(key)
32             try:
33                 result = user_function(self)
34             finally:
35                 repr_running.discard(key)
36             return result
37
38         # Can't use functools.wraps() here because of bootstrap issues
39         wrapper.__module__ = getattr(user_function, '__module__')
40         wrapper.__doc__ = getattr(user_function, '__doc__')
41         wrapper.__name__ = getattr(user_function, '__name__')
42         wrapper.__annotations__ = getattr(user_function, '__annotations__', {})
43         return wrapper
44
45     return decorating_function
46
47 # from collections 3.2.1
48
49 class _ChainMap(MutableMapping):
50     ''' A ChainMap groups multiple dicts (or other mappings) together
51     to create a single, updateable view.
52
53     The underlying mappings are stored in a list.  That list is public and can
54     accessed or updated using the *maps* attribute.  There is no other state.
55
56     Lookups search the underlying mappings successively until a key is found.
57     In contrast, writes, updates, and deletions only operate on the first
58     mapping.
59
60     '''
61
62     def __init__(self, *maps):
63         '''Initialize a ChainMap by setting *maps* to the given mappings.
64         If no mappings are provided, a single empty dictionary is used.
65
66         '''
67         self.maps = list(maps) or [{}]          # always at least one map
68
69     def __missing__(self, key):
70         raise KeyError(key)
71
72     def __getitem__(self, key):
73         for mapping in self.maps:
74             try:
75                 return mapping[key]             # can't use 'key in mapping' with defaultdict
76             except KeyError:
77                 pass
78         return self.__missing__(key)            # support subclasses that define __missing__
79
80     def get(self, key, default=None):
81         return self[key] if key in self else default
82
83     def __len__(self):
84         return len(set().union(*self.maps))     # reuses stored hash values if possible
85
86     def __iter__(self):
87         return iter(set().union(*self.maps))
88
89     def __contains__(self, key):
90         return any(key in m for m in self.maps)
91
92     @recursive_repr()
93     def __repr__(self):
94         return '{0.__class__.__name__}({1})'.format(
95             self, ', '.join(map(repr, self.maps)))
96
97     @classmethod
98     def fromkeys(cls, iterable, *args):
99         'Create a ChainMap with a single dict created from the iterable.'
100         return cls(dict.fromkeys(iterable, *args))
101
102     def copy(self):
103         'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]'
104         return self.__class__(self.maps[0].copy(), *self.maps[1:])
105
106     __copy__ = copy
107
108     def new_child(self):                        # like Django's Context.push()
109         'New ChainMap with a new dict followed by all previous maps.'
110         return self.__class__({}, *self.maps)
111
112     @property
113     def parents(self):                          # like Django's Context.pop()
114         'New ChainMap from maps[1:].'
115         return self.__class__(*self.maps[1:])
116
117     def __setitem__(self, key, value):
118         self.maps[0][key] = value
119
120     def __delitem__(self, key):
121         try:
122             del self.maps[0][key]
123         except KeyError:
124             raise KeyError('Key not found in the first mapping: {!r}'.format(key))
125
126     def popitem(self):
127         'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.'
128         try:
129             return self.maps[0].popitem()
130         except KeyError:
131             raise KeyError('No keys found in the first mapping.')
132
133     def pop(self, key, *args):
134         'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].'
135         try:
136             return self.maps[0].pop(key, *args)
137         except KeyError:
138             raise KeyError('Key not found in the first mapping: {!r}'.format(key))
139
140     def clear(self):
141         'Clear maps[0], leaving maps[1:] intact.'
142         self.maps[0].clear()