5907b0dc7c45a5fa379d312e99e069b147137797
[sdc/sdc-distribution-client.git] /
1 # urllib3/_collections.py
2 # Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
3 #
4 # This module is part of urllib3 and is released under
5 # the MIT License: http://www.opensource.org/licenses/mit-license.php
6
7 from collections import MutableMapping
8 try:
9     from threading import RLock
10 except ImportError: # Platform-specific: No threads available
11     class RLock:
12         def __enter__(self):
13             pass
14
15         def __exit__(self, exc_type, exc_value, traceback):
16             pass
17
18
19 try: # Python 2.7+
20     from collections import OrderedDict
21 except ImportError:
22     from .packages.ordered_dict import OrderedDict
23
24
25 __all__ = ['RecentlyUsedContainer']
26
27
28 _Null = object()
29
30
31 class RecentlyUsedContainer(MutableMapping):
32     """
33     Provides a thread-safe dict-like container which maintains up to
34     ``maxsize`` keys while throwing away the least-recently-used keys beyond
35     ``maxsize``.
36
37     :param maxsize:
38         Maximum number of recent elements to retain.
39
40     :param dispose_func:
41         Every time an item is evicted from the container,
42         ``dispose_func(value)`` is called.  Callback which will get called
43     """
44
45     ContainerCls = OrderedDict
46
47     def __init__(self, maxsize=10, dispose_func=None):
48         self._maxsize = maxsize
49         self.dispose_func = dispose_func
50
51         self._container = self.ContainerCls()
52         self.lock = RLock()
53
54     def __getitem__(self, key):
55         # Re-insert the item, moving it to the end of the eviction line.
56         with self.lock:
57             item = self._container.pop(key)
58             self._container[key] = item
59             return item
60
61     def __setitem__(self, key, value):
62         evicted_value = _Null
63         with self.lock:
64             # Possibly evict the existing value of 'key'
65             evicted_value = self._container.get(key, _Null)
66             self._container[key] = value
67
68             # If we didn't evict an existing value, we might have to evict the
69             # least recently used item from the beginning of the container.
70             if len(self._container) > self._maxsize:
71                 _key, evicted_value = self._container.popitem(last=False)
72
73         if self.dispose_func and evicted_value is not _Null:
74             self.dispose_func(evicted_value)
75
76     def __delitem__(self, key):
77         with self.lock:
78             value = self._container.pop(key)
79
80         if self.dispose_func:
81             self.dispose_func(value)
82
83     def __len__(self):
84         with self.lock:
85             return len(self._container)
86
87     def __iter__(self):
88         raise NotImplementedError('Iteration over this class is unlikely to be threadsafe.')
89
90     def clear(self):
91         with self.lock:
92             # Copy pointers to all values, then wipe the mapping
93             # under Python 2, this copies the list of values twice :-|
94             values = list(self._container.values())
95             self._container.clear()
96
97         if self.dispose_func:
98             for value in values:
99                 self.dispose_func(value)
100
101     def keys(self):
102         with self.lock:
103             return self._container.keys()