Metadata-Version: 2.1
Name: gocept.cache
Version: 5.0
Summary: Cache descriptors for Python and Zope
Home-page: https://github.com/gocept/gocept.cache
Author: gocept
Author-email: mail@gocept.com
License: MIT
Description: .. contents::
        
        ============
        gocept.cache
        ============
        
        Method cache
        ============
        
        Memoize with timeout
        --------------------
        
        Memoize with timeout caches methods with a certain timeout:
        
        >>> import math
        >>> import gocept.cache.method
        >>>
        >>> 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
        ...
        >>> point = Point(1.0, 2.0)
        
        When we first ask for the distance it is computed:
        
        >>> point.distance(2, 2)
        computing distance
        1.0
        
        The second time the distance is not computed but returned from the cache:
        
        >>> 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:
        
        >>> import time
        >>> time.sleep(0.5)
        >>> point.distance(2, 2)
        computing distance
        1.0
        
        
        When we create a new instance, the new instance gets its own cache:
        
        >>> p2 = Point(1.0, 2.0)
        >>> p2.distance(2, 2)
        computing distance
        1.0
        
        It's also possible to explicitly ignore self. We did this for the `add_one`
        method:
        
        >>> point.add_one(3)
        adding one
        4
        
        The second time it's not computed as you would expect:
        >>> point.add_one(3)
        4
        
        If we ask `p2` now the result is not computed as well:
        
        >>> p2.add_one(3)
        4
        
        If we put a non hashable argument into a memoized function it will not be
        cached:
        
        >>> point.add_one({'a': 1})
        I want an int
        >>> point.add_one({'a': 1})
        I want an int
        
        
        The decorated method can be inspected and yields the same results as the
        original:
        
        >>> from inspect import signature
        >>> Point.distance.__name__
        'distance'
        >>> tuple(signature(Point.distance).parameters.keys())
        ('self', 'x', 'y')
        
        
        Explicitly exclude caching of a certain value
        ---------------------------------------------
        
        Especially when talking to external systems, you want to handle
        errors (i.e. by returning an emtpy result). But normally you
        do not want to cache this special return value.
        
        This is our database.
        
        >>> class DB(object):
        ...
        ...     call_count = 0
        ...
        ...     def get_country(self, zip):
        ...         self.call_count += 1
        ...         if self.call_count % 2 == 0:
        ...             raise ValueError('Some strange response')
        ...         return 'Somecountry'
        ...
        
        It will throw an exception with every 2nd call:
        
        >>> db = DB()
        >>> db.get_country(12345)
        'Somecountry'
        
        >>> db.get_country(12345)
        Traceback (most recent call last):
        ...
        ValueError: Some strange response
        
        >>> db.get_country(12345)
        'Somecountry'
        
        >>> db.get_country(12345)
        Traceback (most recent call last):
        ...
        ValueError: Some strange response
        
        
        Now we use do_not_cache_and_return to specify that we do
        not want to cache if there was en error.
        
        >>> import gocept.cache.method
        >>>
        >>> class Country(object):
        ...
        ...     db = DB()
        ...
        ...     @gocept.cache.method.Memoize(1000)
        ...     def by_zip(self, zip):
        ...         try:
        ...             return self.db.get_country(zip)
        ...         except ValueError:
        ...             return gocept.cache.method.do_not_cache_and_return(
        ...                     'DB is down.')
        ...
        
        >>> country = Country()
        
        First call will get cached, so we get the correct country with every call:
        
        >>> country.by_zip(12345)
        'Somecountry'
        
        >>> country.by_zip(12345)
        'Somecountry'
        
        >>> country.by_zip(12345)
        'Somecountry'
        
        By using a new zip, the get_country method is called the second time, and
        there will be an exception, which is not cached:
        
        >>> country.by_zip(54321)
        'DB is down.'
        
        Calling it again will call get_country, because special return value is not
        cached:
        
        >>> country.by_zip(54321)
        'Somecountry'
        
        Now we always get the cached value:
        
        >>> country.by_zip(54321)
        'Somecountry'
        
        
        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:
        
        >>> class Bar(object):
        ...     cache = {}
        ...
        ...     @gocept.cache.method.memoize_on_attribute('cache', 10)
        ...     def echo(self, x):
        ...         print('miss')
        ...         return x
        
        >>> bar = Bar()
        >>> bar.echo(5)
        miss
        5
        >>> bar.echo(5)
        5
        >>> bar.cache.clear()
        >>> 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):
        
        >>> @gocept.cache.method.memoize_on_attribute('cache', 10)
        ... def bar():
        ...     print('foo')
        >>> bar()
        Traceback (most recent call last):
        TypeError: gocept.cache.method.memoize_on_attribute could not retrieve cache attribute 'cache' for function <function bar at 0x...>
        
        >>> @gocept.cache.method.memoize_on_attribute('cache', 10)
        ... def baz(x):
        ...     print('foo')
        >>> baz(5)
        Traceback (most recent call last):
        TypeError: gocept.cache.method.memoize_on_attribute could not retrieve cache attribute 'cache' for function <function baz at 0x...>
        
        
        Cached Properties
        =================
        
        Transaction Bound Cache
        -----------------------
        
        The transaction bound cache is invalidated on transaction boundaries.
        
        Create a class and set some data:
        
        >>> import gocept.cache.property
        >>> class Foo(object):
        ...
        ...     cache = gocept.cache.property.TransactionBoundCache('_v_cache', dict)
        ...
        
        (NOTE: You probably want to use a "volatile" attribute name for the cache
        storage, otherwise a read-only access of the cache triggers a write.)
        
        >>> foo = Foo()
        >>> foo.cache
        {}
        >>> foo.cache['A'] = 1
        >>> foo.cache
        {'A': 1}
        
        If we commit the transaction the cache is empty again:
        
        >>> import transaction
        >>> transaction.commit()
        >>> foo.cache
        {}
        
        
        The same happens on abort:
        
        >>> foo.cache['A'] = 1
        >>> foo.cache
        {'A': 1}
        >>> transaction.abort()
        >>> foo.cache
        {}
        
        
        =======
        Changes
        =======
        
        5.0 (2021-08-31)
        ================
        
        Backwards incompatible changes
        ------------------------------
        
        - Change license from ZPL to MIT.
        
        - Drop support for Python 2.7 and 3.6.
        
        Features
        --------
        
        - Add support for Python 3.10.
        
        Other changes
        -------------
        
        - Use Github actions as CI.
        
        
        4.0 (2020-02-17)
        ================
        
        - Add support for Python 3.7, 3.8 and 3.9a3.
        
        - Drop support for Python 3.4 and 3.5.
        
        - Migrate to github.
        
        
        3.1 (2018-11-20)
        ================
        
        - Add support for Python 3.7.
        
        - Remove DeprecationWarnings concerning inspect.
        
        
        3.0 (2017-11-20)
        ================
        
        - Remove use of ``__file__`` in setup.py, to accommodate recent setuptools.
        
        - Add support for Python 3.6.
        
        - Drop support for Python 3.3.
        
        
        2.1 (2016-11-17)
        ================
        
        - Bugfix: ``.property.CacheDataManager`` no longer invalidates the cache in
          ``tpc_vote()`` and ``commit()`` but in ``tpc_finish()``.
        
        - Raise `TransactionJoinError` if joining the transaction failed in
          ``.property.TransactionBoundCache``.
        
        
        2.0 (2016-03-18)
        ================
        
        - Drop support of Python 2.6.
        
        - Declare support of PyPy and PyPy3.
        
        
        1.0 (2015-09-25)
        ================
        
        - Now testing against currently newest versions of dependencies.
        
        - Drop support of Python 3.2.
        
        - Declare Support of Python 3.4 and 3.5.
        
        
        0.6.1 (2013-09-13)
        ==================
        
        - Finish Python 3 compatibility
        
        
        0.6 (2013-09-13)
        ================
        
        - Changes not recored, sorry.
        
        
        0.6b2 (2013-09-05)
        ==================
        
        - Changes not recored, sorry.
        
        
        0.6b1 (2013-09-05)
        ==================
        
        - Python3 compatibility
        
        
        0.5.2 (2012-06-22)
        ==================
        
        - Added ``gocept.cache.method.do_not_cache_and_return(value)`` in memoized
          methods/functions which will return the given value, without caching it.
        
        0.5.1 (2012-03-10)
        ==================
        
        - Prevent race condition which caused values of ``gocept.cache.method.Memoize``
          not to be stored when collect was called during the Memoize call
          (in multi threaded environments).
        
        - Pin test versions to ZTK 1.1
        
        0.5 (2011-03-15)
        ================
        
        - Replace dependency on ZODB with a dependency on transaction.
        
        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: <lambda>() takes exactly 1 argument (2 given)`
        
Keywords: cache transaction bound zope
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved
Classifier: License :: OSI Approved :: MIT License
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Programming Language :: Python :: Implementation :: CPython
Requires-Python: >=3.7, <4
Provides-Extra: test
