1515from collections import OrderedDict , Counter
1616from heapq import nsmallest
1717from operator import itemgetter
18+ try :
19+ from _thread import allocate_lock as Lock
20+ except :
21+ from _dummy_thread import allocate_lock as Lock
1822
1923# update_wrapper() and wraps() are tools to help write
2024# wrapper functions that can handle naive introspection
@@ -115,37 +119,42 @@ def lfu_cache(maxsize=100):
115119 http://en.wikipedia.org/wiki/Cache_algorithms#Least-Frequently_Used
116120
117121 """
118- def decorating_function (user_function ):
122+ def decorating_function (user_function , tuple = tuple , sorted = sorted , len = len ):
119123 cache = {} # mapping of args to results
120124 use_count = Counter () # times each key has been accessed
121125 kwd_mark = object () # separate positional and keyword args
126+ lock = Lock ()
122127
123128 @wraps (user_function )
124129 def wrapper (* args , ** kwds ):
125130 key = args
126131 if kwds :
127132 key += (kwd_mark ,) + tuple (sorted (kwds .items ()))
128- use_count [key ] += 1 # count a use of this key
129133 try :
130- result = cache [key ]
131- wrapper .hits += 1
134+ with lock :
135+ use_count [key ] += 1 # count a use of this key
136+ result = cache [key ]
137+ wrapper .hits += 1
132138 except KeyError :
133139 result = user_function (* args , ** kwds )
134- cache [key ] = result
135- wrapper .misses += 1
136- if len (cache ) > maxsize :
137- # purge the 10% least frequently used entries
138- for key , _ in nsmallest (maxsize // 10 ,
139- use_count .items (),
140- key = itemgetter (1 )):
141- del cache [key ], use_count [key ]
140+ with lock :
141+ use_count [key ] += 1 # count a use of this key
142+ cache [key ] = result
143+ wrapper .misses += 1
144+ if len (cache ) > maxsize :
145+ # purge the 10% least frequently used entries
146+ for key , _ in nsmallest (maxsize // 10 ,
147+ use_count .items (),
148+ key = itemgetter (1 )):
149+ del cache [key ], use_count [key ]
142150 return result
143151
144152 def clear ():
145153 """Clear the cache and cache statistics"""
146- cache .clear ()
147- use_count .clear ()
148- wrapper .hits = wrapper .misses = 0
154+ with lock :
155+ cache .clear ()
156+ use_count .clear ()
157+ wrapper .hits = wrapper .misses = 0
149158
150159 wrapper .hits = wrapper .misses = 0
151160 wrapper .clear = clear
@@ -161,30 +170,36 @@ def lru_cache(maxsize=100):
161170 http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
162171
163172 """
164- def decorating_function (user_function ):
173+ def decorating_function (user_function , tuple = tuple , sorted = sorted , len = len ):
165174 cache = OrderedDict () # ordered least recent to most recent
166175 kwd_mark = object () # separate positional and keyword args
176+ lock = Lock ()
167177
168178 @wraps (user_function )
169179 def wrapper (* args , ** kwds ):
170180 key = args
171181 if kwds :
172182 key += (kwd_mark ,) + tuple (sorted (kwds .items ()))
173183 try :
174- result = cache .pop (key )
175- wrapper .hits += 1
184+ with lock :
185+ result = cache [key ]
186+ del cache [key ]
187+ cache [key ] = result # record recent use of this key
188+ wrapper .hits += 1
176189 except KeyError :
177190 result = user_function (* args , ** kwds )
178- wrapper .misses += 1
179- if len (cache ) >= maxsize :
180- cache .popitem (0 ) # purge least recently used cache entry
181- cache [key ] = result # record recent use of this key
191+ with lock :
192+ cache [key ] = result # record recent use of this key
193+ wrapper .misses += 1
194+ if len (cache ) > maxsize :
195+ cache .popitem (0 ) # purge least recently used cache entry
182196 return result
183197
184198 def clear ():
185199 """Clear the cache and cache statistics"""
186- cache .clear ()
187- wrapper .hits = wrapper .misses = 0
200+ with lock :
201+ cache .clear ()
202+ wrapper .hits = wrapper .misses = 0
188203
189204 wrapper .hits = wrapper .misses = 0
190205 wrapper .clear = clear
0 commit comments