cc12703-diskcache 5.7.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1245 @@
1
+ """Persistent Data Types
2
+ """
3
+
4
+ import operator as op
5
+ from collections import OrderedDict
6
+ from collections.abc import (
7
+ ItemsView,
8
+ KeysView,
9
+ MutableMapping,
10
+ Sequence,
11
+ ValuesView,
12
+ )
13
+ from contextlib import contextmanager
14
+ from shutil import rmtree
15
+
16
+ from .core import ENOVAL, Cache
17
+
18
+
19
+ def _make_compare(seq_op, doc):
20
+ """Make compare method with Sequence semantics."""
21
+
22
+ def compare(self, that):
23
+ """Compare method for deque and sequence."""
24
+ if not isinstance(that, Sequence):
25
+ return NotImplemented
26
+
27
+ len_self = len(self)
28
+ len_that = len(that)
29
+
30
+ if len_self != len_that:
31
+ if seq_op is op.eq:
32
+ return False
33
+ if seq_op is op.ne:
34
+ return True
35
+
36
+ for alpha, beta in zip(self, that):
37
+ if alpha != beta:
38
+ return seq_op(alpha, beta)
39
+
40
+ return seq_op(len_self, len_that)
41
+
42
+ compare.__name__ = '__{0}__'.format(seq_op.__name__)
43
+ doc_str = 'Return True if and only if deque is {0} `that`.'
44
+ compare.__doc__ = doc_str.format(doc)
45
+
46
+ return compare
47
+
48
+
49
+ class Deque(Sequence):
50
+ """Persistent sequence with double-ended queue semantics.
51
+
52
+ Double-ended queue is an ordered collection with optimized access at its
53
+ endpoints.
54
+
55
+ Items are serialized to disk. Deque may be initialized from directory path
56
+ where items are stored.
57
+
58
+ >>> deque = Deque()
59
+ >>> deque += range(5)
60
+ >>> list(deque)
61
+ [0, 1, 2, 3, 4]
62
+ >>> for value in range(5):
63
+ ... deque.appendleft(-value)
64
+ >>> len(deque)
65
+ 10
66
+ >>> list(deque)
67
+ [-4, -3, -2, -1, 0, 0, 1, 2, 3, 4]
68
+ >>> deque.pop()
69
+ 4
70
+ >>> deque.popleft()
71
+ -4
72
+ >>> deque.reverse()
73
+ >>> list(deque)
74
+ [3, 2, 1, 0, 0, -1, -2, -3]
75
+
76
+ """
77
+
78
+ def __init__(self, iterable=(), directory=None, maxlen=None):
79
+ """Initialize deque instance.
80
+
81
+ If directory is None then temporary directory created. The directory
82
+ will *not* be automatically removed.
83
+
84
+ :param iterable: iterable of items to append to deque
85
+ :param directory: deque directory (default None)
86
+
87
+ """
88
+ self._cache = Cache(directory, eviction_policy='none')
89
+ self._maxlen = float('inf') if maxlen is None else maxlen
90
+ self._extend(iterable)
91
+
92
+ @classmethod
93
+ def fromcache(cls, cache, iterable=(), maxlen=None):
94
+ """Initialize deque using `cache`.
95
+
96
+ >>> cache = Cache()
97
+ >>> deque = Deque.fromcache(cache, [5, 6, 7, 8])
98
+ >>> deque.cache is cache
99
+ True
100
+ >>> len(deque)
101
+ 4
102
+ >>> 7 in deque
103
+ True
104
+ >>> deque.popleft()
105
+ 5
106
+
107
+ :param Cache cache: cache to use
108
+ :param iterable: iterable of items
109
+ :return: initialized Deque
110
+
111
+ """
112
+ # pylint: disable=no-member,protected-access
113
+ self = cls.__new__(cls)
114
+ self._cache = cache
115
+ self._maxlen = float('inf') if maxlen is None else maxlen
116
+ self._extend(iterable)
117
+ return self
118
+
119
+ @property
120
+ def cache(self):
121
+ """Cache used by deque."""
122
+ return self._cache
123
+
124
+ @property
125
+ def directory(self):
126
+ """Directory path where deque is stored."""
127
+ return self._cache.directory
128
+
129
+ @property
130
+ def maxlen(self):
131
+ """Max length of the deque."""
132
+ return self._maxlen
133
+
134
+ @maxlen.setter
135
+ def maxlen(self, value):
136
+ """Set max length of the deque.
137
+
138
+ Pops items from left while length greater than max.
139
+
140
+ >>> deque = Deque()
141
+ >>> deque.extendleft('abcde')
142
+ >>> deque.maxlen = 3
143
+ >>> list(deque)
144
+ ['c', 'd', 'e']
145
+
146
+ :param value: max length
147
+
148
+ """
149
+ self._maxlen = value
150
+ with self._cache.transact(retry=True):
151
+ while len(self._cache) > self._maxlen:
152
+ self._popleft()
153
+
154
+ def _index(self, index, func):
155
+ len_self = len(self)
156
+
157
+ if index >= 0:
158
+ if index >= len_self:
159
+ raise IndexError('deque index out of range')
160
+
161
+ for key in self._cache.iterkeys():
162
+ if index == 0:
163
+ try:
164
+ return func(key)
165
+ except KeyError:
166
+ continue
167
+ index -= 1
168
+ else:
169
+ if index < -len_self:
170
+ raise IndexError('deque index out of range')
171
+
172
+ index += 1
173
+
174
+ for key in self._cache.iterkeys(reverse=True):
175
+ if index == 0:
176
+ try:
177
+ return func(key)
178
+ except KeyError:
179
+ continue
180
+ index += 1
181
+
182
+ raise IndexError('deque index out of range')
183
+
184
+ def __getitem__(self, index):
185
+ """deque.__getitem__(index) <==> deque[index]
186
+
187
+ Return corresponding item for `index` in deque.
188
+
189
+ See also `Deque.peekleft` and `Deque.peek` for indexing deque at index
190
+ ``0`` or ``-1``.
191
+
192
+ >>> deque = Deque()
193
+ >>> deque.extend('abcde')
194
+ >>> deque[1]
195
+ 'b'
196
+ >>> deque[-2]
197
+ 'd'
198
+
199
+ :param int index: index of item
200
+ :return: corresponding item
201
+ :raises IndexError: if index out of range
202
+
203
+ """
204
+ return self._index(index, self._cache.__getitem__)
205
+
206
+ def __setitem__(self, index, value):
207
+ """deque.__setitem__(index, value) <==> deque[index] = value
208
+
209
+ Store `value` in deque at `index`.
210
+
211
+ >>> deque = Deque()
212
+ >>> deque.extend([None] * 3)
213
+ >>> deque[0] = 'a'
214
+ >>> deque[1] = 'b'
215
+ >>> deque[-1] = 'c'
216
+ >>> ''.join(deque)
217
+ 'abc'
218
+
219
+ :param int index: index of value
220
+ :param value: value to store
221
+ :raises IndexError: if index out of range
222
+
223
+ """
224
+
225
+ def _set_value(key):
226
+ return self._cache.__setitem__(key, value)
227
+
228
+ self._index(index, _set_value)
229
+
230
+ def __delitem__(self, index):
231
+ """deque.__delitem__(index) <==> del deque[index]
232
+
233
+ Delete item in deque at `index`.
234
+
235
+ >>> deque = Deque()
236
+ >>> deque.extend([None] * 3)
237
+ >>> del deque[0]
238
+ >>> del deque[1]
239
+ >>> del deque[-1]
240
+ >>> len(deque)
241
+ 0
242
+
243
+ :param int index: index of item
244
+ :raises IndexError: if index out of range
245
+
246
+ """
247
+ self._index(index, self._cache.__delitem__)
248
+
249
+ def __repr__(self):
250
+ """deque.__repr__() <==> repr(deque)
251
+
252
+ Return string with printable representation of deque.
253
+
254
+ """
255
+ name = type(self).__name__
256
+ return '{0}(directory={1!r})'.format(name, self.directory)
257
+
258
+ __eq__ = _make_compare(op.eq, 'equal to')
259
+ __ne__ = _make_compare(op.ne, 'not equal to')
260
+ __lt__ = _make_compare(op.lt, 'less than')
261
+ __gt__ = _make_compare(op.gt, 'greater than')
262
+ __le__ = _make_compare(op.le, 'less than or equal to')
263
+ __ge__ = _make_compare(op.ge, 'greater than or equal to')
264
+
265
+ def __iadd__(self, iterable):
266
+ """deque.__iadd__(iterable) <==> deque += iterable
267
+
268
+ Extend back side of deque with items from iterable.
269
+
270
+ :param iterable: iterable of items to append to deque
271
+ :return: deque with added items
272
+
273
+ """
274
+ self._extend(iterable)
275
+ return self
276
+
277
+ def __iter__(self):
278
+ """deque.__iter__() <==> iter(deque)
279
+
280
+ Return iterator of deque from front to back.
281
+
282
+ """
283
+ _cache = self._cache
284
+
285
+ for key in _cache.iterkeys():
286
+ try:
287
+ yield _cache[key]
288
+ except KeyError:
289
+ pass
290
+
291
+ def __len__(self):
292
+ """deque.__len__() <==> len(deque)
293
+
294
+ Return length of deque.
295
+
296
+ """
297
+ return len(self._cache)
298
+
299
+ def __reversed__(self):
300
+ """deque.__reversed__() <==> reversed(deque)
301
+
302
+ Return iterator of deque from back to front.
303
+
304
+ >>> deque = Deque()
305
+ >>> deque.extend('abcd')
306
+ >>> iterator = reversed(deque)
307
+ >>> next(iterator)
308
+ 'd'
309
+ >>> list(iterator)
310
+ ['c', 'b', 'a']
311
+
312
+ """
313
+ _cache = self._cache
314
+
315
+ for key in _cache.iterkeys(reverse=True):
316
+ try:
317
+ yield _cache[key]
318
+ except KeyError:
319
+ pass
320
+
321
+ def __getstate__(self):
322
+ return self.directory, self.maxlen
323
+
324
+ def __setstate__(self, state):
325
+ directory, maxlen = state
326
+ self.__init__(directory=directory, maxlen=maxlen)
327
+
328
+ def append(self, value):
329
+ """Add `value` to back of deque.
330
+
331
+ >>> deque = Deque()
332
+ >>> deque.append('a')
333
+ >>> deque.append('b')
334
+ >>> deque.append('c')
335
+ >>> list(deque)
336
+ ['a', 'b', 'c']
337
+
338
+ :param value: value to add to back of deque
339
+
340
+ """
341
+ with self._cache.transact(retry=True):
342
+ self._cache.push(value, retry=True)
343
+ if len(self._cache) > self._maxlen:
344
+ self._popleft()
345
+
346
+ _append = append
347
+
348
+ def appendleft(self, value):
349
+ """Add `value` to front of deque.
350
+
351
+ >>> deque = Deque()
352
+ >>> deque.appendleft('a')
353
+ >>> deque.appendleft('b')
354
+ >>> deque.appendleft('c')
355
+ >>> list(deque)
356
+ ['c', 'b', 'a']
357
+
358
+ :param value: value to add to front of deque
359
+
360
+ """
361
+ with self._cache.transact(retry=True):
362
+ self._cache.push(value, side='front', retry=True)
363
+ if len(self._cache) > self._maxlen:
364
+ self._pop()
365
+
366
+ _appendleft = appendleft
367
+
368
+ def clear(self):
369
+ """Remove all elements from deque.
370
+
371
+ >>> deque = Deque('abc')
372
+ >>> len(deque)
373
+ 3
374
+ >>> deque.clear()
375
+ >>> list(deque)
376
+ []
377
+
378
+ """
379
+ self._cache.clear(retry=True)
380
+
381
+ _clear = clear
382
+
383
+ def copy(self):
384
+ """Copy deque with same directory and max length."""
385
+ TypeSelf = type(self)
386
+ return TypeSelf(directory=self.directory, maxlen=self.maxlen)
387
+
388
+ def count(self, value):
389
+ """Return number of occurrences of `value` in deque.
390
+
391
+ >>> deque = Deque()
392
+ >>> deque += [num for num in range(1, 5) for _ in range(num)]
393
+ >>> deque.count(0)
394
+ 0
395
+ >>> deque.count(1)
396
+ 1
397
+ >>> deque.count(4)
398
+ 4
399
+
400
+ :param value: value to count in deque
401
+ :return: count of items equal to value in deque
402
+
403
+ """
404
+ return sum(1 for item in self if value == item)
405
+
406
+ def extend(self, iterable):
407
+ """Extend back side of deque with values from `iterable`.
408
+
409
+ :param iterable: iterable of values
410
+
411
+ """
412
+ for value in iterable:
413
+ self._append(value)
414
+
415
+ _extend = extend
416
+
417
+ def extendleft(self, iterable):
418
+ """Extend front side of deque with value from `iterable`.
419
+
420
+ >>> deque = Deque()
421
+ >>> deque.extendleft('abc')
422
+ >>> list(deque)
423
+ ['c', 'b', 'a']
424
+
425
+ :param iterable: iterable of values
426
+
427
+ """
428
+ for value in iterable:
429
+ self._appendleft(value)
430
+
431
+ def peek(self):
432
+ """Peek at value at back of deque.
433
+
434
+ Faster than indexing deque at -1.
435
+
436
+ If deque is empty then raise IndexError.
437
+
438
+ >>> deque = Deque()
439
+ >>> deque.peek()
440
+ Traceback (most recent call last):
441
+ ...
442
+ IndexError: peek from an empty deque
443
+ >>> deque += 'abc'
444
+ >>> deque.peek()
445
+ 'c'
446
+
447
+ :return: value at back of deque
448
+ :raises IndexError: if deque is empty
449
+
450
+ """
451
+ default = None, ENOVAL
452
+ _, value = self._cache.peek(default=default, side='back', retry=True)
453
+ if value is ENOVAL:
454
+ raise IndexError('peek from an empty deque')
455
+ return value
456
+
457
+ def peekleft(self):
458
+ """Peek at value at front of deque.
459
+
460
+ Faster than indexing deque at 0.
461
+
462
+ If deque is empty then raise IndexError.
463
+
464
+ >>> deque = Deque()
465
+ >>> deque.peekleft()
466
+ Traceback (most recent call last):
467
+ ...
468
+ IndexError: peek from an empty deque
469
+ >>> deque += 'abc'
470
+ >>> deque.peekleft()
471
+ 'a'
472
+
473
+ :return: value at front of deque
474
+ :raises IndexError: if deque is empty
475
+
476
+ """
477
+ default = None, ENOVAL
478
+ _, value = self._cache.peek(default=default, side='front', retry=True)
479
+ if value is ENOVAL:
480
+ raise IndexError('peek from an empty deque')
481
+ return value
482
+
483
+ def pop(self):
484
+ """Remove and return value at back of deque.
485
+
486
+ If deque is empty then raise IndexError.
487
+
488
+ >>> deque = Deque()
489
+ >>> deque += 'ab'
490
+ >>> deque.pop()
491
+ 'b'
492
+ >>> deque.pop()
493
+ 'a'
494
+ >>> deque.pop()
495
+ Traceback (most recent call last):
496
+ ...
497
+ IndexError: pop from an empty deque
498
+
499
+ :return: value at back of deque
500
+ :raises IndexError: if deque is empty
501
+
502
+ """
503
+ default = None, ENOVAL
504
+ _, value = self._cache.pull(default=default, side='back', retry=True)
505
+ if value is ENOVAL:
506
+ raise IndexError('pop from an empty deque')
507
+ return value
508
+
509
+ _pop = pop
510
+
511
+ def popleft(self):
512
+ """Remove and return value at front of deque.
513
+
514
+ >>> deque = Deque()
515
+ >>> deque += 'ab'
516
+ >>> deque.popleft()
517
+ 'a'
518
+ >>> deque.popleft()
519
+ 'b'
520
+ >>> deque.popleft()
521
+ Traceback (most recent call last):
522
+ ...
523
+ IndexError: pop from an empty deque
524
+
525
+ :return: value at front of deque
526
+ :raises IndexError: if deque is empty
527
+
528
+ """
529
+ default = None, ENOVAL
530
+ _, value = self._cache.pull(default=default, retry=True)
531
+ if value is ENOVAL:
532
+ raise IndexError('pop from an empty deque')
533
+ return value
534
+
535
+ _popleft = popleft
536
+
537
+ def remove(self, value):
538
+ """Remove first occurrence of `value` in deque.
539
+
540
+ >>> deque = Deque()
541
+ >>> deque += 'aab'
542
+ >>> deque.remove('a')
543
+ >>> list(deque)
544
+ ['a', 'b']
545
+ >>> deque.remove('b')
546
+ >>> list(deque)
547
+ ['a']
548
+ >>> deque.remove('c')
549
+ Traceback (most recent call last):
550
+ ...
551
+ ValueError: deque.remove(value): value not in deque
552
+
553
+ :param value: value to remove
554
+ :raises ValueError: if value not in deque
555
+
556
+ """
557
+ _cache = self._cache
558
+
559
+ for key in _cache.iterkeys():
560
+ try:
561
+ item = _cache[key]
562
+ except KeyError:
563
+ continue
564
+ else:
565
+ if value == item:
566
+ try:
567
+ del _cache[key]
568
+ except KeyError:
569
+ continue
570
+ return
571
+
572
+ raise ValueError('deque.remove(value): value not in deque')
573
+
574
+ def reverse(self):
575
+ """Reverse deque in place.
576
+
577
+ >>> deque = Deque()
578
+ >>> deque += 'abc'
579
+ >>> deque.reverse()
580
+ >>> list(deque)
581
+ ['c', 'b', 'a']
582
+
583
+ """
584
+ # pylint: disable=protected-access
585
+ # GrantJ 2019-03-22 Consider using an algorithm that swaps the values
586
+ # at two keys. Like self._cache.swap(key1, key2, retry=True) The swap
587
+ # method would exchange the values at two given keys. Then, using a
588
+ # forward iterator and a reverse iterator, the reverse method could
589
+ # avoid making copies of the values.
590
+ temp = Deque(iterable=reversed(self))
591
+ self._clear()
592
+ self._extend(temp)
593
+ directory = temp.directory
594
+ temp._cache.close()
595
+ del temp
596
+ rmtree(directory)
597
+
598
+ def rotate(self, steps=1):
599
+ """Rotate deque right by `steps`.
600
+
601
+ If steps is negative then rotate left.
602
+
603
+ >>> deque = Deque()
604
+ >>> deque += range(5)
605
+ >>> deque.rotate(2)
606
+ >>> list(deque)
607
+ [3, 4, 0, 1, 2]
608
+ >>> deque.rotate(-1)
609
+ >>> list(deque)
610
+ [4, 0, 1, 2, 3]
611
+
612
+ :param int steps: number of steps to rotate (default 1)
613
+
614
+ """
615
+ if not isinstance(steps, int):
616
+ type_name = type(steps).__name__
617
+ raise TypeError('integer argument expected, got %s' % type_name)
618
+
619
+ len_self = len(self)
620
+
621
+ if not len_self:
622
+ return
623
+
624
+ if steps >= 0:
625
+ steps %= len_self
626
+
627
+ for _ in range(steps):
628
+ try:
629
+ value = self._pop()
630
+ except IndexError:
631
+ return
632
+ else:
633
+ self._appendleft(value)
634
+ else:
635
+ steps *= -1
636
+ steps %= len_self
637
+
638
+ for _ in range(steps):
639
+ try:
640
+ value = self._popleft()
641
+ except IndexError:
642
+ return
643
+ else:
644
+ self._append(value)
645
+
646
+ __hash__ = None # type: ignore
647
+
648
+ @contextmanager
649
+ def transact(self):
650
+ """Context manager to perform a transaction by locking the deque.
651
+
652
+ While the deque is locked, no other write operation is permitted.
653
+ Transactions should therefore be as short as possible. Read and write
654
+ operations performed in a transaction are atomic. Read operations may
655
+ occur concurrent to a transaction.
656
+
657
+ Transactions may be nested and may not be shared between threads.
658
+
659
+ >>> from diskcache import Deque
660
+ >>> deque = Deque()
661
+ >>> deque += range(5)
662
+ >>> with deque.transact(): # Atomically rotate elements.
663
+ ... value = deque.pop()
664
+ ... deque.appendleft(value)
665
+ >>> list(deque)
666
+ [4, 0, 1, 2, 3]
667
+
668
+ :return: context manager for use in `with` statement
669
+
670
+ """
671
+ with self._cache.transact(retry=True):
672
+ yield
673
+
674
+
675
+ class Index(MutableMapping):
676
+ """Persistent mutable mapping with insertion order iteration.
677
+
678
+ Items are serialized to disk. Index may be initialized from directory path
679
+ where items are stored.
680
+
681
+ Hashing protocol is not used. Keys are looked up by their serialized
682
+ format. See ``diskcache.Disk`` for details.
683
+
684
+ >>> index = Index()
685
+ >>> index.update([('a', 1), ('b', 2), ('c', 3)])
686
+ >>> index['a']
687
+ 1
688
+ >>> list(index)
689
+ ['a', 'b', 'c']
690
+ >>> len(index)
691
+ 3
692
+ >>> del index['b']
693
+ >>> index.popitem()
694
+ ('c', 3)
695
+
696
+ """
697
+
698
+ def __init__(self, *args, **kwargs):
699
+ """Initialize index in directory and update items.
700
+
701
+ Optional first argument may be string specifying directory where items
702
+ are stored. When None or not given, temporary directory is created.
703
+
704
+ >>> index = Index({'a': 1, 'b': 2, 'c': 3})
705
+ >>> len(index)
706
+ 3
707
+ >>> directory = index.directory
708
+ >>> inventory = Index(directory, d=4)
709
+ >>> inventory['b']
710
+ 2
711
+ >>> len(inventory)
712
+ 4
713
+
714
+ """
715
+ if args and isinstance(args[0], (bytes, str)):
716
+ directory = args[0]
717
+ args = args[1:]
718
+ else:
719
+ if args and args[0] is None:
720
+ args = args[1:]
721
+ directory = None
722
+ self._cache = Cache(directory, eviction_policy='none')
723
+ self._update(*args, **kwargs)
724
+
725
+ _update = MutableMapping.update
726
+
727
+ @classmethod
728
+ def fromcache(cls, cache, *args, **kwargs):
729
+ """Initialize index using `cache` and update items.
730
+
731
+ >>> cache = Cache()
732
+ >>> index = Index.fromcache(cache, {'a': 1, 'b': 2, 'c': 3})
733
+ >>> index.cache is cache
734
+ True
735
+ >>> len(index)
736
+ 3
737
+ >>> 'b' in index
738
+ True
739
+ >>> index['c']
740
+ 3
741
+
742
+ :param Cache cache: cache to use
743
+ :param args: mapping or sequence of items
744
+ :param kwargs: mapping of items
745
+ :return: initialized Index
746
+
747
+ """
748
+ # pylint: disable=no-member,protected-access
749
+ self = cls.__new__(cls)
750
+ self._cache = cache
751
+ self._update(*args, **kwargs)
752
+ return self
753
+
754
+ @property
755
+ def cache(self):
756
+ """Cache used by index."""
757
+ return self._cache
758
+
759
+ @property
760
+ def directory(self):
761
+ """Directory path where items are stored."""
762
+ return self._cache.directory
763
+
764
+ def __getitem__(self, key):
765
+ """index.__getitem__(key) <==> index[key]
766
+
767
+ Return corresponding value for `key` in index.
768
+
769
+ >>> index = Index()
770
+ >>> index.update({'a': 1, 'b': 2})
771
+ >>> index['a']
772
+ 1
773
+ >>> index['b']
774
+ 2
775
+ >>> index['c']
776
+ Traceback (most recent call last):
777
+ ...
778
+ KeyError: 'c'
779
+
780
+ :param key: key for item
781
+ :return: value for item in index with given key
782
+ :raises KeyError: if key is not found
783
+
784
+ """
785
+ return self._cache[key]
786
+
787
+ def __setitem__(self, key, value):
788
+ """index.__setitem__(key, value) <==> index[key] = value
789
+
790
+ Set `key` and `value` item in index.
791
+
792
+ >>> index = Index()
793
+ >>> index['a'] = 1
794
+ >>> index[0] = None
795
+ >>> len(index)
796
+ 2
797
+
798
+ :param key: key for item
799
+ :param value: value for item
800
+
801
+ """
802
+ self._cache[key] = value
803
+
804
+ def __delitem__(self, key):
805
+ """index.__delitem__(key) <==> del index[key]
806
+
807
+ Delete corresponding item for `key` from index.
808
+
809
+ >>> index = Index()
810
+ >>> index.update({'a': 1, 'b': 2})
811
+ >>> del index['a']
812
+ >>> del index['b']
813
+ >>> len(index)
814
+ 0
815
+ >>> del index['c']
816
+ Traceback (most recent call last):
817
+ ...
818
+ KeyError: 'c'
819
+
820
+ :param key: key for item
821
+ :raises KeyError: if key is not found
822
+
823
+ """
824
+ del self._cache[key]
825
+
826
+ def setdefault(self, key, default=None):
827
+ """Set and get value for `key` in index using `default`.
828
+
829
+ If `key` is not in index then set corresponding value to `default`. If
830
+ `key` is in index then ignore `default` and return existing value.
831
+
832
+ >>> index = Index()
833
+ >>> index.setdefault('a', 0)
834
+ 0
835
+ >>> index.setdefault('a', 1)
836
+ 0
837
+
838
+ :param key: key for item
839
+ :param default: value if key is missing (default None)
840
+ :return: value for item in index with given key
841
+
842
+ """
843
+ _cache = self._cache
844
+ while True:
845
+ try:
846
+ return _cache[key]
847
+ except KeyError:
848
+ _cache.add(key, default, retry=True)
849
+
850
+ def peekitem(self, last=True):
851
+ """Peek at key and value item pair in index based on iteration order.
852
+
853
+ >>> index = Index()
854
+ >>> for num, letter in enumerate('xyz'):
855
+ ... index[letter] = num
856
+ >>> index.peekitem()
857
+ ('z', 2)
858
+ >>> index.peekitem(last=False)
859
+ ('x', 0)
860
+
861
+ :param bool last: last item in iteration order (default True)
862
+ :return: key and value item pair
863
+ :raises KeyError: if cache is empty
864
+
865
+ """
866
+ return self._cache.peekitem(last, retry=True)
867
+
868
+ def pop(self, key, default=ENOVAL):
869
+ """Remove corresponding item for `key` from index and return value.
870
+
871
+ If `key` is missing then return `default`. If `default` is `ENOVAL`
872
+ then raise KeyError.
873
+
874
+ >>> index = Index({'a': 1, 'b': 2})
875
+ >>> index.pop('a')
876
+ 1
877
+ >>> index.pop('b')
878
+ 2
879
+ >>> index.pop('c', default=3)
880
+ 3
881
+ >>> index.pop('d')
882
+ Traceback (most recent call last):
883
+ ...
884
+ KeyError: 'd'
885
+
886
+ :param key: key for item
887
+ :param default: return value if key is missing (default ENOVAL)
888
+ :return: value for item if key is found else default
889
+ :raises KeyError: if key is not found and default is ENOVAL
890
+
891
+ """
892
+ _cache = self._cache
893
+ value = _cache.pop(key, default=default, retry=True)
894
+ if value is ENOVAL:
895
+ raise KeyError(key)
896
+ return value
897
+
898
+ def popitem(self, last=True):
899
+ """Remove and return item pair.
900
+
901
+ Item pairs are returned in last-in-first-out (LIFO) order if last is
902
+ True else first-in-first-out (FIFO) order. LIFO order imitates a stack
903
+ and FIFO order imitates a queue.
904
+
905
+ >>> index = Index()
906
+ >>> index.update([('a', 1), ('b', 2), ('c', 3)])
907
+ >>> index.popitem()
908
+ ('c', 3)
909
+ >>> index.popitem(last=False)
910
+ ('a', 1)
911
+ >>> index.popitem()
912
+ ('b', 2)
913
+ >>> index.popitem()
914
+ Traceback (most recent call last):
915
+ ...
916
+ KeyError: 'dictionary is empty'
917
+
918
+ :param bool last: pop last item pair (default True)
919
+ :return: key and value item pair
920
+ :raises KeyError: if index is empty
921
+
922
+ """
923
+ # pylint: disable=arguments-differ,unbalanced-tuple-unpacking
924
+ _cache = self._cache
925
+
926
+ with _cache.transact(retry=True):
927
+ key, value = _cache.peekitem(last=last)
928
+ del _cache[key]
929
+
930
+ return key, value
931
+
932
+ def push(self, value, prefix=None, side='back'):
933
+ """Push `value` onto `side` of queue in index identified by `prefix`.
934
+
935
+ When prefix is None, integer keys are used. Otherwise, string keys are
936
+ used in the format "prefix-integer". Integer starts at 500 trillion.
937
+
938
+ Defaults to pushing value on back of queue. Set side to 'front' to push
939
+ value on front of queue. Side must be one of 'back' or 'front'.
940
+
941
+ See also `Index.pull`.
942
+
943
+ >>> index = Index()
944
+ >>> print(index.push('apples'))
945
+ 500000000000000
946
+ >>> print(index.push('beans'))
947
+ 500000000000001
948
+ >>> print(index.push('cherries', side='front'))
949
+ 499999999999999
950
+ >>> index[500000000000001]
951
+ 'beans'
952
+ >>> index.push('dates', prefix='fruit')
953
+ 'fruit-500000000000000'
954
+
955
+ :param value: value for item
956
+ :param str prefix: key prefix (default None, key is integer)
957
+ :param str side: either 'back' or 'front' (default 'back')
958
+ :return: key for item in cache
959
+
960
+ """
961
+ return self._cache.push(value, prefix, side, retry=True)
962
+
963
+ def pull(self, prefix=None, default=(None, None), side='front'):
964
+ """Pull key and value item pair from `side` of queue in index.
965
+
966
+ When prefix is None, integer keys are used. Otherwise, string keys are
967
+ used in the format "prefix-integer". Integer starts at 500 trillion.
968
+
969
+ If queue is empty, return default.
970
+
971
+ Defaults to pulling key and value item pairs from front of queue. Set
972
+ side to 'back' to pull from back of queue. Side must be one of 'front'
973
+ or 'back'.
974
+
975
+ See also `Index.push`.
976
+
977
+ >>> index = Index()
978
+ >>> for letter in 'abc':
979
+ ... print(index.push(letter))
980
+ 500000000000000
981
+ 500000000000001
982
+ 500000000000002
983
+ >>> key, value = index.pull()
984
+ >>> print(key)
985
+ 500000000000000
986
+ >>> value
987
+ 'a'
988
+ >>> _, value = index.pull(side='back')
989
+ >>> value
990
+ 'c'
991
+ >>> index.pull(prefix='fruit')
992
+ (None, None)
993
+
994
+ :param str prefix: key prefix (default None, key is integer)
995
+ :param default: value to return if key is missing
996
+ (default (None, None))
997
+ :param str side: either 'front' or 'back' (default 'front')
998
+ :return: key and value item pair or default if queue is empty
999
+
1000
+ """
1001
+ return self._cache.pull(prefix, default, side, retry=True)
1002
+
1003
+ def clear(self):
1004
+ """Remove all items from index.
1005
+
1006
+ >>> index = Index({'a': 0, 'b': 1, 'c': 2})
1007
+ >>> len(index)
1008
+ 3
1009
+ >>> index.clear()
1010
+ >>> dict(index)
1011
+ {}
1012
+
1013
+ """
1014
+ self._cache.clear(retry=True)
1015
+
1016
+ def __iter__(self):
1017
+ """index.__iter__() <==> iter(index)
1018
+
1019
+ Return iterator of index keys in insertion order.
1020
+
1021
+ """
1022
+ return iter(self._cache)
1023
+
1024
+ def __reversed__(self):
1025
+ """index.__reversed__() <==> reversed(index)
1026
+
1027
+ Return iterator of index keys in reversed insertion order.
1028
+
1029
+ >>> index = Index()
1030
+ >>> index.update([('a', 1), ('b', 2), ('c', 3)])
1031
+ >>> iterator = reversed(index)
1032
+ >>> next(iterator)
1033
+ 'c'
1034
+ >>> list(iterator)
1035
+ ['b', 'a']
1036
+
1037
+ """
1038
+ return reversed(self._cache)
1039
+
1040
+ def __len__(self):
1041
+ """index.__len__() <==> len(index)
1042
+
1043
+ Return length of index.
1044
+
1045
+ """
1046
+ return len(self._cache)
1047
+
1048
+ def keys(self):
1049
+ """Set-like object providing a view of index keys.
1050
+
1051
+ >>> index = Index()
1052
+ >>> index.update({'a': 1, 'b': 2, 'c': 3})
1053
+ >>> keys_view = index.keys()
1054
+ >>> 'b' in keys_view
1055
+ True
1056
+
1057
+ :return: keys view
1058
+
1059
+ """
1060
+ return KeysView(self)
1061
+
1062
+ def values(self):
1063
+ """Set-like object providing a view of index values.
1064
+
1065
+ >>> index = Index()
1066
+ >>> index.update({'a': 1, 'b': 2, 'c': 3})
1067
+ >>> values_view = index.values()
1068
+ >>> 2 in values_view
1069
+ True
1070
+
1071
+ :return: values view
1072
+
1073
+ """
1074
+ return ValuesView(self)
1075
+
1076
+ def items(self):
1077
+ """Set-like object providing a view of index items.
1078
+
1079
+ >>> index = Index()
1080
+ >>> index.update({'a': 1, 'b': 2, 'c': 3})
1081
+ >>> items_view = index.items()
1082
+ >>> ('b', 2) in items_view
1083
+ True
1084
+
1085
+ :return: items view
1086
+
1087
+ """
1088
+ return ItemsView(self)
1089
+
1090
+ __hash__ = None # type: ignore
1091
+
1092
+ def __getstate__(self):
1093
+ return self.directory
1094
+
1095
+ def __setstate__(self, state):
1096
+ self.__init__(state)
1097
+
1098
+ def __eq__(self, other):
1099
+ """index.__eq__(other) <==> index == other
1100
+
1101
+ Compare equality for index and `other`.
1102
+
1103
+ Comparison to another index or ordered dictionary is
1104
+ order-sensitive. Comparison to all other mappings is order-insensitive.
1105
+
1106
+ >>> index = Index()
1107
+ >>> pairs = [('a', 1), ('b', 2), ('c', 3)]
1108
+ >>> index.update(pairs)
1109
+ >>> from collections import OrderedDict
1110
+ >>> od = OrderedDict(pairs)
1111
+ >>> index == od
1112
+ True
1113
+ >>> index == {'c': 3, 'b': 2, 'a': 1}
1114
+ True
1115
+
1116
+ :param other: other mapping in equality comparison
1117
+ :return: True if index equals other
1118
+
1119
+ """
1120
+ if len(self) != len(other):
1121
+ return False
1122
+
1123
+ if isinstance(other, (Index, OrderedDict)):
1124
+ alpha = ((key, self[key]) for key in self)
1125
+ beta = ((key, other[key]) for key in other)
1126
+ pairs = zip(alpha, beta)
1127
+ return not any(a != x or b != y for (a, b), (x, y) in pairs)
1128
+ else:
1129
+ return all(self[key] == other.get(key, ENOVAL) for key in self)
1130
+
1131
+ def __ne__(self, other):
1132
+ """index.__ne__(other) <==> index != other
1133
+
1134
+ Compare inequality for index and `other`.
1135
+
1136
+ Comparison to another index or ordered dictionary is
1137
+ order-sensitive. Comparison to all other mappings is order-insensitive.
1138
+
1139
+ >>> index = Index()
1140
+ >>> index.update([('a', 1), ('b', 2), ('c', 3)])
1141
+ >>> from collections import OrderedDict
1142
+ >>> od = OrderedDict([('c', 3), ('b', 2), ('a', 1)])
1143
+ >>> index != od
1144
+ True
1145
+ >>> index != {'a': 1, 'b': 2}
1146
+ True
1147
+
1148
+ :param other: other mapping in inequality comparison
1149
+ :return: True if index does not equal other
1150
+
1151
+ """
1152
+ return not self == other
1153
+
1154
+ def memoize(self, name=None, typed=False, ignore=()):
1155
+ """Memoizing cache decorator.
1156
+
1157
+ Decorator to wrap callable with memoizing function using cache.
1158
+ Repeated calls with the same arguments will lookup result in cache and
1159
+ avoid function evaluation.
1160
+
1161
+ If name is set to None (default), the callable name will be determined
1162
+ automatically.
1163
+
1164
+ If typed is set to True, function arguments of different types will be
1165
+ cached separately. For example, f(3) and f(3.0) will be treated as
1166
+ distinct calls with distinct results.
1167
+
1168
+ The original underlying function is accessible through the __wrapped__
1169
+ attribute. This is useful for introspection, for bypassing the cache,
1170
+ or for rewrapping the function with a different cache.
1171
+
1172
+ >>> from diskcache import Index
1173
+ >>> mapping = Index()
1174
+ >>> @mapping.memoize()
1175
+ ... def fibonacci(number):
1176
+ ... if number == 0:
1177
+ ... return 0
1178
+ ... elif number == 1:
1179
+ ... return 1
1180
+ ... else:
1181
+ ... return fibonacci(number - 1) + fibonacci(number - 2)
1182
+ >>> print(fibonacci(100))
1183
+ 354224848179261915075
1184
+
1185
+ An additional `__cache_key__` attribute can be used to generate the
1186
+ cache key used for the given arguments.
1187
+
1188
+ >>> key = fibonacci.__cache_key__(100)
1189
+ >>> print(mapping[key])
1190
+ 354224848179261915075
1191
+
1192
+ Remember to call memoize when decorating a callable. If you forget,
1193
+ then a TypeError will occur. Note the lack of parenthenses after
1194
+ memoize below:
1195
+
1196
+ >>> @mapping.memoize
1197
+ ... def test():
1198
+ ... pass
1199
+ Traceback (most recent call last):
1200
+ ...
1201
+ TypeError: name cannot be callable
1202
+
1203
+ :param str name: name given for callable (default None, automatic)
1204
+ :param bool typed: cache different types separately (default False)
1205
+ :param set ignore: positional or keyword args to ignore (default ())
1206
+ :return: callable decorator
1207
+
1208
+ """
1209
+ return self._cache.memoize(name, typed, ignore=ignore)
1210
+
1211
+ @contextmanager
1212
+ def transact(self):
1213
+ """Context manager to perform a transaction by locking the index.
1214
+
1215
+ While the index is locked, no other write operation is permitted.
1216
+ Transactions should therefore be as short as possible. Read and write
1217
+ operations performed in a transaction are atomic. Read operations may
1218
+ occur concurrent to a transaction.
1219
+
1220
+ Transactions may be nested and may not be shared between threads.
1221
+
1222
+ >>> from diskcache import Index
1223
+ >>> mapping = Index()
1224
+ >>> with mapping.transact(): # Atomically increment two keys.
1225
+ ... mapping['total'] = mapping.get('total', 0) + 123.4
1226
+ ... mapping['count'] = mapping.get('count', 0) + 1
1227
+ >>> with mapping.transact(): # Atomically calculate average.
1228
+ ... average = mapping['total'] / mapping['count']
1229
+ >>> average
1230
+ 123.4
1231
+
1232
+ :return: context manager for use in `with` statement
1233
+
1234
+ """
1235
+ with self._cache.transact(retry=True):
1236
+ yield
1237
+
1238
+ def __repr__(self):
1239
+ """index.__repr__() <==> repr(index)
1240
+
1241
+ Return string with printable representation of index.
1242
+
1243
+ """
1244
+ name = type(self).__name__
1245
+ return '{0}({1!r})'.format(name, self.directory)