<?xml version="1.0" encoding="UTF-8" ?>
<rdf:RDF xmlns="http://usefulinc.com/ns/doap#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><Project><name>gocept.cache</name>
<shortdesc>Cache descriptors for Python and Zope</shortdesc>
<description>============
gocept.cache
============

Method cache
============

Memoize with timeout
--------------------

Memoize with timeout caches methods with a certain timeout:

&gt;&gt;&gt; import math
&gt;&gt;&gt; import gocept.cache.method
&gt;&gt;&gt;
&gt;&gt;&gt; class Point(object):
...
...     def __init__(self, x, y):
...         self.x, self.y = x, y
...
...     @gocept.cache.method.Memoize(0.1)
...     def distance(self, x, y):
...         print 'computing distance'
...         return math.sqrt((self.x - x)**2 + (self.y - y)**2)
...
...     @gocept.cache.method.Memoize(0.1, ignore_self=True)
...     def add_one(self, i):
...         if not isinstance(i, int):
...             print "I want an int"
...         else:
...             print 'adding one'
...             return i + 1
...
&gt;&gt;&gt; point = Point(1.0, 2.0)

When we first ask for the distance it is computed:

&gt;&gt;&gt; point.distance(2, 2)
computing distance
1.0

The second time the distance is not computed but returned from the cache:

&gt;&gt;&gt; point.distance(2, 2)
1.0

Now, let's wait 0.1 secondes, the value we set as cache timeout. After that the
distance is computed again:

&gt;&gt;&gt; import time
&gt;&gt;&gt; time.sleep(0.5)
&gt;&gt;&gt; point.distance(2, 2)
computing distance
1.0


When we create a new instance, the new instance gets its own cache:

&gt;&gt;&gt; p2 = Point(1.0, 2.0)
&gt;&gt;&gt; p2.distance(2, 2)
computing distance
1.0

It's also possible to explicitly ignore self. We did this for the `add_one`
method:

&gt;&gt;&gt; point.add_one(3)
adding one
4

The second time it's not computed as you would expect:
&gt;&gt;&gt; point.add_one(3)
4

If we ask `p2` now the result is not computed as well:

&gt;&gt;&gt; p2.add_one(3)
4

If we put a non hashable argument into a memoized function it will not be
cached:

&gt;&gt;&gt; point.add_one({'a': 1})
I want an int
&gt;&gt;&gt; point.add_one({'a': 1})
I want an int


The decorated method can be introspected and yields the same results ad the
original:

&gt;&gt;&gt; import inspect
&gt;&gt;&gt; Point.distance.func_name
'distance'
&gt;&gt;&gt; inspect.getargspec(Point.distance)
(['self', 'x', 'y'], None, None, None)

Store memoizations on an attribute
----------------------------------

If you want more control over the cache used by gocept.cache.method.Memoize
(e. g. you want to associate it with a gocept.cache.property.CacheDataManager
to invalidate it on transaction boundaries), you can use the @memoize_on_attribute
decorator to retrieve the cache-dictionary from the instance:

&gt;&gt;&gt; class Bar(object):
...     cache = {}
...
...     @gocept.cache.method.memoize_on_attribute('cache', 10)
...     def echo(self, x):
...         print 'miss'
...         return x

&gt;&gt;&gt; bar = Bar()
&gt;&gt;&gt; bar.echo(5)
miss
5
&gt;&gt;&gt; bar.echo(5)
5
&gt;&gt;&gt; bar.cache.clear()
&gt;&gt;&gt; bar.echo(5)
miss
5

This decorator should be used on methods, not on plain functions, since it must
be able to retrieve the cache-dictionary from the first argument of the function
(which is 'self' for methods):

&gt;&gt;&gt; @gocept.cache.method.memoize_on_attribute('cache', 10)
... def bar():
...     print 'foo'
&gt;&gt;&gt; bar()
Traceback (most recent call last):
TypeError: gocept.cache.method.memoize_on_attribute could not retrieve cache attribute 'cache' for function &lt;function bar at 0x...&gt;

&gt;&gt;&gt; @gocept.cache.method.memoize_on_attribute('cache', 10)
... def baz(x):
...     print 'foo'
&gt;&gt;&gt; baz(5)
Traceback (most recent call last):
TypeError: gocept.cache.method.memoize_on_attribute could not retrieve cache attribute 'cache' for function &lt;function baz at 0x...&gt;


Cached Properties
=================

Transaction Bound Cache
-----------------------

The transaction bound cache is invalidated on transaction boundaries.

Create a class and set some data:

&gt;&gt;&gt; import gocept.cache.property
&gt;&gt;&gt; class Foo(object):
...
...     cache = gocept.cache.property.TransactionBoundCache('_cache', dict)
...
&gt;&gt;&gt; foo = Foo()
&gt;&gt;&gt; foo.cache
{}
&gt;&gt;&gt; foo.cache['A'] = 1
&gt;&gt;&gt; foo.cache
{'A': 1}

If we commit the transaction the cache is empty again:

&gt;&gt;&gt; import transaction
&gt;&gt;&gt; transaction.commit()
&gt;&gt;&gt; foo.cache
{}


The same happens on abort:

&gt;&gt;&gt; foo.cache['A'] = 1
&gt;&gt;&gt; foo.cache
{'A': 1}
&gt;&gt;&gt; transaction.abort()
&gt;&gt;&gt; foo.cache
{}


=======
Changes
=======

0.4 (2009-06-18)
================

- Registered clearing the cache with zope.testing.cleanup.

0.3 (2008-12-19)
================

- Added @memoize_on_attribute to retrieve the memoization cache from the
  instance instead of using gocept.cache.method's built-in cache.

0.2.2 (2007-12-17)
==================

- Fixed the bug in `TransactionBoundCache` where the cache was not invalidated
  on transaction abort.

0.2.1 (2007-10-17)
==================

- Fixed a bug in `TransactionBoundCache` which yielded an error in the log:
  `TypeError: &lt;lambda&gt;() takes exactly 1 argument (2 given)`</description>
<homepage rdf:resource="http://pypi.python.org/pypi/gocept.cache" />
<maintainer><foaf:Person><foaf:name>Christian Zagrodnick</foaf:name>
<foaf:mbox_sha1sum>88c4add050b0c67a4c17a2d28bb886524aa54a9a</foaf:mbox_sha1sum></foaf:Person></maintainer>
<release><Version><revision>0.4</revision></Version></release>
</Project></rdf:RDF>