Ubuntu

Merge lp:~logan/ubuntu/saucy/python-repoze.lru/0.6-1ubuntu1 into lp:ubuntu/saucy/python-repoze.lru

Proposed by Logan Rosen on 2013-06-07
Status: Merged
Merged at revision: 12
Proposed branch: lp:~logan/ubuntu/saucy/python-repoze.lru/0.6-1ubuntu1
Merge into: lp:ubuntu/saucy/python-repoze.lru
Diff against target: 1945 lines (+1277/-183) 23 files modified
To merge this branch: bzr merge lp:~logan/ubuntu/saucy/python-repoze.lru/0.6-1ubuntu1
Reviewer Review Type Date Requested Status
Didier Roche Approve on 2013-06-12
Ubuntu branches 2013-06-07 Pending
Review via email: mp+167882@code.launchpad.net
To post a comment you must log in.
Didier Roche (didrocks) wrote :

Looking good, approved!

review: Approve

Preview Diff

1=== modified file '.gitignore'
2--- .gitignore 2012-04-09 10:24:06 +0000
3+++ .gitignore 2013-06-07 00:36:27 +0000
4@@ -10,3 +10,4 @@
5 nosetests.xml
6 coverage.xml
7 repoze/lru/coverage.xml
8+docs/_build/*
9
10=== modified file '.pc/fix-lp-tests.patch/repoze/lru/tests.py'
11--- .pc/fix-lp-tests.patch/repoze/lru/tests.py 2012-05-24 11:54:28 +0000
12+++ .pc/fix-lp-tests.patch/repoze/lru/tests.py 2013-06-07 00:36:27 +0000
13@@ -1,14 +1,15 @@
14-#!/usr/bin/python -tt
15 import random
16 import time
17 import unittest
18
19 try:
20 range = xrange
21-except NameError: # pragma: no cover
22+except NameError: # pragma: NO COVER (Python3)
23 pass
24
25+
26 class LRUCacheTests(unittest.TestCase):
27+
28 def _getTargetClass(self):
29 from repoze.lru import LRUCache
30 return LRUCache
31@@ -23,7 +24,7 @@
32
33 # lengths of data structures
34 self.assertEqual(len(cache.clock_keys), len(cache.clock_refs))
35- self.assertTrue(len(cache.data) <=len(cache.clock_refs))
36+ self.assertTrue(len(cache.data) <= len(cache.clock_refs))
37
38 # For each item in cache.data
39 # 1. pos must be a valid index
40@@ -181,6 +182,9 @@
41 else:
42 cache.put(item, "item%s" % item)
43
44+ self.assertEqual(cache.misses, 0)
45+ self.assertEqual(cache.evictions, 0)
46+
47 self.check_cache_is_consistent(cache)
48
49 def test_imperfect_hitrate(self):
50@@ -214,8 +218,36 @@
51 self.assertTrue(hit_ratio > 45)
52 self.assertTrue(hit_ratio < 55)
53
54+ # The internal cache counters should have the same information
55+ internal_hit_ratio = 100 * cache.hits / cache.lookups
56+ self.assertTrue(internal_hit_ratio > 45)
57+ self.assertTrue(internal_hit_ratio < 55)
58+
59+ # The internal miss counters should also be around 50%
60+ internal_miss_ratio = 100 * cache.misses / cache.lookups
61+ self.assertTrue(internal_miss_ratio > 45)
62+ self.assertTrue(internal_miss_ratio < 55)
63+
64 self.check_cache_is_consistent(cache)
65
66+ def test_eviction_counter(self):
67+ cache = self._makeOne(2)
68+ cache.put(1, 1)
69+ cache.put(2, 1)
70+ self.assertEqual(cache.evictions, 0)
71+
72+ cache.put(3, 1)
73+ cache.put(4, 1)
74+ self.assertEqual(cache.evictions, 2)
75+
76+ cache.put(3, 1)
77+ cache.put(4, 1)
78+ self.assertEqual(cache.evictions, 2)
79+
80+ cache.clear()
81+ self.assertEqual(cache.evictions, 0)
82+
83+
84 def test_it(self):
85 cache = self._makeOne(3)
86 self.assertEqual(cache.get('a'), None)
87@@ -271,6 +303,7 @@
88
89
90 class ExpiringLRUCacheTests(LRUCacheTests):
91+
92 def _getTargetClass(self):
93 from repoze.lru import ExpiringLRUCache
94 return ExpiringLRUCache
95@@ -316,6 +349,7 @@
96 # All clock_refs must be True or False, nothing else.
97 for clock_ref in cache.clock_refs:
98 self.assertTrue(clock_ref is True or clock_ref is False)
99+
100 def test_it(self):
101 """Test a sequence of operations
102
103@@ -455,7 +489,9 @@
104 self.assertEqual(cache.get("foo3"), "bar3")
105 self.check_cache_is_consistent(cache)
106
107+
108 class DecoratorTests(unittest.TestCase):
109+
110 def _getTargetClass(self):
111 from repoze.lru import lru_cache
112 return lru_cache
113@@ -526,6 +562,111 @@
114 self.assertEqual(result3, 2 * "hello")
115 self.assertTrue(stop - start > 0.1)
116
117+
118 class DummyLRUCache(dict):
119+
120 def put(self, k, v):
121 return self.__setitem__(k, v)
122+
123+
124+class CacherMaker(unittest.TestCase):
125+
126+ def _getTargetClass(self):
127+ from repoze.lru import CacheMaker
128+ return CacheMaker
129+
130+ def _makeOne(self, *args, **kw):
131+ return self._getTargetClass()(*args, **kw)
132+
133+ def test_named_cache(self):
134+ maker = self._makeOne()
135+ size = 10
136+ name = "name"
137+ decorated = maker.lrucache(maxsize=size, name=name)(_adder)
138+ self.assertEqual(list(maker._cache.keys()), [name])
139+ self.assertEqual(maker._cache[name].size, size)
140+ decorated(10)
141+ decorated(11)
142+ self.assertEqual(len(maker._cache[name].data),2)
143+
144+ def test_exception(self):
145+ maker = self._makeOne()
146+ size = 10
147+ name = "name"
148+ decorated = maker.lrucache(maxsize=size, name=name)(_adder)
149+ self.assertRaises(KeyError, maker.lrucache, maxsize=size, name=name)
150+ self.assertRaises(ValueError, maker.lrucache)
151+
152+ def test_defaultvalue_and_clear(self):
153+ size = 10
154+ maker = self._makeOne(maxsize=size)
155+ for i in range(100):
156+ decorated = maker.lrucache()(_adder)
157+ decorated(10)
158+
159+ self.assertEqual(len(maker._cache) , 100)
160+ for _cache in maker._cache.values():
161+ self.assertEqual( _cache.size,size)
162+ self.assertEqual(len(_cache.data),1)
163+ ## and test clear cache
164+ maker.clear()
165+ for _cache in maker._cache.values():
166+ self.assertEqual( _cache.size,size)
167+ self.assertEqual(len(_cache.data),0)
168+
169+ def test_clear_with_single_name(self):
170+ maker = self._makeOne(maxsize=10)
171+ one = maker.lrucache(name='one')(_adder)
172+ two = maker.lrucache(name='two')(_adder)
173+ for i in range(100):
174+ _ = one(i)
175+ _ = two(i)
176+ self.assertEqual(len(maker._cache['one'].data), 10)
177+ self.assertEqual(len(maker._cache['two'].data), 10)
178+ maker.clear('one')
179+ self.assertEqual(len(maker._cache['one'].data), 0)
180+ self.assertEqual(len(maker._cache['two'].data), 10)
181+
182+ def test_clear_with_multiple_names(self):
183+ maker = self._makeOne(maxsize=10)
184+ one = maker.lrucache(name='one')(_adder)
185+ two = maker.lrucache(name='two')(_adder)
186+ three = maker.lrucache(name='three')(_adder)
187+ for i in range(100):
188+ _ = one(i)
189+ _ = two(i)
190+ _ = three(i)
191+ self.assertEqual(len(maker._cache['one'].data), 10)
192+ self.assertEqual(len(maker._cache['two'].data), 10)
193+ self.assertEqual(len(maker._cache['three'].data), 10)
194+ maker.clear('one', 'three')
195+ self.assertEqual(len(maker._cache['one'].data), 0)
196+ self.assertEqual(len(maker._cache['two'].data), 10)
197+ self.assertEqual(len(maker._cache['three'].data), 0)
198+
199+ def test_expiring(self):
200+ size = 10
201+ timeout = 10
202+ name = "name"
203+ cache = self._makeOne(maxsize=size, timeout=timeout)
204+ for i in range(100):
205+ if not i:
206+ decorated = cache.expiring_lrucache(name=name)(_adder)
207+ self.assertEqual( cache._cache[name].size,size)
208+ else:
209+ decorated = cache.expiring_lrucache()(_adder)
210+ decorated(10)
211+
212+ self.assertEqual( len(cache._cache) , 100)
213+ for _cache in cache._cache.values():
214+ self.assertEqual( _cache.size,size)
215+ self.assertEqual( _cache.default_timeout,timeout)
216+ self.assertEqual(len(_cache.data),1)
217+ ## and test clear cache
218+ cache.clear()
219+ for _cache in cache._cache.values():
220+ self.assertEqual( _cache.size,size)
221+ self.assertEqual(len(_cache.data),0)
222+
223+def _adder(x):
224+ return x + 10
225
226=== modified file 'CHANGES.txt'
227--- CHANGES.txt 2012-04-09 10:24:06 +0000
228+++ CHANGES.txt 2013-06-07 00:36:27 +0000
229@@ -1,8 +1,29 @@
230 Changelog
231 =========
232
233-After 0.5
234----------
235+0.6 (2012-07-12)
236+----------------
237+
238+- Added a 'CacheMaker' helper class: a maker keeps references (by name)
239+ to the caches it creates, to permit them to be cleared.
240+
241+- Added statistics to each cache, tracking lookups, hits, misses, and
242+ evictions.
243+
244+- Automated building Sphinx docs and testing example snippets under ``tox``.
245+
246+- Added Sphinx documentation.
247+
248+- Dropped support for Python 2.5.
249+
250+- Added support for PyPy.
251+
252+- Added ``setup.py docs`` alias (installs ``Sphinx`` and dependencies).
253+
254+- Added ``setup.py dev`` alias (runs ``develop`` plus installs ``nose``
255+ and ``coverage``).
256+
257+- Added support for CI under supported Pythons using ``tox``.
258
259 - Bug: Remove potential race condition on lock in face of interrupts
260 (Issue #10).
261
262=== modified file 'CONTRIBUTORS.txt'
263--- CONTRIBUTORS.txt 2011-09-20 11:37:18 +0000
264+++ CONTRIBUTORS.txt 2013-06-07 00:36:27 +0000
265@@ -105,3 +105,4 @@
266
267 - Tres Seaver, 2011/02/22
268 - Joel Bohman, 2011/08/16
269+- Julien Tayon, 2012/07/04
270
271=== modified file 'PKG-INFO'
272--- PKG-INFO 2012-04-09 10:24:06 +0000
273+++ PKG-INFO 2013-06-07 00:36:27 +0000
274@@ -1,6 +1,6 @@
275-Metadata-Version: 1.0
276+Metadata-Version: 1.1
277 Name: repoze.lru
278-Version: 0.5
279+Version: 0.6
280 Summary: A tiny LRU cache implementation and decorator
281 Home-page: http://www.repoze.org
282 Author: Agendaless Consulting
283@@ -14,49 +14,35 @@
284 than keys and values that are used frequently. It works under Python 2.5,
285 Python 2.6, Python 2.7, and Python 3.2.
286
287- API
288- ---
289-
290- Creating an LRUCache object::
291-
292- from repoze.lru import LRUCache
293- cache = LRUCache(100) # 100 max length
294-
295- Retrieving from an LRUCache object::
296-
297- cache.get('nonexisting', 'foo') # will return 'foo'
298- cache.get('nonexisting') # will return None
299- cache.get('existing') # will return the value for existing
300-
301- Adding to an LRUCache object::
302-
303- cache.put('key', 'value') # will add the key 'key' with the value 'value'
304-
305- Clearing an LRUCache::
306-
307- cache.clear()
308-
309- Decorator
310- ---------
311-
312- A ``lru_cache`` decorator exists. All values passed to the decorated
313- function must be hashable. It does not support keyword arguments::
314-
315- from repoze.lru import lru_cache
316-
317- @lru_cache(500)
318- def expensive_function(*arg):
319- pass
320-
321- Each function decorated with the lru_cache decorator uses its own
322- cache related to that function.
323+ Please see ``docs/index.rst`` for detailed documentation.
324
325
326 Changelog
327 =========
328
329- After 0.5
330- ---------
331+ 0.6 (2012-07-12)
332+ ----------------
333+
334+ - Added a 'CacheMaker' helper class: a maker keeps references (by name)
335+ to the caches it creates, to permit them to be cleared.
336+
337+ - Added statistics to each cache, tracking lookups, hits, misses, and
338+ evictions.
339+
340+ - Automated building Sphinx docs and testing example snippets under ``tox``.
341+
342+ - Added Sphinx documentation.
343+
344+ - Dropped support for Python 2.5.
345+
346+ - Added support for PyPy.
347+
348+ - Added ``setup.py docs`` alias (installs ``Sphinx`` and dependencies).
349+
350+ - Added ``setup.py dev`` alias (runs ``develop`` plus installs ``nose``
351+ and ``coverage``).
352+
353+ - Added support for CI under supported Pythons using ``tox``.
354
355 - Bug: Remove potential race condition on lock in face of interrupts
356 (Issue #10).
357@@ -108,7 +94,6 @@
358 Platform: UNKNOWN
359 Classifier: Intended Audience :: Developers
360 Classifier: Programming Language :: Python
361-Classifier: Programming Language :: Python :: 2.5
362 Classifier: Programming Language :: Python :: 2.6
363 Classifier: Programming Language :: Python :: 2.7
364 Classifier: Programming Language :: Python :: 3
365
366=== modified file 'README.txt'
367--- README.txt 2011-09-20 11:37:18 +0000
368+++ README.txt 2013-06-07 00:36:27 +0000
369@@ -6,39 +6,4 @@
370 than keys and values that are used frequently. It works under Python 2.5,
371 Python 2.6, Python 2.7, and Python 3.2.
372
373-API
374----
375-
376-Creating an LRUCache object::
377-
378- from repoze.lru import LRUCache
379- cache = LRUCache(100) # 100 max length
380-
381-Retrieving from an LRUCache object::
382-
383- cache.get('nonexisting', 'foo') # will return 'foo'
384- cache.get('nonexisting') # will return None
385- cache.get('existing') # will return the value for existing
386-
387-Adding to an LRUCache object::
388-
389- cache.put('key', 'value') # will add the key 'key' with the value 'value'
390-
391-Clearing an LRUCache::
392-
393- cache.clear()
394-
395-Decorator
396----------
397-
398-A ``lru_cache`` decorator exists. All values passed to the decorated
399-function must be hashable. It does not support keyword arguments::
400-
401- from repoze.lru import lru_cache
402-
403- @lru_cache(500)
404- def expensive_function(*arg):
405- pass
406-
407-Each function decorated with the lru_cache decorator uses its own
408-cache related to that function.
409+Please see ``docs/index.rst`` for detailed documentation.
410
411=== modified file 'debian/changelog'
412--- debian/changelog 2013-03-09 13:55:57 +0000
413+++ debian/changelog 2013-06-07 00:36:27 +0000
414@@ -1,7 +1,25 @@
415+python-repoze.lru (0.6-1ubuntu1) saucy; urgency=low
416+
417+ * Merge from Debian unstable. Remaining changes:
418+ - debian/rules: Run test during build.
419+ - debian/patches/fix-lp-tests.patch: Enable test during build.
420+
421+ -- Logan Rosen <logan@ubuntu.com> Thu, 06 Jun 2013 20:24:23 -0400
422+
423+python-repoze.lru (0.6-1) unstable; urgency=low
424+
425+ [ Jakub Wilk ]
426+ * Use canonical URIs for Vcs-* fields.
427+
428+ [ TANIGUCHI Takaki ]
429+ * New upstream release
430+
431+ -- TANIGUCHI Takaki <takaki@debian.org> Wed, 08 May 2013 21:01:13 +0900
432+
433 python-repoze.lru (0.5-2ubuntu1) raring; urgency=low
434
435 * Merge from Debian unstable. Remaining changes:
436- - debian/rules: Run test duing build.
437+ - debian/rules: Run test during build.
438 - debian/patches/fix-lp-tests.patch: Enable test during build.
439
440 -- Logan Rosen <logan@ubuntu.com> Sat, 09 Mar 2013 13:55:57 -0500
441
442=== modified file 'debian/control'
443--- debian/control 2012-05-17 14:04:22 +0000
444+++ debian/control 2013-06-07 00:36:27 +0000
445@@ -7,8 +7,8 @@
446 Build-Depends: debhelper (>= 7.0.50~), python-setuptools, python-all
447 Standards-Version: 3.9.3
448 Homepage: http://www.repoze.org/
449-Vcs-Svn: svn://svn.debian.org/python-modules/packages/python-repoze.lru/trunk/
450-Vcs-Browser: http://svn.debian.org/viewsvn/python-modules/packages/python-repoze.lru/trunk/
451+Vcs-Svn: svn://anonscm.debian.org/python-modules/packages/python-repoze.lru/trunk/
452+Vcs-Browser: http://anonscm.debian.org/viewvc/python-modules/packages/python-repoze.lru/trunk/
453
454 Package: python-repoze.lru
455 Architecture: all
456
457=== modified file 'debian/patches/fix-lp-tests.patch'
458--- debian/patches/fix-lp-tests.patch 2012-05-24 12:29:59 +0000
459+++ debian/patches/fix-lp-tests.patch 2013-06-07 00:36:27 +0000
460@@ -1,7 +1,6 @@
461-diff -Naurp repoze.lru-0.5.orig/repoze/lru/tests.py repoze.lru-0.5/repoze/lru/tests.py
462---- repoze.lru-0.5.orig/repoze/lru/tests.py 2012-03-24 13:38:58.000000000 -0400
463-+++ repoze.lru-0.5/repoze/lru/tests.py 2012-05-24 12:28:34.287214437 -0400
464-@@ -391,7 +391,7 @@ class ExpiringLRUCacheTests(LRUCacheTest
465+--- a/repoze/lru/tests.py
466++++ b/repoze/lru/tests.py
467+@@ -425,7 +425,7 @@
468
469 time.sleep(0.1)
470 cache.put("FOO", "BAR")
471@@ -10,7 +9,7 @@
472 self.assertEqual(cache.get("FOO"), "BAR")
473 self.check_cache_is_consistent(cache)
474
475-@@ -410,21 +410,21 @@ class ExpiringLRUCacheTests(LRUCacheTest
476+@@ -444,21 +444,21 @@
477
478 # Entry "one" must expire, "two"/"three" remain valid
479 time.sleep(0.1)
480@@ -42,7 +41,7 @@
481
482 self.check_cache_is_consistent(cache)
483
484-@@ -450,10 +450,10 @@ class ExpiringLRUCacheTests(LRUCacheTest
485+@@ -484,10 +484,10 @@
486
487 time.sleep(0.1)
488 # "foo2" must have expired
489@@ -55,9 +54,9 @@
490 + #self.assertEqual(cache.get("foo3"), "bar3")
491 + #self.check_cache_is_consistent(cache)
492
493+
494 class DecoratorTests(unittest.TestCase):
495- def _getTargetClass(self):
496-@@ -508,23 +508,23 @@ class DecoratorTests(unittest.TestCase):
497+@@ -544,23 +544,23 @@
498 start = time.time()
499 result1 = sleep_a_bit("hello")
500 stop = time.time()
501@@ -85,5 +84,5 @@
502 + #self.assertEqual(result3, 2 * "hello")
503 + #self.assertTrue(stop - start > 0.1)
504
505+
506 class DummyLRUCache(dict):
507- def put(self, k, v):
508
509=== added directory 'docs'
510=== added file 'docs/Makefile'
511--- docs/Makefile 1970-01-01 00:00:00 +0000
512+++ docs/Makefile 2013-06-07 00:36:27 +0000
513@@ -0,0 +1,153 @@
514+# Makefile for Sphinx documentation
515+#
516+
517+# You can set these variables from the command line.
518+SPHINXOPTS =
519+SPHINXBUILD = sphinx-build
520+PAPER =
521+BUILDDIR = _build
522+
523+# Internal variables.
524+PAPEROPT_a4 = -D latex_paper_size=a4
525+PAPEROPT_letter = -D latex_paper_size=letter
526+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
527+# the i18n builder cannot share the environment and doctrees with the others
528+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
529+
530+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
531+
532+help:
533+ @echo "Please use \`make <target>' where <target> is one of"
534+ @echo " html to make standalone HTML files"
535+ @echo " dirhtml to make HTML files named index.html in directories"
536+ @echo " singlehtml to make a single large HTML file"
537+ @echo " pickle to make pickle files"
538+ @echo " json to make JSON files"
539+ @echo " htmlhelp to make HTML files and a HTML help project"
540+ @echo " qthelp to make HTML files and a qthelp project"
541+ @echo " devhelp to make HTML files and a Devhelp project"
542+ @echo " epub to make an epub"
543+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
544+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
545+ @echo " text to make text files"
546+ @echo " man to make manual pages"
547+ @echo " texinfo to make Texinfo files"
548+ @echo " info to make Texinfo files and run them through makeinfo"
549+ @echo " gettext to make PO message catalogs"
550+ @echo " changes to make an overview of all changed/added/deprecated items"
551+ @echo " linkcheck to check all external links for integrity"
552+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
553+
554+clean:
555+ -rm -rf $(BUILDDIR)/*
556+
557+html:
558+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
559+ @echo
560+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
561+
562+dirhtml:
563+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
564+ @echo
565+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
566+
567+singlehtml:
568+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
569+ @echo
570+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
571+
572+pickle:
573+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
574+ @echo
575+ @echo "Build finished; now you can process the pickle files."
576+
577+json:
578+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
579+ @echo
580+ @echo "Build finished; now you can process the JSON files."
581+
582+htmlhelp:
583+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
584+ @echo
585+ @echo "Build finished; now you can run HTML Help Workshop with the" \
586+ ".hhp project file in $(BUILDDIR)/htmlhelp."
587+
588+qthelp:
589+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
590+ @echo
591+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
592+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
593+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/repozelru.qhcp"
594+ @echo "To view the help file:"
595+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/repozelru.qhc"
596+
597+devhelp:
598+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
599+ @echo
600+ @echo "Build finished."
601+ @echo "To view the help file:"
602+ @echo "# mkdir -p $$HOME/.local/share/devhelp/repozelru"
603+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/repozelru"
604+ @echo "# devhelp"
605+
606+epub:
607+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
608+ @echo
609+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
610+
611+latex:
612+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
613+ @echo
614+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
615+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
616+ "(use \`make latexpdf' here to do that automatically)."
617+
618+latexpdf:
619+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
620+ @echo "Running LaTeX files through pdflatex..."
621+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
622+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
623+
624+text:
625+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
626+ @echo
627+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
628+
629+man:
630+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
631+ @echo
632+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
633+
634+texinfo:
635+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
636+ @echo
637+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
638+ @echo "Run \`make' in that directory to run these through makeinfo" \
639+ "(use \`make info' here to do that automatically)."
640+
641+info:
642+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
643+ @echo "Running Texinfo files through makeinfo..."
644+ make -C $(BUILDDIR)/texinfo info
645+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
646+
647+gettext:
648+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
649+ @echo
650+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
651+
652+changes:
653+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
654+ @echo
655+ @echo "The overview file is in $(BUILDDIR)/changes."
656+
657+linkcheck:
658+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
659+ @echo
660+ @echo "Link check complete; look for any errors in the above output " \
661+ "or in $(BUILDDIR)/linkcheck/output.txt."
662+
663+doctest:
664+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
665+ @echo "Testing of doctests in the sources finished, look at the " \
666+ "results in $(BUILDDIR)/doctest/output.txt."
667
668=== added directory 'docs/_static'
669=== added file 'docs/_static/placeholder.txt'
670=== added directory 'docs/_templates'
671=== added file 'docs/_templates/placeholder.txt'
672=== added file 'docs/api.rst'
673--- docs/api.rst 1970-01-01 00:00:00 +0000
674+++ docs/api.rst 2013-06-07 00:36:27 +0000
675@@ -0,0 +1,23 @@
676+:mod:`repoze.lru` API
677+=====================
678+
679+Module: :mod:`repoze.lru`
680+--------------------------
681+
682+.. automodule:: repoze.lru
683+
684+ .. autoclass:: LRUCache
685+ :members:
686+ :member-order: bysource
687+
688+ .. autoclass:: ExpiringLRUCache
689+ :members:
690+ :member-order: bysource
691+
692+ .. autoclass:: lru_cache
693+ :members:
694+ :member-order: bysource
695+
696+ .. autoclass:: CacheMaker
697+ :members:
698+ :member-order: bysource
699
700=== added file 'docs/conf.py'
701--- docs/conf.py 1970-01-01 00:00:00 +0000
702+++ docs/conf.py 2013-06-07 00:36:27 +0000
703@@ -0,0 +1,242 @@
704+# -*- coding: utf-8 -*-
705+#
706+# repoze.lru documentation build configuration file, created by
707+# sphinx-quickstart on Mon Jun 11 16:50:52 2012.
708+#
709+# This file is execfile()d with the current directory set to its containing dir.
710+#
711+# Note that not all possible configuration values are present in this
712+# autogenerated file.
713+#
714+# All configuration values have a default; values that are commented out
715+# serve to show the default.
716+
717+import sys, os
718+
719+# If extensions (or modules to document with autodoc) are in another directory,
720+# add these directories to sys.path here. If the directory is relative to the
721+# documentation root, use os.path.abspath to make it absolute, like shown here.
722+#sys.path.insert(0, os.path.abspath('.'))
723+
724+# -- General configuration -----------------------------------------------------
725+
726+# If your documentation needs a minimal Sphinx version, state it here.
727+#needs_sphinx = '1.0'
728+
729+# Add any Sphinx extension module names here, as strings. They can be extensions
730+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
731+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode']
732+
733+# Add any paths that contain templates here, relative to this directory.
734+templates_path = ['_templates']
735+
736+# The suffix of source filenames.
737+source_suffix = '.rst'
738+
739+# The encoding of source files.
740+#source_encoding = 'utf-8-sig'
741+
742+# The master toctree document.
743+master_doc = 'index'
744+
745+# General information about the project.
746+project = u'repoze.lru'
747+copyright = u'2012, Repoze contributors'
748+
749+# The version info for the project you're documenting, acts as replacement for
750+# |version| and |release|, also used in various other places throughout the
751+# built documents.
752+#
753+# The short X.Y version.
754+version = '0.6'
755+# The full version, including alpha/beta/rc tags.
756+release = '0.6'
757+
758+# The language for content autogenerated by Sphinx. Refer to documentation
759+# for a list of supported languages.
760+#language = None
761+
762+# There are two options for replacing |today|: either, you set today to some
763+# non-false value, then it is used:
764+#today = ''
765+# Else, today_fmt is used as the format for a strftime call.
766+#today_fmt = '%B %d, %Y'
767+
768+# List of patterns, relative to source directory, that match files and
769+# directories to ignore when looking for source files.
770+exclude_patterns = ['_build']
771+
772+# The reST default role (used for this markup: `text`) to use for all documents.
773+#default_role = None
774+
775+# If true, '()' will be appended to :func: etc. cross-reference text.
776+#add_function_parentheses = True
777+
778+# If true, the current module name will be prepended to all description
779+# unit titles (such as .. function::).
780+#add_module_names = True
781+
782+# If true, sectionauthor and moduleauthor directives will be shown in the
783+# output. They are ignored by default.
784+#show_authors = False
785+
786+# The name of the Pygments (syntax highlighting) style to use.
787+pygments_style = 'sphinx'
788+
789+# A list of ignored prefixes for module index sorting.
790+#modindex_common_prefix = []
791+
792+
793+# -- Options for HTML output ---------------------------------------------------
794+
795+# The theme to use for HTML and HTML Help pages. See the documentation for
796+# a list of builtin themes.
797+html_theme = 'default'
798+
799+# Theme options are theme-specific and customize the look and feel of a theme
800+# further. For a list of options available for each theme, see the
801+# documentation.
802+#html_theme_options = {}
803+
804+# Add any paths that contain custom themes here, relative to this directory.
805+#html_theme_path = []
806+
807+# The name for this set of Sphinx documents. If None, it defaults to
808+# "<project> v<release> documentation".
809+#html_title = None
810+
811+# A shorter title for the navigation bar. Default is the same as html_title.
812+#html_short_title = None
813+
814+# The name of an image file (relative to this directory) to place at the top
815+# of the sidebar.
816+#html_logo = None
817+
818+# The name of an image file (within the static path) to use as favicon of the
819+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
820+# pixels large.
821+#html_favicon = None
822+
823+# Add any paths that contain custom static files (such as style sheets) here,
824+# relative to this directory. They are copied after the builtin static files,
825+# so a file named "default.css" will overwrite the builtin "default.css".
826+html_static_path = ['_static']
827+
828+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
829+# using the given strftime format.
830+#html_last_updated_fmt = '%b %d, %Y'
831+
832+# If true, SmartyPants will be used to convert quotes and dashes to
833+# typographically correct entities.
834+#html_use_smartypants = True
835+
836+# Custom sidebar templates, maps document names to template names.
837+#html_sidebars = {}
838+
839+# Additional templates that should be rendered to pages, maps page names to
840+# template names.
841+#html_additional_pages = {}
842+
843+# If false, no module index is generated.
844+#html_domain_indices = True
845+
846+# If false, no index is generated.
847+#html_use_index = True
848+
849+# If true, the index is split into individual pages for each letter.
850+#html_split_index = False
851+
852+# If true, links to the reST sources are added to the pages.
853+#html_show_sourcelink = True
854+
855+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
856+#html_show_sphinx = True
857+
858+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
859+#html_show_copyright = True
860+
861+# If true, an OpenSearch description file will be output, and all pages will
862+# contain a <link> tag referring to it. The value of this option must be the
863+# base URL from which the finished HTML is served.
864+#html_use_opensearch = ''
865+
866+# This is the file name suffix for HTML files (e.g. ".xhtml").
867+#html_file_suffix = None
868+
869+# Output file base name for HTML help builder.
870+htmlhelp_basename = 'repozelrudoc'
871+
872+
873+# -- Options for LaTeX output --------------------------------------------------
874+
875+latex_elements = {
876+# The paper size ('letterpaper' or 'a4paper').
877+#'papersize': 'letterpaper',
878+
879+# The font size ('10pt', '11pt' or '12pt').
880+#'pointsize': '10pt',
881+
882+# Additional stuff for the LaTeX preamble.
883+#'preamble': '',
884+}
885+
886+# Grouping the document tree into LaTeX files. List of tuples
887+# (source start file, target name, title, author, documentclass [howto/manual]).
888+latex_documents = [
889+ ('index', 'repozelru.tex', u'repoze.lru Documentation',
890+ u'Repoze contributors', 'manual'),
891+]
892+
893+# The name of an image file (relative to this directory) to place at the top of
894+# the title page.
895+#latex_logo = None
896+
897+# For "manual" documents, if this is true, then toplevel headings are parts,
898+# not chapters.
899+#latex_use_parts = False
900+
901+# If true, show page references after internal links.
902+#latex_show_pagerefs = False
903+
904+# If true, show URL addresses after external links.
905+#latex_show_urls = False
906+
907+# Documents to append as an appendix to all manuals.
908+#latex_appendices = []
909+
910+# If false, no module index is generated.
911+#latex_domain_indices = True
912+
913+
914+# -- Options for manual page output --------------------------------------------
915+
916+# One entry per manual page. List of tuples
917+# (source start file, name, description, authors, manual section).
918+man_pages = [
919+ ('index', 'repozelru', u'repoze.lru Documentation',
920+ [u'Repoze contributors'], 1)
921+]
922+
923+# If true, show URL addresses after external links.
924+#man_show_urls = False
925+
926+
927+# -- Options for Texinfo output ------------------------------------------------
928+
929+# Grouping the document tree into Texinfo files. List of tuples
930+# (source start file, target name, title, author,
931+# dir menu entry, description, category)
932+texinfo_documents = [
933+ ('index', 'repozelru', u'repoze.lru Documentation',
934+ u'Repoze contributors', 'repozelru', 'One line description of project.',
935+ 'Miscellaneous'),
936+]
937+
938+# Documents to append as an appendix to all manuals.
939+#texinfo_appendices = []
940+
941+# If false, no module index is generated.
942+#texinfo_domain_indices = True
943+
944+# How to display URL addresses: 'footnote', 'no', or 'inline'.
945+#texinfo_show_urls = 'footnote'
946
947=== added file 'docs/index.rst'
948--- docs/index.rst 1970-01-01 00:00:00 +0000
949+++ docs/index.rst 2013-06-07 00:36:27 +0000
950@@ -0,0 +1,19 @@
951+:mod:`repoze.lru`
952+=================
953+
954+Contents:
955+
956+.. toctree::
957+ :maxdepth: 2
958+
959+ narr
960+ api
961+
962+
963+Indices and tables
964+==================
965+
966+* :ref:`genindex`
967+* :ref:`modindex`
968+* :ref:`search`
969+
970
971=== added file 'docs/make.bat'
972--- docs/make.bat 1970-01-01 00:00:00 +0000
973+++ docs/make.bat 2013-06-07 00:36:27 +0000
974@@ -0,0 +1,190 @@
975+@ECHO OFF
976+
977+REM Command file for Sphinx documentation
978+
979+if "%SPHINXBUILD%" == "" (
980+ set SPHINXBUILD=sphinx-build
981+)
982+set BUILDDIR=_build
983+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
984+set I18NSPHINXOPTS=%SPHINXOPTS% .
985+if NOT "%PAPER%" == "" (
986+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
987+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
988+)
989+
990+if "%1" == "" goto help
991+
992+if "%1" == "help" (
993+ :help
994+ echo.Please use `make ^<target^>` where ^<target^> is one of
995+ echo. html to make standalone HTML files
996+ echo. dirhtml to make HTML files named index.html in directories
997+ echo. singlehtml to make a single large HTML file
998+ echo. pickle to make pickle files
999+ echo. json to make JSON files
1000+ echo. htmlhelp to make HTML files and a HTML help project
1001+ echo. qthelp to make HTML files and a qthelp project
1002+ echo. devhelp to make HTML files and a Devhelp project
1003+ echo. epub to make an epub
1004+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
1005+ echo. text to make text files
1006+ echo. man to make manual pages
1007+ echo. texinfo to make Texinfo files
1008+ echo. gettext to make PO message catalogs
1009+ echo. changes to make an overview over all changed/added/deprecated items
1010+ echo. linkcheck to check all external links for integrity
1011+ echo. doctest to run all doctests embedded in the documentation if enabled
1012+ goto end
1013+)
1014+
1015+if "%1" == "clean" (
1016+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
1017+ del /q /s %BUILDDIR%\*
1018+ goto end
1019+)
1020+
1021+if "%1" == "html" (
1022+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
1023+ if errorlevel 1 exit /b 1
1024+ echo.
1025+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
1026+ goto end
1027+)
1028+
1029+if "%1" == "dirhtml" (
1030+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
1031+ if errorlevel 1 exit /b 1
1032+ echo.
1033+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
1034+ goto end
1035+)
1036+
1037+if "%1" == "singlehtml" (
1038+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
1039+ if errorlevel 1 exit /b 1
1040+ echo.
1041+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
1042+ goto end
1043+)
1044+
1045+if "%1" == "pickle" (
1046+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
1047+ if errorlevel 1 exit /b 1
1048+ echo.
1049+ echo.Build finished; now you can process the pickle files.
1050+ goto end
1051+)
1052+
1053+if "%1" == "json" (
1054+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
1055+ if errorlevel 1 exit /b 1
1056+ echo.
1057+ echo.Build finished; now you can process the JSON files.
1058+ goto end
1059+)
1060+
1061+if "%1" == "htmlhelp" (
1062+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
1063+ if errorlevel 1 exit /b 1
1064+ echo.
1065+ echo.Build finished; now you can run HTML Help Workshop with the ^
1066+.hhp project file in %BUILDDIR%/htmlhelp.
1067+ goto end
1068+)
1069+
1070+if "%1" == "qthelp" (
1071+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
1072+ if errorlevel 1 exit /b 1
1073+ echo.
1074+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
1075+.qhcp project file in %BUILDDIR%/qthelp, like this:
1076+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\repozelru.qhcp
1077+ echo.To view the help file:
1078+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\repozelru.ghc
1079+ goto end
1080+)
1081+
1082+if "%1" == "devhelp" (
1083+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
1084+ if errorlevel 1 exit /b 1
1085+ echo.
1086+ echo.Build finished.
1087+ goto end
1088+)
1089+
1090+if "%1" == "epub" (
1091+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
1092+ if errorlevel 1 exit /b 1
1093+ echo.
1094+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
1095+ goto end
1096+)
1097+
1098+if "%1" == "latex" (
1099+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
1100+ if errorlevel 1 exit /b 1
1101+ echo.
1102+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
1103+ goto end
1104+)
1105+
1106+if "%1" == "text" (
1107+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
1108+ if errorlevel 1 exit /b 1
1109+ echo.
1110+ echo.Build finished. The text files are in %BUILDDIR%/text.
1111+ goto end
1112+)
1113+
1114+if "%1" == "man" (
1115+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
1116+ if errorlevel 1 exit /b 1
1117+ echo.
1118+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
1119+ goto end
1120+)
1121+
1122+if "%1" == "texinfo" (
1123+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
1124+ if errorlevel 1 exit /b 1
1125+ echo.
1126+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
1127+ goto end
1128+)
1129+
1130+if "%1" == "gettext" (
1131+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
1132+ if errorlevel 1 exit /b 1
1133+ echo.
1134+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
1135+ goto end
1136+)
1137+
1138+if "%1" == "changes" (
1139+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
1140+ if errorlevel 1 exit /b 1
1141+ echo.
1142+ echo.The overview file is in %BUILDDIR%/changes.
1143+ goto end
1144+)
1145+
1146+if "%1" == "linkcheck" (
1147+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
1148+ if errorlevel 1 exit /b 1
1149+ echo.
1150+ echo.Link check complete; look for any errors in the above output ^
1151+or in %BUILDDIR%/linkcheck/output.txt.
1152+ goto end
1153+)
1154+
1155+if "%1" == "doctest" (
1156+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
1157+ if errorlevel 1 exit /b 1
1158+ echo.
1159+ echo.Testing of doctests in the sources finished, look at the ^
1160+results in %BUILDDIR%/doctest/output.txt.
1161+ goto end
1162+)
1163+
1164+:end
1165
1166=== added file 'docs/narr.rst'
1167--- docs/narr.rst 1970-01-01 00:00:00 +0000
1168+++ docs/narr.rst 2013-06-07 00:36:27 +0000
1169@@ -0,0 +1,95 @@
1170+Using :mod:`repoze.lru`
1171+======================
1172+
1173+``repoze.lru`` is a LRU (least recently used) cache implementation. Keys and
1174+values that are not used frequently will be evicted from the cache faster
1175+than keys and values that are used frequently. It works under Python 2.5,
1176+Python 2.6, Python 2.7, and Python 3.2.
1177+
1178+Using the API programmatically
1179+------------------------------
1180+
1181+Creating an LRUCache object:
1182+
1183+.. doctest::
1184+
1185+ >>> from repoze.lru import LRUCache
1186+ >>> cache = LRUCache(100) # 100 max length
1187+
1188+Retrieving from an LRUCache object:
1189+
1190+.. doctest::
1191+
1192+ >>> cache.get('nonexisting', 'foo') # return 'foo'
1193+ 'foo'
1194+ >>> cache.get('nonexisting') is None
1195+ True
1196+
1197+Adding to an LRUCache object:
1198+
1199+.. doctest::
1200+
1201+ >>> cache.put('existing', 'value') # add the key 'key' with the value 'value'
1202+ >>> cache.get('existing') # return the value for existing
1203+ 'value'
1204+
1205+Clearing an LRUCache:
1206+
1207+.. doctest::
1208+
1209+ >>> cache.clear()
1210+
1211+Each LRU cache tracks some basic statistics via attributes:
1212+
1213+ cache.lookups # number of calls to the get method
1214+ cache.hits # number of times a call to get found an object
1215+ cache.misses # number of times a call to get did not find an object
1216+ cahce.evictions # number of times a object was evicted from cache
1217+
1218+
1219+Decorating an "expensive" function call
1220+---------------------------------------
1221+
1222+:mod:`repoze.lru` provides a class :class:`~repoze.lru.lru_cache`, which
1223+wrapps another callable, caching the results. All values passed to the
1224+decorated function must be hashable. It does not support keyword arguments:
1225+
1226+.. doctest::
1227+
1228+ >>> from repoze.lru import lru_cache
1229+ >>> @lru_cache(500)
1230+ ... def expensive_function(*arg): #*
1231+ ... pass
1232+
1233+Each function decorated with the lru_cache decorator uses its own
1234+cache related to that function.
1235+
1236+Cleaning cache of decorated function
1237+------------------------------------
1238+
1239+:mod:`repoze.lru` provides a :class:`~repoze.lru.CacheMaker`, which generates
1240+decorators. This way, you can later clear your cache if needed.
1241+
1242+.. doctest::
1243+
1244+ >>> from repoze.lru import CacheMaker
1245+ >>> cache_maker=CacheMaker()
1246+ >>> @cache_maker.lrucache(maxsize=300, name="adder")
1247+ ... def yet_another_exepensive_function(*arg):#*
1248+ ... pass
1249+
1250+ >>> @cache_maker.expiring_lrucache(maxsize=300,timeout=30)
1251+ ... def another_exepensive_function(*arg):#*
1252+ ... pass
1253+
1254+This way, when you need it you can choose to either clear all cache:
1255+
1256+.. doctest::
1257+
1258+ >>> cache_maker.clear()
1259+
1260+or clear a specific cache
1261+
1262+.. doctest::
1263+
1264+ >>> cache_maker.clear("adder")
1265
1266=== modified file 'repoze.lru.egg-info/PKG-INFO'
1267--- repoze.lru.egg-info/PKG-INFO 2012-04-09 10:24:06 +0000
1268+++ repoze.lru.egg-info/PKG-INFO 2013-06-07 00:36:27 +0000
1269@@ -1,6 +1,6 @@
1270-Metadata-Version: 1.0
1271+Metadata-Version: 1.1
1272 Name: repoze.lru
1273-Version: 0.5
1274+Version: 0.6
1275 Summary: A tiny LRU cache implementation and decorator
1276 Home-page: http://www.repoze.org
1277 Author: Agendaless Consulting
1278@@ -14,49 +14,35 @@
1279 than keys and values that are used frequently. It works under Python 2.5,
1280 Python 2.6, Python 2.7, and Python 3.2.
1281
1282- API
1283- ---
1284-
1285- Creating an LRUCache object::
1286-
1287- from repoze.lru import LRUCache
1288- cache = LRUCache(100) # 100 max length
1289-
1290- Retrieving from an LRUCache object::
1291-
1292- cache.get('nonexisting', 'foo') # will return 'foo'
1293- cache.get('nonexisting') # will return None
1294- cache.get('existing') # will return the value for existing
1295-
1296- Adding to an LRUCache object::
1297-
1298- cache.put('key', 'value') # will add the key 'key' with the value 'value'
1299-
1300- Clearing an LRUCache::
1301-
1302- cache.clear()
1303-
1304- Decorator
1305- ---------
1306-
1307- A ``lru_cache`` decorator exists. All values passed to the decorated
1308- function must be hashable. It does not support keyword arguments::
1309-
1310- from repoze.lru import lru_cache
1311-
1312- @lru_cache(500)
1313- def expensive_function(*arg):
1314- pass
1315-
1316- Each function decorated with the lru_cache decorator uses its own
1317- cache related to that function.
1318+ Please see ``docs/index.rst`` for detailed documentation.
1319
1320
1321 Changelog
1322 =========
1323
1324- After 0.5
1325- ---------
1326+ 0.6 (2012-07-12)
1327+ ----------------
1328+
1329+ - Added a 'CacheMaker' helper class: a maker keeps references (by name)
1330+ to the caches it creates, to permit them to be cleared.
1331+
1332+ - Added statistics to each cache, tracking lookups, hits, misses, and
1333+ evictions.
1334+
1335+ - Automated building Sphinx docs and testing example snippets under ``tox``.
1336+
1337+ - Added Sphinx documentation.
1338+
1339+ - Dropped support for Python 2.5.
1340+
1341+ - Added support for PyPy.
1342+
1343+ - Added ``setup.py docs`` alias (installs ``Sphinx`` and dependencies).
1344+
1345+ - Added ``setup.py dev`` alias (runs ``develop`` plus installs ``nose``
1346+ and ``coverage``).
1347+
1348+ - Added support for CI under supported Pythons using ``tox``.
1349
1350 - Bug: Remove potential race condition on lock in face of interrupts
1351 (Issue #10).
1352@@ -108,7 +94,6 @@
1353 Platform: UNKNOWN
1354 Classifier: Intended Audience :: Developers
1355 Classifier: Programming Language :: Python
1356-Classifier: Programming Language :: Python :: 2.5
1357 Classifier: Programming Language :: Python :: 2.6
1358 Classifier: Programming Language :: Python :: 2.7
1359 Classifier: Programming Language :: Python :: 3
1360
1361=== modified file 'repoze.lru.egg-info/SOURCES.txt'
1362--- repoze.lru.egg-info/SOURCES.txt 2011-09-20 11:37:18 +0000
1363+++ repoze.lru.egg-info/SOURCES.txt 2013-06-07 00:36:27 +0000
1364@@ -7,6 +7,14 @@
1365 setup.cfg
1366 setup.py
1367 tox.ini
1368+docs/Makefile
1369+docs/api.rst
1370+docs/conf.py
1371+docs/index.rst
1372+docs/make.bat
1373+docs/narr.rst
1374+docs/_static/placeholder.txt
1375+docs/_templates/placeholder.txt
1376 repoze/__init__.py
1377 repoze.lru.egg-info/PKG-INFO
1378 repoze.lru.egg-info/SOURCES.txt
1379@@ -14,6 +22,7 @@
1380 repoze.lru.egg-info/entry_points.txt
1381 repoze.lru.egg-info/namespace_packages.txt
1382 repoze.lru.egg-info/not-zip-safe
1383+repoze.lru.egg-info/requires.txt
1384 repoze.lru.egg-info/top_level.txt
1385 repoze/lru/__init__.py
1386 repoze/lru/tests.py
1387\ No newline at end of file
1388
1389=== added file 'repoze.lru.egg-info/requires.txt'
1390--- repoze.lru.egg-info/requires.txt 1970-01-01 00:00:00 +0000
1391+++ repoze.lru.egg-info/requires.txt 2013-06-07 00:36:27 +0000
1392@@ -0,0 +1,8 @@
1393+
1394+
1395+[docs]
1396+Sphinx
1397+
1398+[testing]
1399+nose
1400+coverage
1401\ No newline at end of file
1402
1403=== modified file 'repoze/lru/__init__.py'
1404--- repoze/lru/__init__.py 2012-04-09 10:24:06 +0000
1405+++ repoze/lru/__init__.py 2013-06-07 00:36:27 +0000
1406@@ -3,17 +3,15 @@
1407
1408 import threading
1409 import time
1410+import uuid
1411
1412-try:
1413- range = xrange
1414-except NameError: # pragma: no cover
1415- pass
1416
1417 _MARKER = object()
1418 # By default, expire items after 2**60 seconds. This fits into 64 bit
1419 # integers and is close enough to "never" for practical purposes.
1420 _DEFAULT_TIMEOUT = 2 ** 60
1421
1422+
1423 class LRUCache(object):
1424 """ Implements a pseudo-LRU algorithm (CLOCK)
1425
1426@@ -31,6 +29,10 @@
1427 self.clock_keys = None
1428 self.clock_refs = None
1429 self.data = None
1430+ self.evictions = 0
1431+ self.hits = 0
1432+ self.misses = 0
1433+ self.lookups = 0
1434 self.clear()
1435
1436 def clear(self):
1437@@ -47,12 +49,19 @@
1438 self.clock_keys = [_MARKER] * size
1439 self.clock_refs = [False] * size
1440 self.hand = 0
1441+ self.evictions = 0
1442+ self.hits = 0
1443+ self.misses = 0
1444+ self.lookups = 0
1445
1446 def get(self, key, default=None):
1447 """Return value for key. If not in cache, return default"""
1448+ self.lookups += 1
1449 try:
1450 pos, val = self.data[key]
1451+ self.hits += 1
1452 except KeyError:
1453+ self.misses += 1
1454 return default
1455 self.clock_refs[pos] = True
1456 return val
1457@@ -98,7 +107,9 @@
1458 # Maybe oldkey was not in self.data to begin with. If it
1459 # was, self.invalidate() in another thread might have
1460 # already removed it. del() would raise KeyError, so pop().
1461- data.pop(oldkey, None)
1462+ oldentry = data.pop(oldkey, _MARKER)
1463+ if oldentry is not _MARKER:
1464+ self.evictions += 1
1465 clock_keys[hand] = key
1466 clock_refs[hand] = True
1467 data[key] = (hand, val)
1468@@ -137,6 +148,10 @@
1469 self.clock_keys = None
1470 self.clock_refs = None
1471 self.data = None
1472+ self.evictions = 0
1473+ self.hits = 0
1474+ self.misses = 0
1475+ self.lookups = 0
1476 self.clear()
1477
1478 def clear(self):
1479@@ -154,20 +169,28 @@
1480 self.clock_keys = [_MARKER] * size
1481 self.clock_refs = [False] * size
1482 self.hand = 0
1483+ self.evictions = 0
1484+ self.hits = 0
1485+ self.misses = 0
1486+ self.lookups = 0
1487
1488 def get(self, key, default=None):
1489 """Return value for key. If not in cache or expired, return default"""
1490+ self.lookups += 1
1491 try:
1492 pos, val, expires = self.data[key]
1493 except KeyError:
1494+ self.misses += 1
1495 return default
1496 if expires > time.time():
1497 # cache entry still valid
1498+ self.hits += 1
1499 self.clock_refs[pos] = True
1500 return val
1501 else:
1502 # cache entry has expired. Make sure the space in the cache can
1503 # be recycled soon.
1504+ self.misses += 1
1505 self.clock_refs[pos] = False
1506 return default
1507
1508@@ -218,7 +241,9 @@
1509 # Maybe oldkey was not in self.data to begin with. If it
1510 # was, self.invalidate() in another thread might have
1511 # already removed it. del() would raise KeyError, so pop().
1512- data.pop(oldkey, None)
1513+ oldentry = data.pop(oldkey, _MARKER)
1514+ if oldentry is not _MARKER:
1515+ self.evictions += 1
1516 clock_keys[hand] = key
1517 clock_refs[hand] = True
1518 data[key] = (hand, val, time.time() + timeout)
1519@@ -238,6 +263,7 @@
1520 self.clock_refs[entry[0]] = False
1521 # else: key was not in cache. Nothing to do.
1522
1523+
1524 class lru_cache(object):
1525 """ Decorator for LRU-cached function
1526
1527@@ -265,3 +291,78 @@
1528 lru_cached.__name__ = f.__name__
1529 lru_cached.__doc__ = f.__doc__
1530 return lru_cached
1531+
1532+
1533+class CacheMaker(object):
1534+ """Generates decorators that can be cleared later
1535+ """
1536+ def __init__(self, maxsize=None, timeout=_DEFAULT_TIMEOUT):
1537+ """Create cache decorator factory.
1538+
1539+ - maxsize : the default size for created caches.
1540+
1541+ - timeout : the defaut expiraiton time for created caches.
1542+ """
1543+ self._maxsize = maxsize
1544+ self._timeout = timeout
1545+ self._cache = {}
1546+
1547+ def _resolve_setting(self, name=None, maxsize=None, timeout=None):
1548+ if name is None:
1549+ while True:
1550+ name = str(uuid.uuid4())
1551+ ## the probability of collision is so low ....
1552+ if name not in self._cache:
1553+ break
1554+
1555+ if name in self._cache:
1556+ raise KeyError("cache %s already in use" % name)
1557+
1558+ if maxsize is None:
1559+ maxsize = self._maxsize
1560+
1561+ if maxsize is None:
1562+ raise ValueError("Cache must have a maxsize set")
1563+
1564+ if timeout is None:
1565+ timeout = self._timeout
1566+
1567+ return name, maxsize, timeout
1568+
1569+ def lrucache(self, name=None, maxsize=None):
1570+ """Named arguments:
1571+
1572+ - name (optional) is a string, and should be unique amongst all caches
1573+
1574+ - maxsize (optional) is an int, overriding any default value set by
1575+ the constructor
1576+ """
1577+ name, maxsize, _ = self._resolve_setting(name, maxsize)
1578+ cache = self._cache[name] = LRUCache(maxsize)
1579+ return lru_cache(maxsize, cache)
1580+
1581+ def expiring_lrucache(self, name=None, maxsize=None, timeout=None):
1582+ """Named arguments:
1583+
1584+ - name (optional) is a string, and should be unique amongst all caches
1585+
1586+ - maxsize (optional) is an int, overriding any default value set by
1587+ the constructor
1588+
1589+ - timeout (optional) is an int, overriding any default value set by
1590+ the constructor or the default value (%d seconds)
1591+ """ % _DEFAULT_TIMEOUT
1592+ name, maxsize, timeout = self._resolve_setting(name, maxsize, timeout)
1593+ cache = self._cache[name] = ExpiringLRUCache(maxsize, timeout)
1594+ return lru_cache(maxsize, cache, timeout)
1595+
1596+ def clear(self, *names):
1597+ """Clear the given cache(s).
1598+
1599+ If no 'names' are passed, clear all caches.
1600+ """
1601+ if len(names) == 0:
1602+ names = self._cache.keys()
1603+
1604+ for name in names:
1605+ self._cache[name].clear()
1606
1607=== modified file 'repoze/lru/tests.py'
1608--- repoze/lru/tests.py 2012-05-24 12:29:59 +0000
1609+++ repoze/lru/tests.py 2013-06-07 00:36:27 +0000
1610@@ -1,14 +1,15 @@
1611-#!/usr/bin/python -tt
1612 import random
1613 import time
1614 import unittest
1615
1616 try:
1617 range = xrange
1618-except NameError: # pragma: no cover
1619+except NameError: # pragma: NO COVER (Python3)
1620 pass
1621
1622+
1623 class LRUCacheTests(unittest.TestCase):
1624+
1625 def _getTargetClass(self):
1626 from repoze.lru import LRUCache
1627 return LRUCache
1628@@ -23,7 +24,7 @@
1629
1630 # lengths of data structures
1631 self.assertEqual(len(cache.clock_keys), len(cache.clock_refs))
1632- self.assertTrue(len(cache.data) <=len(cache.clock_refs))
1633+ self.assertTrue(len(cache.data) <= len(cache.clock_refs))
1634
1635 # For each item in cache.data
1636 # 1. pos must be a valid index
1637@@ -181,6 +182,9 @@
1638 else:
1639 cache.put(item, "item%s" % item)
1640
1641+ self.assertEqual(cache.misses, 0)
1642+ self.assertEqual(cache.evictions, 0)
1643+
1644 self.check_cache_is_consistent(cache)
1645
1646 def test_imperfect_hitrate(self):
1647@@ -214,8 +218,36 @@
1648 self.assertTrue(hit_ratio > 45)
1649 self.assertTrue(hit_ratio < 55)
1650
1651+ # The internal cache counters should have the same information
1652+ internal_hit_ratio = 100 * cache.hits / cache.lookups
1653+ self.assertTrue(internal_hit_ratio > 45)
1654+ self.assertTrue(internal_hit_ratio < 55)
1655+
1656+ # The internal miss counters should also be around 50%
1657+ internal_miss_ratio = 100 * cache.misses / cache.lookups
1658+ self.assertTrue(internal_miss_ratio > 45)
1659+ self.assertTrue(internal_miss_ratio < 55)
1660+
1661 self.check_cache_is_consistent(cache)
1662
1663+ def test_eviction_counter(self):
1664+ cache = self._makeOne(2)
1665+ cache.put(1, 1)
1666+ cache.put(2, 1)
1667+ self.assertEqual(cache.evictions, 0)
1668+
1669+ cache.put(3, 1)
1670+ cache.put(4, 1)
1671+ self.assertEqual(cache.evictions, 2)
1672+
1673+ cache.put(3, 1)
1674+ cache.put(4, 1)
1675+ self.assertEqual(cache.evictions, 2)
1676+
1677+ cache.clear()
1678+ self.assertEqual(cache.evictions, 0)
1679+
1680+
1681 def test_it(self):
1682 cache = self._makeOne(3)
1683 self.assertEqual(cache.get('a'), None)
1684@@ -271,6 +303,7 @@
1685
1686
1687 class ExpiringLRUCacheTests(LRUCacheTests):
1688+
1689 def _getTargetClass(self):
1690 from repoze.lru import ExpiringLRUCache
1691 return ExpiringLRUCache
1692@@ -316,6 +349,7 @@
1693 # All clock_refs must be True or False, nothing else.
1694 for clock_ref in cache.clock_refs:
1695 self.assertTrue(clock_ref is True or clock_ref is False)
1696+
1697 def test_it(self):
1698 """Test a sequence of operations
1699
1700@@ -455,7 +489,9 @@
1701 #self.assertEqual(cache.get("foo3"), "bar3")
1702 #self.check_cache_is_consistent(cache)
1703
1704+
1705 class DecoratorTests(unittest.TestCase):
1706+
1707 def _getTargetClass(self):
1708 from repoze.lru import lru_cache
1709 return lru_cache
1710@@ -526,6 +562,111 @@
1711 #self.assertEqual(result3, 2 * "hello")
1712 #self.assertTrue(stop - start > 0.1)
1713
1714+
1715 class DummyLRUCache(dict):
1716+
1717 def put(self, k, v):
1718 return self.__setitem__(k, v)
1719+
1720+
1721+class CacherMaker(unittest.TestCase):
1722+
1723+ def _getTargetClass(self):
1724+ from repoze.lru import CacheMaker
1725+ return CacheMaker
1726+
1727+ def _makeOne(self, *args, **kw):
1728+ return self._getTargetClass()(*args, **kw)
1729+
1730+ def test_named_cache(self):
1731+ maker = self._makeOne()
1732+ size = 10
1733+ name = "name"
1734+ decorated = maker.lrucache(maxsize=size, name=name)(_adder)
1735+ self.assertEqual(list(maker._cache.keys()), [name])
1736+ self.assertEqual(maker._cache[name].size, size)
1737+ decorated(10)
1738+ decorated(11)
1739+ self.assertEqual(len(maker._cache[name].data),2)
1740+
1741+ def test_exception(self):
1742+ maker = self._makeOne()
1743+ size = 10
1744+ name = "name"
1745+ decorated = maker.lrucache(maxsize=size, name=name)(_adder)
1746+ self.assertRaises(KeyError, maker.lrucache, maxsize=size, name=name)
1747+ self.assertRaises(ValueError, maker.lrucache)
1748+
1749+ def test_defaultvalue_and_clear(self):
1750+ size = 10
1751+ maker = self._makeOne(maxsize=size)
1752+ for i in range(100):
1753+ decorated = maker.lrucache()(_adder)
1754+ decorated(10)
1755+
1756+ self.assertEqual(len(maker._cache) , 100)
1757+ for _cache in maker._cache.values():
1758+ self.assertEqual( _cache.size,size)
1759+ self.assertEqual(len(_cache.data),1)
1760+ ## and test clear cache
1761+ maker.clear()
1762+ for _cache in maker._cache.values():
1763+ self.assertEqual( _cache.size,size)
1764+ self.assertEqual(len(_cache.data),0)
1765+
1766+ def test_clear_with_single_name(self):
1767+ maker = self._makeOne(maxsize=10)
1768+ one = maker.lrucache(name='one')(_adder)
1769+ two = maker.lrucache(name='two')(_adder)
1770+ for i in range(100):
1771+ _ = one(i)
1772+ _ = two(i)
1773+ self.assertEqual(len(maker._cache['one'].data), 10)
1774+ self.assertEqual(len(maker._cache['two'].data), 10)
1775+ maker.clear('one')
1776+ self.assertEqual(len(maker._cache['one'].data), 0)
1777+ self.assertEqual(len(maker._cache['two'].data), 10)
1778+
1779+ def test_clear_with_multiple_names(self):
1780+ maker = self._makeOne(maxsize=10)
1781+ one = maker.lrucache(name='one')(_adder)
1782+ two = maker.lrucache(name='two')(_adder)
1783+ three = maker.lrucache(name='three')(_adder)
1784+ for i in range(100):
1785+ _ = one(i)
1786+ _ = two(i)
1787+ _ = three(i)
1788+ self.assertEqual(len(maker._cache['one'].data), 10)
1789+ self.assertEqual(len(maker._cache['two'].data), 10)
1790+ self.assertEqual(len(maker._cache['three'].data), 10)
1791+ maker.clear('one', 'three')
1792+ self.assertEqual(len(maker._cache['one'].data), 0)
1793+ self.assertEqual(len(maker._cache['two'].data), 10)
1794+ self.assertEqual(len(maker._cache['three'].data), 0)
1795+
1796+ def test_expiring(self):
1797+ size = 10
1798+ timeout = 10
1799+ name = "name"
1800+ cache = self._makeOne(maxsize=size, timeout=timeout)
1801+ for i in range(100):
1802+ if not i:
1803+ decorated = cache.expiring_lrucache(name=name)(_adder)
1804+ self.assertEqual( cache._cache[name].size,size)
1805+ else:
1806+ decorated = cache.expiring_lrucache()(_adder)
1807+ decorated(10)
1808+
1809+ self.assertEqual( len(cache._cache) , 100)
1810+ for _cache in cache._cache.values():
1811+ self.assertEqual( _cache.size,size)
1812+ self.assertEqual( _cache.default_timeout,timeout)
1813+ self.assertEqual(len(_cache.data),1)
1814+ ## and test clear cache
1815+ cache.clear()
1816+ for _cache in cache._cache.values():
1817+ self.assertEqual( _cache.size,size)
1818+ self.assertEqual(len(_cache.data),0)
1819+
1820+def _adder(x):
1821+ return x + 10
1822
1823=== modified file 'setup.cfg'
1824--- setup.cfg 2012-04-09 10:24:06 +0000
1825+++ setup.cfg 2013-06-07 00:36:27 +0000
1826@@ -8,6 +8,10 @@
1827 cover-package = repoze.lru
1828 cover-erase = 1
1829
1830+[aliases]
1831+dev = develop easy_install repoze.lru[testing]
1832+docs = develop easy_install repoze.lru[docs]
1833+
1834 [egg_info]
1835 tag_build =
1836 tag_date = 0
1837
1838=== modified file 'setup.py'
1839--- setup.py 2012-04-09 10:24:06 +0000
1840+++ setup.py 2013-06-07 00:36:27 +0000
1841@@ -24,14 +24,15 @@
1842 README = ''
1843 CHANGES = ''
1844
1845+testing_extras = ['nose', 'coverage']
1846+
1847 setup(name='repoze.lru',
1848- version='0.5',
1849+ version='0.6',
1850 description='A tiny LRU cache implementation and decorator',
1851 long_description=README + '\n\n' + CHANGES,
1852 classifiers=[
1853 "Intended Audience :: Developers",
1854 "Programming Language :: Python",
1855- "Programming Language :: Python :: 2.5",
1856 "Programming Language :: Python :: 2.6",
1857 "Programming Language :: Python :: 2.7",
1858 "Programming Language :: Python :: 3",
1859@@ -54,5 +55,9 @@
1860 test_suite="repoze.lru",
1861 entry_points = """\
1862 """,
1863- )
1864+ extras_require = {
1865+ 'testing': testing_extras,
1866+ 'docs': ['Sphinx',],
1867+ }
1868+)
1869
1870
1871=== modified file 'tox.ini'
1872--- tox.ini 2012-04-09 10:24:06 +0000
1873+++ tox.ini 2013-06-07 00:36:27 +0000
1874@@ -1,47 +1,35 @@
1875 [tox]
1876 envlist =
1877- py25,py26,py27,py32,pypy
1878+ py26,py27,py32,pypy,cover,docs
1879
1880 [testenv]
1881 commands =
1882 python setup.py test -q
1883-
1884-# timing race conditions in tests cause a test under Jython 2.5 to fail:
1885-
1886-#.F.......................
1887-#======================================================================
1888-#FAIL: When timeout is given, decorator must eventually forget entries
1889-#----------------------------------------------------------------------
1890-#Traceback (most recent call last):
1891-# File "/home/chrism/projects/repoze.lru/.tox/jython/Lib/site-packages/repoze/l#ru/tests.py", line 512, in test_expiry
1892-# self.assertTrue(stop - start > 0.1)
1893-#AssertionError
1894-
1895-#[testenv:jython]
1896-#commands =
1897-# jython setup.py test -q
1898-
1899-# coverage reporting broken with namespace packages and pip/nose apparently.
1900-# It's not a tox thing or is it related to setuptools vs. distribute; once
1901-# tox creates the virtualenv with either setuptools or distribute, this
1902-# command finds no tests to run
1903-#
1904-# .tox/cover/bin/python setup.py nosetests
1905-#
1906-# which means it's something to do with nose probably
1907-
1908-# [testenv:cover]
1909-# basepython =
1910-# python2.7
1911-# commands =
1912-# python setup.py nosetests --with-xunit --with-xcoverage
1913-# deps =
1914-# nose
1915-# coverage==3.4
1916-# nosexcover
1917-# distribute = False
1918+deps =
1919+ setuptools-git
1920+ virtualenv
1921+
1922+[testenv:cover]
1923+basepython =
1924+ python2.6
1925+commands =
1926+ nosetests --with-xunit --with-xcoverage
1927+deps =
1928+ setuptools-git
1929+ virtualenv
1930+ nose
1931+ coverage
1932+ nosexcover
1933
1934 # we separate coverage into its own testenv because a) "last run wins" wrt
1935 # cobertura jenkins reporting and b) pypy and jython can't handle any
1936 # combination of versions of coverage and nosexcover that i can find.
1937
1938+[testenv:docs]
1939+basepython =
1940+ python2.6
1941+commands =
1942+ sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html
1943+ sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest
1944+deps =
1945+ Sphinx

Subscribers

People subscribed via source and target branches

to all changes: