Python module (submodule repositary), which provides content (video streams) from various online stream sources to corresponding Enigma2, Kodi, Plex plugins

persistent.py 34KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348
  1. """Persistent Data Types
  2. """
  3. import operator as op
  4. import sys
  5. from collections import MutableMapping, OrderedDict, Sequence
  6. from collections import KeysView, ValuesView, ItemsView
  7. from itertools import islice
  8. from shutil import rmtree
  9. from tempfile import mkdtemp
  10. from .core import BytesType, Cache, ENOVAL, TextType, Timeout
  11. if sys.hexversion < 0x03000000:
  12. from itertools import izip as zip # pylint: disable=redefined-builtin,no-name-in-module,ungrouped-imports
  13. range = xrange # pylint: disable=redefined-builtin,invalid-name,undefined-variable
  14. def _make_compare(seq_op, doc):
  15. "Make compare method with Sequence semantics."
  16. def compare(self, that):
  17. "Compare method for deque and sequence."
  18. if not isinstance(that, Sequence):
  19. return NotImplemented
  20. len_self = len(self)
  21. len_that = len(that)
  22. if len_self != len_that:
  23. if seq_op is op.eq:
  24. return False
  25. if seq_op is op.ne:
  26. return True
  27. for alpha, beta in zip(self, that):
  28. if alpha != beta:
  29. return seq_op(alpha, beta)
  30. return seq_op(len_self, len_that)
  31. compare.__name__ = '__{0}__'.format(seq_op.__name__)
  32. doc_str = 'Return True if and only if deque is {0} `that`.'
  33. compare.__doc__ = doc_str.format(doc)
  34. return compare
  35. class Deque(Sequence):
  36. """Persistent sequence with double-ended queue semantics.
  37. Double-ended queue is an ordered collection with optimized access at its
  38. endpoints.
  39. Items are serialized to disk. Deque may be initialized from directory path
  40. where items are stored.
  41. >>> deque = Deque(directory='/tmp/diskcache/deque')
  42. >>> deque
  43. Deque(directory='/tmp/diskcache/deque')
  44. >>> deque.clear()
  45. >>> deque += range(5)
  46. >>> list(deque)
  47. [0, 1, 2, 3, 4]
  48. >>> for value in range(5):
  49. ... deque.appendleft(-value)
  50. >>> len(deque)
  51. 10
  52. >>> list(deque)
  53. [-4, -3, -2, -1, 0, 0, 1, 2, 3, 4]
  54. >>> deque.pop()
  55. 4
  56. >>> deque.popleft()
  57. -4
  58. >>> deque.reverse()
  59. >>> list(deque)
  60. [3, 2, 1, 0, 0, -1, -2, -3]
  61. """
  62. def __init__(self, iterable=(), directory=None):
  63. """Initialize deque instance.
  64. If directory is None then temporary directory created. The directory
  65. will *not* be automatically removed.
  66. :param iterable: iterable of items to append to deque
  67. :param directory: deque directory (default None)
  68. """
  69. if directory is None:
  70. directory = mkdtemp()
  71. self._cache = Cache(directory, eviction_policy='none')
  72. self.extend(iterable)
  73. @classmethod
  74. def fromcache(cls, cache, iterable=()):
  75. """Initialize deque using `cache`.
  76. >>> cache = Cache('/tmp/diskcache/index')
  77. >>> _ = cache.clear()
  78. >>> deque = Deque.fromcache(cache, [5, 6, 7, 8])
  79. >>> len(deque)
  80. 4
  81. >>> 7 in deque
  82. True
  83. >>> deque.popleft()
  84. 5
  85. :param Cache cache: cache to use
  86. :param iterable: iterable of items
  87. :return: initialized Deque
  88. """
  89. # pylint: disable=no-member,protected-access
  90. self = cls.__new__(cls)
  91. self._cache = cache
  92. self.extend(iterable)
  93. return self
  94. @property
  95. def directory(self):
  96. "Directory path where deque is stored."
  97. return self._cache.directory
  98. def _key(self, index):
  99. len_self = len(self)
  100. if index < 0:
  101. index += len_self
  102. if index < 0:
  103. raise IndexError('deque index out of range')
  104. elif index >= len_self:
  105. raise IndexError('deque index out of range')
  106. diff = len_self - index - 1
  107. _cache_iterkeys = self._cache.iterkeys
  108. try:
  109. if index <= diff:
  110. iter_keys = _cache_iterkeys()
  111. key = next(islice(iter_keys, index, index + 1))
  112. else:
  113. iter_keys = _cache_iterkeys(reverse=True)
  114. key = next(islice(iter_keys, diff, diff + 1))
  115. except StopIteration:
  116. raise IndexError('deque index out of range')
  117. return key
  118. def __getitem__(self, index):
  119. """deque.__getitem__(index) <==> deque[index]
  120. Return corresponding item for `index` in deque.
  121. >>> deque = Deque(directory='/tmp/diskcache/deque')
  122. >>> deque.clear()
  123. >>> deque.extend('abcde')
  124. >>> deque[0]
  125. 'a'
  126. >>> deque[-1]
  127. 'e'
  128. >>> deque[2]
  129. 'c'
  130. :param int index: index of item
  131. :return: corresponding item
  132. :raises IndexError: if index out of range
  133. """
  134. _key = self._key
  135. _cache = self._cache
  136. while True:
  137. try:
  138. key = _key(index)
  139. return _cache[key]
  140. except (KeyError, Timeout):
  141. continue
  142. def __setitem__(self, index, value):
  143. """deque.__setitem__(index, value) <==> deque[index] = value
  144. Store `value` in deque at `index`.
  145. >>> deque = Deque(directory='/tmp/diskcache/deque')
  146. >>> deque.clear()
  147. >>> deque.extend([None] * 3)
  148. >>> deque[0] = 'a'
  149. >>> deque[1] = 'b'
  150. >>> deque[-1] = 'c'
  151. >>> ''.join(deque)
  152. 'abc'
  153. :param int index: index of value
  154. :param value: value to store
  155. :raises IndexError: if index out of range
  156. """
  157. _key = self._key
  158. _cache = self._cache
  159. while True:
  160. try:
  161. key = _key(index)
  162. _cache[key] = value
  163. return
  164. except Timeout:
  165. continue
  166. def __delitem__(self, index):
  167. """deque.__delitem__(index) <==> del deque[index]
  168. Delete item in deque at `index`.
  169. >>> deque = Deque(directory='/tmp/diskcache/deque')
  170. >>> deque.clear()
  171. >>> deque.extend([None] * 3)
  172. >>> del deque[0]
  173. >>> del deque[1]
  174. >>> del deque[-1]
  175. >>> len(deque)
  176. 0
  177. :param int index: index of item
  178. :raises IndexError: if index out of range
  179. """
  180. _key = self._key
  181. _cache = self._cache
  182. while True:
  183. try:
  184. key = _key(index)
  185. del _cache[key]
  186. return
  187. except (KeyError, Timeout):
  188. continue
  189. def __repr__(self):
  190. """deque.__repr__() <==> repr(deque)
  191. Return string with printable representation of deque.
  192. """
  193. name = type(self).__name__
  194. return '{0}(directory={1!r})'.format(name, self.directory)
  195. __eq__ = _make_compare(op.eq, 'equal to')
  196. __ne__ = _make_compare(op.ne, 'not equal to')
  197. __lt__ = _make_compare(op.lt, 'less than')
  198. __gt__ = _make_compare(op.gt, 'greater than')
  199. __le__ = _make_compare(op.le, 'less than or equal to')
  200. __ge__ = _make_compare(op.ge, 'greater than or equal to')
  201. def __iadd__(self, iterable):
  202. """deque.__iadd__(iterable) <==> deque += iterable
  203. Extend back side of deque with items from iterable.
  204. """
  205. self.extend(iterable)
  206. return self
  207. def __iter__(self):
  208. """deque.__iter__() <==> iter(deque)
  209. Return iterator of deque from front to back.
  210. """
  211. _cache = self._cache
  212. for key in _cache.iterkeys():
  213. try:
  214. yield _cache[key]
  215. except (KeyError, Timeout):
  216. pass
  217. def __len__(self):
  218. """deque.__len__() <==> len(deque)
  219. Return length of deque.
  220. """
  221. return len(self._cache)
  222. def __reversed__(self):
  223. """deque.__reversed__() <==> reversed(deque)
  224. Return iterator of deque from back to front.
  225. >>> deque = Deque(directory='/tmp/diskcache/deque')
  226. >>> deque.clear()
  227. >>> deque.extend('abcd')
  228. >>> iterator = reversed(deque)
  229. >>> next(iterator)
  230. 'd'
  231. >>> list(iterator)
  232. ['c', 'b', 'a']
  233. """
  234. _cache = self._cache
  235. for key in _cache.iterkeys(reverse=True):
  236. try:
  237. yield _cache[key]
  238. except (KeyError, Timeout):
  239. pass
  240. def __getstate__(self):
  241. return self.directory
  242. def __setstate__(self, state):
  243. self.__init__(directory=state)
  244. def append(self, value):
  245. """Add `value` to back of deque.
  246. >>> deque = Deque(directory='/tmp/diskcache/deque')
  247. >>> deque.clear()
  248. >>> deque.append('a')
  249. >>> deque.append('b')
  250. >>> deque.append('c')
  251. >>> list(deque)
  252. ['a', 'b', 'c']
  253. :param value: value to add to back of deque
  254. """
  255. _cache_push = self._cache.push
  256. while True:
  257. try:
  258. _cache_push(value)
  259. return
  260. except Timeout:
  261. continue
  262. def appendleft(self, value):
  263. """Add `value` to front of deque.
  264. >>> deque = Deque(directory='/tmp/diskcache/deque')
  265. >>> deque.clear()
  266. >>> deque.appendleft('a')
  267. >>> deque.appendleft('b')
  268. >>> deque.appendleft('c')
  269. >>> list(deque)
  270. ['c', 'b', 'a']
  271. :param value: value to add to front of deque
  272. """
  273. _cache_push = self._cache.push
  274. while True:
  275. try:
  276. _cache_push(value, side='front')
  277. return
  278. except Timeout:
  279. continue
  280. def clear(self):
  281. """Remove all elements from deque.
  282. """
  283. _cache_clear = self._cache.clear
  284. while True:
  285. try:
  286. _cache_clear()
  287. return
  288. except Timeout:
  289. continue
  290. def count(self, value):
  291. """Return number of occurrences of `value` in deque.
  292. >>> deque = Deque(directory='/tmp/diskcache/deque')
  293. >>> deque.clear()
  294. >>> deque += [num for num in range(1, 5) for _ in range(num)]
  295. >>> deque.count(0)
  296. 0
  297. >>> deque.count(1)
  298. 1
  299. >>> deque.count(4)
  300. 4
  301. :param value: value to count in deque
  302. """
  303. return sum(1 for item in self if item == value)
  304. def extend(self, iterable):
  305. """Extend back side of deque with values from `iterable`.
  306. :param iterable: iterable of values
  307. """
  308. for value in iterable:
  309. self.append(value)
  310. def extendleft(self, iterable):
  311. """Extend front side of deque with value from `iterable`.
  312. >>> deque = Deque(directory='/tmp/diskcache/deque')
  313. >>> deque.clear()
  314. >>> deque.extendleft('abc')
  315. >>> list(deque)
  316. ['c', 'b', 'a']
  317. :param iterable: iterable of values
  318. """
  319. for value in iterable:
  320. self.appendleft(value)
  321. def pop(self):
  322. """Remove and return value at back of deque.
  323. If deque is empty then raise IndexError.
  324. >>> deque = Deque(directory='/tmp/diskcache/deque')
  325. >>> deque.clear()
  326. >>> deque += 'ab'
  327. >>> deque.pop()
  328. 'b'
  329. >>> deque.pop()
  330. 'a'
  331. >>> deque.pop()
  332. Traceback (most recent call last):
  333. ...
  334. IndexError: pop from an empty deque
  335. :raises IndexError: if deque is empty
  336. """
  337. _cache_pull = self._cache.pull
  338. while True:
  339. try:
  340. default = None, ENOVAL
  341. _, value = _cache_pull(default=default, side='back')
  342. except Timeout:
  343. continue
  344. else:
  345. if value is ENOVAL:
  346. raise IndexError('pop from an empty deque')
  347. return value
  348. def popleft(self):
  349. """Remove and return value at front of deque.
  350. >>> deque = Deque(directory='/tmp/diskcache/deque')
  351. >>> deque.clear()
  352. >>> deque += 'ab'
  353. >>> deque.popleft()
  354. 'a'
  355. >>> deque.popleft()
  356. 'b'
  357. >>> deque.popleft()
  358. Traceback (most recent call last):
  359. ...
  360. IndexError: pop from an empty deque
  361. """
  362. _cache_pull = self._cache.pull
  363. while True:
  364. try:
  365. default = None, ENOVAL
  366. _, value = _cache_pull(default=default)
  367. except Timeout:
  368. continue
  369. else:
  370. if value is ENOVAL:
  371. raise IndexError('pop from an empty deque')
  372. return value
  373. def remove(self, value):
  374. """Remove first occurrence of `value` in deque.
  375. >>> deque = Deque(directory='/tmp/diskcache/deque')
  376. >>> deque.clear()
  377. >>> deque += 'aab'
  378. >>> deque.remove('a')
  379. >>> list(deque)
  380. ['a', 'b']
  381. >>> deque.remove('b')
  382. >>> list(deque)
  383. ['a']
  384. >>> deque.remove('c')
  385. Traceback (most recent call last):
  386. ...
  387. ValueError: deque.remove(value): value not in deque
  388. :param value: value to remove
  389. :raises ValueError: if value not in deque
  390. """
  391. _cache = self._cache
  392. for key in _cache.iterkeys():
  393. try:
  394. while True:
  395. try:
  396. item = _cache[key]
  397. except Timeout:
  398. continue
  399. else:
  400. break
  401. except KeyError:
  402. continue
  403. else:
  404. if value == item:
  405. try:
  406. while True:
  407. try:
  408. del _cache[key]
  409. except Timeout:
  410. continue
  411. else:
  412. return
  413. except KeyError:
  414. continue
  415. raise ValueError('deque.remove(value): value not in deque')
  416. def reverse(self):
  417. """Reverse deque in place.
  418. """
  419. # pylint: disable=protected-access
  420. directory = mkdtemp()
  421. temp = None
  422. try:
  423. temp = Deque(iterable=reversed(self), directory=directory)
  424. self.clear()
  425. self.extend(temp)
  426. finally:
  427. if temp is not None:
  428. temp._cache.close()
  429. del temp
  430. rmtree(directory)
  431. def rotate(self, steps=1):
  432. """Rotate deque right by `steps`.
  433. If steps is negative then rotate left.
  434. >>> deque = Deque(directory='/tmp/diskcache/deque')
  435. >>> deque.clear()
  436. >>> deque += range(5)
  437. >>> deque.rotate(2)
  438. >>> list(deque)
  439. [3, 4, 0, 1, 2]
  440. >>> deque.rotate(-1)
  441. >>> list(deque)
  442. [4, 0, 1, 2, 3]
  443. :param int steps: number of steps to rotate (default 1)
  444. """
  445. if not isinstance(steps, int):
  446. type_name = type(steps).__name__
  447. raise TypeError('integer argument expected, got %s' % type_name)
  448. len_self = len(self)
  449. if not len_self:
  450. return
  451. if steps >= 0:
  452. steps %= len_self
  453. for _ in range(steps):
  454. try:
  455. value = self.pop()
  456. except IndexError:
  457. return
  458. else:
  459. self.appendleft(value)
  460. else:
  461. steps *= -1
  462. steps %= len_self
  463. for _ in range(steps):
  464. try:
  465. value = self.popleft()
  466. except IndexError:
  467. return
  468. else:
  469. self.append(value)
  470. def __del__(self):
  471. self._cache.close()
  472. __hash__ = None
  473. class Index(MutableMapping):
  474. """Persistent mutable mapping with insertion order iteration.
  475. Items are serialized to disk. Index may be initialized from directory path
  476. where items are stored.
  477. Hashing protocol is not used. Keys are looked up by their serialized
  478. format. See ``diskcache.Disk`` for details.
  479. >>> index = Index('/tmp/diskcache/index')
  480. >>> index
  481. Index('/tmp/diskcache/index')
  482. >>> index.clear()
  483. >>> index.update([('a', 1), ('b', 2), ('c', 3)])
  484. >>> index['a']
  485. 1
  486. >>> list(index)
  487. ['a', 'b', 'c']
  488. >>> len(index)
  489. 3
  490. >>> del index['b']
  491. >>> index.popitem()
  492. ('c', 3)
  493. """
  494. def __init__(self, *args, **kwargs):
  495. """Initialize index in directory and update items.
  496. Optional first argument may be string specifying directory where items
  497. are stored. When None or not given, temporary directory is created.
  498. >>> index = Index({'a': 1, 'b': 2, 'c': 3})
  499. >>> len(index)
  500. 3
  501. >>> directory = index.directory
  502. >>> inventory = Index(directory, d=4)
  503. >>> inventory['b']
  504. 2
  505. >>> len(inventory)
  506. 4
  507. """
  508. if args and isinstance(args[0], (BytesType, TextType)):
  509. directory = args[0]
  510. args = args[1:]
  511. else:
  512. if args and args[0] is None:
  513. args = args[1:]
  514. directory = mkdtemp(prefix='diskcache-')
  515. self._cache = Cache(directory, eviction_policy='none')
  516. self.update(*args, **kwargs)
  517. @classmethod
  518. def fromcache(cls, cache, *args, **kwargs):
  519. """Initialize index using `cache` and update items.
  520. >>> cache = Cache('/tmp/diskcache/index')
  521. >>> _ = cache.clear()
  522. >>> index = Index.fromcache(cache, {'a': 1, 'b': 2, 'c': 3})
  523. >>> len(index)
  524. 3
  525. >>> 'b' in index
  526. True
  527. >>> index['c']
  528. 3
  529. :param Cache cache: cache to use
  530. :param args: mapping or sequence of items
  531. :param kwargs: mapping of items
  532. :return: initialized Index
  533. """
  534. # pylint: disable=no-member,protected-access
  535. self = cls.__new__(cls)
  536. self._cache = cache
  537. self.update(*args, **kwargs)
  538. return self
  539. @property
  540. def directory(self):
  541. "Directory path where items are stored."
  542. return self._cache.directory
  543. def __getitem__(self, key):
  544. """index.__getitem__(key) <==> index[key]
  545. Return corresponding value for `key` in index.
  546. >>> index = Index('/tmp/diskcache/index')
  547. >>> index.clear()
  548. >>> index.update({'a': 1, 'b': 2})
  549. >>> index['a']
  550. 1
  551. >>> index['b']
  552. 2
  553. >>> index['c']
  554. Traceback (most recent call last):
  555. ...
  556. KeyError: 'c'
  557. :param key: key for item
  558. :return: value for item in index with given key
  559. :raises KeyError: if key is not found
  560. """
  561. _cache = self._cache
  562. while True:
  563. try:
  564. return _cache[key]
  565. except Timeout:
  566. continue
  567. def __setitem__(self, key, value):
  568. """index.__setitem__(key, value) <==> index[key] = value
  569. Set `key` and `value` item in index.
  570. >>> index = Index('/tmp/diskcache/index')
  571. >>> index.clear()
  572. >>> index['a'] = 1
  573. >>> index[0] = None
  574. >>> len(index)
  575. 2
  576. :param key: key for item
  577. :param value: value for item
  578. """
  579. _cache = self._cache
  580. while True:
  581. try:
  582. _cache[key] = value
  583. except Timeout:
  584. continue
  585. else:
  586. return
  587. def __delitem__(self, key):
  588. """index.__delitem__(key) <==> del index[key]
  589. Delete corresponding item for `key` from index.
  590. >>> index = Index('/tmp/diskcache/index')
  591. >>> index.clear()
  592. >>> index.update({'a': 1, 'b': 2})
  593. >>> del index['a']
  594. >>> del index['b']
  595. >>> len(index)
  596. 0
  597. >>> del index['c']
  598. Traceback (most recent call last):
  599. ...
  600. KeyError: 'c'
  601. :param key: key for item
  602. :raises KeyError: if key is not found
  603. """
  604. _cache = self._cache
  605. while True:
  606. try:
  607. del _cache[key]
  608. except Timeout:
  609. continue
  610. else:
  611. return
  612. def setdefault(self, key, default=None):
  613. """Set and get value for `key` in index using `default`.
  614. If `key` is not in index then set corresponding value to `default`. If
  615. `key` is in index then ignore `default` and return existing value.
  616. >>> index = Index('/tmp/diskcache/index')
  617. >>> index.clear()
  618. >>> index.setdefault('a', 0)
  619. 0
  620. >>> index.setdefault('a', 1)
  621. 0
  622. :param key: key for item
  623. :param default: value if key is missing (default None)
  624. :return: value for item in index with given key
  625. """
  626. _cache = self._cache
  627. while True:
  628. try:
  629. return self[key]
  630. except KeyError:
  631. while True:
  632. try:
  633. _cache.add(key, default)
  634. except Timeout:
  635. continue
  636. else:
  637. break
  638. def pop(self, key, default=ENOVAL):
  639. """Remove corresponding item for `key` from index and return value.
  640. If `key` is missing then return `default`. If `default` is `ENOVAL`
  641. then raise KeyError.
  642. >>> index = Index('/tmp/diskcache/index', {'a': 1, 'b': 2})
  643. >>> index.pop('a')
  644. 1
  645. >>> index.pop('b')
  646. 2
  647. >>> index.pop('c', default=3)
  648. 3
  649. >>> index.pop('d')
  650. Traceback (most recent call last):
  651. ...
  652. KeyError: 'd'
  653. :param key: key for item
  654. :param default: return value if key is missing (default ENOVAL)
  655. :return: value for item if key is found else default
  656. :raises KeyError: if key is not found and default is ENOVAL
  657. """
  658. _cache = self._cache
  659. while True:
  660. try:
  661. value = _cache.pop(key, default=default)
  662. except Timeout:
  663. continue
  664. else:
  665. if value is ENOVAL:
  666. raise KeyError(key)
  667. return value
  668. def popitem(self, last=True):
  669. """Remove and return item pair.
  670. Item pairs are returned in last-in-first-out (LIFO) order if last is
  671. True else first-in-first-out (FIFO) order. LIFO order imitates a stack
  672. and FIFO order imitates a queue.
  673. >>> index = Index('/tmp/diskcache/index')
  674. >>> index.clear()
  675. >>> index.update([('a', 1), ('b', 2), ('c', 3)])
  676. >>> index.popitem()
  677. ('c', 3)
  678. >>> index.popitem(last=False)
  679. ('a', 1)
  680. >>> index.popitem()
  681. ('b', 2)
  682. >>> index.popitem()
  683. Traceback (most recent call last):
  684. ...
  685. KeyError
  686. :param bool last: pop last item pair (default True)
  687. :return: key and value item pair
  688. :raises KeyError: if index is empty
  689. """
  690. # pylint: disable=arguments-differ
  691. _cache = self._cache
  692. while True:
  693. try:
  694. if last:
  695. key = next(reversed(_cache))
  696. else:
  697. key = next(iter(_cache))
  698. except StopIteration:
  699. raise KeyError
  700. try:
  701. value = _cache.pop(key)
  702. except (KeyError, Timeout):
  703. continue
  704. else:
  705. return key, value
  706. def push(self, value, prefix=None, side='back'):
  707. """Push `value` onto `side` of queue in index identified by `prefix`.
  708. When prefix is None, integer keys are used. Otherwise, string keys are
  709. used in the format "prefix-integer". Integer starts at 500 trillion.
  710. Defaults to pushing value on back of queue. Set side to 'front' to push
  711. value on front of queue. Side must be one of 'back' or 'front'.
  712. See also `Index.pull`.
  713. >>> index = Index('/tmp/diskcache/index')
  714. >>> index.clear()
  715. >>> print(index.push('apples'))
  716. 500000000000000
  717. >>> print(index.push('beans'))
  718. 500000000000001
  719. >>> print(index.push('cherries', side='front'))
  720. 499999999999999
  721. >>> index[500000000000001]
  722. 'beans'
  723. >>> index.push('dates', prefix='fruit')
  724. 'fruit-500000000000000'
  725. :param value: value for item
  726. :param str prefix: key prefix (default None, key is integer)
  727. :param str side: either 'back' or 'front' (default 'back')
  728. :return: key for item in cache
  729. """
  730. _cache_push = self._cache.push
  731. while True:
  732. try:
  733. return _cache_push(value, prefix, side)
  734. except Timeout:
  735. continue
  736. def pull(self, prefix=None, default=(None, None), side='front'):
  737. """Pull key and value item pair from `side` of queue in index.
  738. When prefix is None, integer keys are used. Otherwise, string keys are
  739. used in the format "prefix-integer". Integer starts at 500 trillion.
  740. If queue is empty, return default.
  741. Defaults to pulling key and value item pairs from front of queue. Set
  742. side to 'back' to pull from back of queue. Side must be one of 'front'
  743. or 'back'.
  744. See also `Index.push`.
  745. >>> index = Index('/tmp/diskcache/index')
  746. >>> index.clear()
  747. >>> for letter in 'abc':
  748. ... print(index.push(letter))
  749. 500000000000000
  750. 500000000000001
  751. 500000000000002
  752. >>> key, value = index.pull()
  753. >>> print(key)
  754. 500000000000000
  755. >>> value
  756. 'a'
  757. >>> _, value = index.pull(side='back')
  758. >>> value
  759. 'c'
  760. >>> index.pull(prefix='fruit')
  761. (None, None)
  762. :param str prefix: key prefix (default None, key is integer)
  763. :param default: value to return if key is missing
  764. (default (None, None))
  765. :param str side: either 'front' or 'back' (default 'front')
  766. :return: key and value item pair or default if queue is empty
  767. """
  768. _cache_pull = self._cache.pull
  769. while True:
  770. try:
  771. return _cache_pull(prefix, default, side)
  772. except Timeout:
  773. continue
  774. def clear(self):
  775. """Remove all items from index.
  776. """
  777. _cache_clear = self._cache.clear
  778. while True:
  779. try:
  780. _cache_clear()
  781. return
  782. except Timeout:
  783. continue
  784. def __iter__(self):
  785. """index.__iter__() <==> iter(index)
  786. Return iterator of index keys in insertion order.
  787. """
  788. return iter(self._cache)
  789. def __reversed__(self):
  790. """index.__reversed__() <==> reversed(index)
  791. Return iterator of index keys in reversed insertion order.
  792. >>> index = Index('/tmp/diskcache/index')
  793. >>> index.clear()
  794. >>> index.update([('a', 1), ('b', 2), ('c', 3)])
  795. >>> iterator = reversed(index)
  796. >>> next(iterator)
  797. 'c'
  798. >>> list(iterator)
  799. ['b', 'a']
  800. """
  801. return reversed(self._cache)
  802. def __len__(self):
  803. """index.__len__() <==> len(index)
  804. Return length of index.
  805. """
  806. return len(self._cache)
  807. if sys.hexversion < 0x03000000:
  808. def keys(self):
  809. """List of index keys.
  810. >>> index = Index('/tmp/diskcache/index')
  811. >>> index.clear()
  812. >>> index.update([('a', 1), ('b', 2), ('c', 3)])
  813. >>> index.keys()
  814. ['a', 'b', 'c']
  815. :return: list of keys
  816. """
  817. return list(self._cache)
  818. def values(self):
  819. """List of index values.
  820. >>> index = Index('/tmp/diskcache/index')
  821. >>> index.clear()
  822. >>> index.update([('a', 1), ('b', 2), ('c', 3)])
  823. >>> index.values()
  824. [1, 2, 3]
  825. :return: list of values
  826. """
  827. return list(self.itervalues())
  828. def items(self):
  829. """List of index items.
  830. >>> index = Index('/tmp/diskcache/index')
  831. >>> index.clear()
  832. >>> index.update([('a', 1), ('b', 2), ('c', 3)])
  833. >>> index.items()
  834. [('a', 1), ('b', 2), ('c', 3)]
  835. :return: list of items
  836. """
  837. return list(self.iteritems())
  838. def iterkeys(self):
  839. """Iterator of index keys.
  840. >>> index = Index('/tmp/diskcache/index')
  841. >>> index.clear()
  842. >>> index.update([('a', 1), ('b', 2), ('c', 3)])
  843. >>> list(index.iterkeys())
  844. ['a', 'b', 'c']
  845. :return: iterator of keys
  846. """
  847. return iter(self._cache)
  848. def itervalues(self):
  849. """Iterator of index values.
  850. >>> index = Index('/tmp/diskcache/index')
  851. >>> index.clear()
  852. >>> index.update([('a', 1), ('b', 2), ('c', 3)])
  853. >>> list(index.itervalues())
  854. [1, 2, 3]
  855. :return: iterator of values
  856. """
  857. _cache = self._cache
  858. for key in _cache:
  859. while True:
  860. try:
  861. yield _cache[key]
  862. except KeyError:
  863. break
  864. except Timeout:
  865. continue
  866. else:
  867. break
  868. def iteritems(self):
  869. """Iterator of index items.
  870. >>> index = Index('/tmp/diskcache/index')
  871. >>> index.clear()
  872. >>> index.update([('a', 1), ('b', 2), ('c', 3)])
  873. >>> list(index.iteritems())
  874. [('a', 1), ('b', 2), ('c', 3)]
  875. :return: iterator of items
  876. """
  877. _cache = self._cache
  878. for key in _cache:
  879. while True:
  880. try:
  881. yield key, _cache[key]
  882. except KeyError:
  883. break
  884. except Timeout:
  885. continue
  886. else:
  887. break
  888. def viewkeys(self):
  889. """Set-like object providing a view of index keys.
  890. >>> index = Index('/tmp/diskcache/index')
  891. >>> index.clear()
  892. >>> index.update({'a': 1, 'b': 2, 'c': 3})
  893. >>> keys_view = index.viewkeys()
  894. >>> 'b' in keys_view
  895. True
  896. :return: keys view
  897. """
  898. return KeysView(self)
  899. def viewvalues(self):
  900. """Set-like object providing a view of index values.
  901. >>> index = Index('/tmp/diskcache/index')
  902. >>> index.clear()
  903. >>> index.update({'a': 1, 'b': 2, 'c': 3})
  904. >>> values_view = index.viewvalues()
  905. >>> 2 in values_view
  906. True
  907. :return: values view
  908. """
  909. return ValuesView(self)
  910. def viewitems(self):
  911. """Set-like object providing a view of index items.
  912. >>> index = Index('/tmp/diskcache/index')
  913. >>> index.clear()
  914. >>> index.update({'a': 1, 'b': 2, 'c': 3})
  915. >>> items_view = index.viewitems()
  916. >>> ('b', 2) in items_view
  917. True
  918. :return: items view
  919. """
  920. return ItemsView(self)
  921. else:
  922. def keys(self):
  923. """Set-like object providing a view of index keys.
  924. >>> index = Index('/tmp/diskcache/index')
  925. >>> index.clear()
  926. >>> index.update({'a': 1, 'b': 2, 'c': 3})
  927. >>> keys_view = index.keys()
  928. >>> 'b' in keys_view
  929. True
  930. :return: keys view
  931. """
  932. return KeysView(self)
  933. def values(self):
  934. """Set-like object providing a view of index values.
  935. >>> index = Index('/tmp/diskcache/index')
  936. >>> index.clear()
  937. >>> index.update({'a': 1, 'b': 2, 'c': 3})
  938. >>> values_view = index.values()
  939. >>> 2 in values_view
  940. True
  941. :return: values view
  942. """
  943. return ValuesView(self)
  944. def items(self):
  945. """Set-like object providing a view of index items.
  946. >>> index = Index('/tmp/diskcache/index')
  947. >>> index.clear()
  948. >>> index.update({'a': 1, 'b': 2, 'c': 3})
  949. >>> items_view = index.items()
  950. >>> ('b', 2) in items_view
  951. True
  952. :return: items view
  953. """
  954. return ItemsView(self)
  955. __hash__ = None
  956. def __getstate__(self):
  957. return self.directory
  958. def __setstate__(self, state):
  959. self.__init__(state)
  960. def __eq__(self, other):
  961. """index.__eq__(other) <==> index == other
  962. Compare equality for index and `other`.
  963. Comparison to another index or ordered dictionary is
  964. order-sensitive. Comparison to all other mappings is order-insensitive.
  965. >>> index = Index('/tmp/diskcache/index')
  966. >>> index.clear()
  967. >>> pairs = [('a', 1), ('b', 2), ('c', 3)]
  968. >>> index.update(pairs)
  969. >>> from collections import OrderedDict
  970. >>> od = OrderedDict(pairs)
  971. >>> index == od
  972. True
  973. >>> index == {'c': 3, 'b': 2, 'a': 1}
  974. True
  975. :param other: other mapping in equality comparison
  976. """
  977. if len(self) != len(other):
  978. return False
  979. if isinstance(other, (Index, OrderedDict)):
  980. alpha = ((key, self[key]) for key in self)
  981. beta = ((key, other[key]) for key in other)
  982. pairs = zip(alpha, beta)
  983. return not any(a != x or b != y for (a, b), (x, y) in pairs)
  984. else:
  985. return all(self[key] == other.get(key, ENOVAL) for key in self)
  986. def __ne__(self, other):
  987. """index.__ne__(other) <==> index != other
  988. Compare inequality for index and `other`.
  989. Comparison to another index or ordered dictionary is
  990. order-sensitive. Comparison to all other mappings is order-insensitive.
  991. >>> index = Index('/tmp/diskcache/index')
  992. >>> index.clear()
  993. >>> index.update([('a', 1), ('b', 2), ('c', 3)])
  994. >>> from collections import OrderedDict
  995. >>> od = OrderedDict([('c', 3), ('b', 2), ('a', 1)])
  996. >>> index != od
  997. True
  998. >>> index != {'a': 1, 'b': 2}
  999. True
  1000. :param other: other mapping in inequality comparison
  1001. """
  1002. return not self == other
  1003. def __repr__(self):
  1004. """index.__repr__() <==> repr(index)
  1005. Return string with printable representation of index.
  1006. """
  1007. name = type(self).__name__
  1008. return '{0}({1!r})'.format(name, self.directory)
  1009. def __del__(self):
  1010. self._cache.close()