Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

#!/usr/bin/env python 

 

""" 

@file ion/util/cache.py 

@author Adam R. Smith 

@brief Simple caching utilities. 

""" 

 

from time import time 

 

class memoize(object): 

    """ 

    Memoize with timeout. 

    This has been optimized repeatedly to squeeze out extra performance. 

     

    http://code.activestate.com/recipes/325905/ (r5) 

    Modified by Adam R. Smith 

    """ 

 

    _caches = {} 

    _timeouts = {} 

 

    def __init__(self, timeout=0): 

        self.timeout = timeout 

 

    def collect(self): 

        """Clear cache of results which have timed out""" 

        for func in self._caches: 

            cache = {} 

            for key in self._caches[func]: 

                if (self._timeouts[func] <= 0) or ((time() - self._caches[func][key][1]) < self._timeouts[func]): 

                    cache[key] = self._caches[func][key] 

            self._caches[func] = cache 

 

    def __call__(self, f): 

        cache = self.cache = self._caches[f] = {} 

        timeout = self._timeouts[f] = self.timeout 

 

        def func(*args, **kwargs): 

            key = tuple(args) 

            if len(kwargs): 

                kw = kwargs.items() 

                kw.sort() 

                key += tuple(kw) 

 

            cached = (key in cache) 

            if cached: 

                v = cache[key] 

                if (timeout > 0) and ((time() - v[1]) > timeout): 

                    cached = False 

            if not cached: 

                ts = time() if timeout else 0 

                v = cache[key] = (f(*args, **kwargs), ts) 

            return v[0] 

        #func.func_name = f.func_name 

        func.__doc__ = f.__doc__ 

 

        return func 

 

 

class LRUDict(object): 

    """ 

    Amortized O(1) LRU cache with a dict-like interface. Based on http://code.activestate.com/recipes/252524/ (r3) 

    Copyright 2003 Josiah Carlson. 

    Modified by Adam R. Smith to support sizes and to be more dict-like. 

    Licensed under the PSF License: http://docs.python.org/license.html 

    """ 

 

    class Node(object): 

        __slots__ = ['prev', 'next', 'me', 'size'] 

        def __init__(self, prev, me, size=1): 

            self.prev = prev 

            self.me = me 

            self.next = None 

            self.size = size 

 

 

    def __init__(self, limit, pairs=None, use_size=False): 

        """ limit is either an integer item count or a size in bytes. """ 

 

        self.limit = max(limit, 1) 

        self.d = {} 

        self.first = None 

        self.last = None 

        self.use_size = use_size 

        self.total_size = 0 

 

        if pairs is None: pairs = [] 

        for key, value in pairs: 

            self[key] = value 

 

    def __len__(self): 

        return len(self.d) 

 

    def __contains__(self, key): 

        return key in self.d 

 

    def has_key(self, key): 

        return key in self.d 

 

    def __getitem__(self, key): 

        a = self.d[key].me 

        self[a[0]] = a[1] 

        return a[1] 

 

    def __setitem__(self, key, val): 

        if key in self.d: 

            del self[key] 

 

        size = 1 

        if self.use_size and hasattr(val, '__sizeof__'): 

            size = val.__sizeof__() 

        self.total_size += size 

 

        nobj = LRUDict.Node(self.last, (key, val), size) 

        if self.first is None: 

            self.first = nobj 

        if self.last: 

            self.last.next = nobj 

        self.last = nobj 

        self.d[key] = nobj 

 

        self.purge() 

 

    def purge(self): 

        while self.total_size > self.limit: 

            if self.first == self.last: 

                self.first = None 

                self.last = None 

                self.total_size = 0 

                return 

 

            a = self.first 

            self.total_size -= a.size 

            a.next.prev = None 

            self.first = a.next 

            a.next = None 

 

            nobj = self.d[a.me[0]] 

            obj = nobj.me[1] 

            if hasattr(obj, 'clear'): 

                obj.clear() 

 

            del self.d[a.me[0]] 

            del a 

 

    def __delitem__(self, key): 

        nobj = self.d[key] 

        self.total_size -= nobj.size 

 

        if nobj.prev: 

            nobj.prev.next = nobj.next 

        else: 

            self.first = nobj.next 

        if nobj.next: 

            nobj.next.prev = nobj.prev 

        else: 

            self.last = nobj.prev 

        del self.d[key] 

 

    def __iter__(self): 

        cur = self.first 

        while cur != None: 

            cur2 = cur.next 

            yield cur.me[1] 

            cur = cur2 

 

    def iteritems(self): 

        cur = self.first 

        while cur != None: 

            cur2 = cur.next 

            yield cur.me 

            cur = cur2 

 

    def iterkeys(self): 

        return iter(self.d) 

 

    def itervalues(self): 

        for i,j in self.iteritems(): 

            yield j 

 

    def keys(self): 

        return self.d.keys() 

 

    def pop(self, key): 

        obj = self.get(key) 

        del self[key] 

        return obj 

 

    def touch(self, key): 

        """ Recalculate the size of the object at the given key, and update its access time. """ 

        val = self[key] 

        if self.use_size and hasattr(val, '__sizeof__'): 

            old_size = val.size 

            val.size = val.__sizeof__() 

            self.total_size += val.size - old_size 

 

        self.purge() 

        return val 

 

    def get(self, key, default=None): 

        if key in self.d: 

            return self[key] 

        return default 

 

    def update(self, d): 

        for k,v in d.iteritems(): 

            self[k] = v 

 

    def clear(self): 

 

        for nobj in self.d.itervalues(): 

            obj = nobj.me[1] 

 

            if hasattr(obj, 'clear'): 

                obj.clear() 

 

        self.d.clear() 

        self.total_size = 0 

        self.first = None 

        self.last = None 

 

# Remove for code coverage 

''' 

if __name__ == '__main__': 

    def main(): 

        class ObjectWithSize(object): 

            def __init__(self, size): 

                self.size = size 

            def __sizeof__(self): 

                return self.size 

 

        lru = LRUDict(3) 

        lru['a'] = 1 

        lru['b'] = 2 

        lru['c'] = 3 

        lru['a'] = 1 

        lru['d'] = 4 

        print 'Should be: a, c, d', lru.keys() 

 

        lru = LRUDict(limit=100, use_size=True) 

        lru['a'] = ObjectWithSize(25) 

        lru['b'] = ObjectWithSize(50) 

        lru['c'] = ObjectWithSize(25) 

        lru['d'] = ObjectWithSize(1) 

        print 'Should be: c, b, d', lru.keys() 

 

        lru.touch('b') 

        lru.touch('c') 

        lru['a'] = ObjectWithSize(25) 

        print 'Should be: a, c, b', lru.keys() 

 

        lru.update({'e': ObjectWithSize(1), 'f': ObjectWithSize(2), 'g': ObjectWithSize(20)}) 

 

        for k,v in lru.iteritems(): 

            print '%s: %s' % (k, str(v)) 

 

        lru.clear() 

        print 'Should be empty: ', lru.keys() 

 

        print 'Should be false: ', lru.has_key('monkey') 

 

    main() 

'''