{ "info": { "author": "Jared Gillespie", "author_email": "jaredlgillespie@hotmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Utilities" ], "description": "Cache Me\n========\n\n.. image:: https://img.shields.io/travis/JaredLGillespie/cache.me.svg\n :alt: Travis\n :target: https://travis-ci.org/JaredLGillespie/cache.me\n.. image:: https://img.shields.io/coveralls/github/JaredLGillespie/cache.me.svg\n :alt: Coveralls github\n :target: https://coveralls.io/github/JaredLGillespie/cache.me\n.. image:: https://img.shields.io/pypi/v/cache.me.svg\n :alt: PyPI\n :target: https://pypi.org/project/cache.me/\n.. image:: https://img.shields.io/pypi/wheel/cache.me.svg\n :alt: PyPI - Wheel\n :target: https://pypi.org/project/cache.me/\n.. image:: https://img.shields.io/pypi/pyversions/cache.me.svg\n :alt: PyPI - Python Version\n :target: https://pypi.org/project/cache.me/\n.. image:: https://img.shields.io/pypi/l/cache.me.svg\n :alt: PyPI - License\n :target: https://pypi.org/project/cache.me/\n\nA library for caching the outputs of functions based on the inputted parameters to reduce recomputing expensive\ncomputations and requesting frequently accessed content, and to improve performance.\n\n.. code-block:: python\n\n @cache(StaticCache(),\n on_hit=lambda h: print('Hit %s time(s)' % h),\n on_miss=lambda m: print('Missed %s time(s)' % m))\n def fibonacci(n):\n if n < 2:\n return n\n return fib(n-1) + fib(n-2)\n\nInstallation\n------------\n\nThe latest version of cache.me is available via ``pip``:\n\n.. code-block:: python\n\n pip install cache.me\n\nAlternatively, you can download and install from source:\n\n.. code-block:: python\n\n python setup.py install\n\nGetting Started\n---------------\n\nThe ``cache`` decorator contains the following signature:\n\n.. code-block:: python\n\n @cache(algorithm, include_types=False, on_hit=None, on_miss=None, key_func=None)\n def func(...)\n ...\n\nIt serves as both a function decorator, and a runnable wrapper and is configurable through it's dynamic parameters.\n\nIn its simplest form, the decorator accepts different algorithms that can be used to cache the outputs of the function\nbased on its inputs, otherwise known as `memoization`_. Different algorithms are provided to perform this memoization\nfor different access patterns, as described below.\n\nThe creation of the key is based on the arguments and keyword arguments passed in. The type information can also be used\nin the formation of the key by setting the ``include_types`` parameter to True. In addition, the arguments used for the\nfunction can be altered before use for the key creation via the ``key_func`` parameter. A common use case for\nthis is to remove a parameter since as a timer or logging object from the key creation. The function should accept the\nsame arguments as the calling function and return a tuple of the arguments and keyword arguments to use to create the\nkey.\n\n.. code-block:: python\n\n def key_changer(x, logger):\n return (x,), {}\n\n @cache(RandomCache(size=30), key_func=key_changer)\n def func(x, logger)\n ...\n\nTwo callback functions, ``on_hit`` and ``on_miss``, can be passed into the function to be called when either a cache hit\nor cache miss occurs. For the most typical use cases, these are passed either the number of hits or number of misses\noccurred.\n\n.. _memoization: https://en.wikipedia.org/wiki/Memoization\n\nBound Function Methods\n^^^^^^^^^^^^^^^^^^^^^^\n\nIn addition to caching the function, other methods are bound to the function for interacting with the cache. The\nfollowing methods are added to the decorated function:\n\n- ``cache_info()``: Returns a tuple of information about the cache containing the number of hits, the number of misses, the current size, and the maximum size.\n- ``cache_clear()``: Clears the cache along with its statistics.\n\n.. code-block:: python\n\n @cache(NMRUCache(30))\n def func(...)\n ...\n\n # Clear the cache\n func.cache_clear()\n\n # Grab cache information\n hits, misses, current_size, max_size = func.cache_info()\n\nCaching Algorithms\n------------------\n\nThe following caching algorithms are provided by the library (although others could be extended from the ``BaseCache``):\n\n- `FIFO (First-in First-out)`_\n- `LIFO (Last-in First-out`_\n- `LFU (Least Frequently Used)`_\n- `LRU (Least Recently Used)`_\n- `MQ (Multi-Queue)`_\n- MRU (Most Recently Used)\n- NMRU (Not Most Recently Used)\n- `RR (Random Replacement)`_\n- `SLRU (Segmented Least Recently Used)`_\n- `Static`\n- `TLRU (Time-aware Least Recently Used)`_\n- TwoQ (simple 2Q or Two-Queue)\n- TwoQFull (full 2Q or Two-Queue)\n\nWhile each of these can be fed into the ``cache`` decorator, they can also be used on their own by simply creating an\ninstance and calling the appropriate methods. Each implemented algorithm has an O(1) time complexity for accesses and\ninsertions, and have the following methods and properties:\n\n- ``current_size``: The current size of the cache.\n- ``hits``: The number of cache hits.\n- ``max_size``: The maximum size of the cache.\n- ``misses``: The number of cache misses.\n- ``clear()``: Clears the items in the cache.\n- ``get(key, sentinel)``: Gets an item in the cache.\n- ``put(key, value)``: Retrieves an item in the cache.\n- ``dynamic_methods()``: Provides dynamic binding of methods for ``cache`` decorator.\n- ``create_key(...)``: Creates a cache key.\n\n.. _FIFO (First-in First-out): https://en.wikipedia.org/wiki/Cache_replacement_policies#First_in_first_out_(FIFO)\n.. _LIFO (Last-in First-out: https://en.wikipedia.org/wiki/Cache_replacement_policies#Last_in_first_out_(LIFO)\n.. _LFU (Least Frequently Used): https://en.wikipedia.org/wiki/Cache_replacement_policies#Least-frequently_used_(LFU)\n.. _LRU (Least Recently Used): https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)\n.. _MQ (Multi-Queue): https://en.wikipedia.org/wiki/Cache_replacement_policies#Multi_queue_(MQ)\n.. _MRU (Most Recently Used): https://en.wikipedia.org/wiki/Cache_replacement_policies#Most_recently_used_(MRU)\n.. _RR (Random Replacement): https://en.wikipedia.org/wiki/Cache_replacement_policies#Random_replacement_(RR)\n.. _SLRU (Segmented Least Recently Used): https://en.wikipedia.org/wiki/Cache_replacement_policies#Segmented_LRU_(SLRU)\n.. _TLRU (Time-aware Least Recently Used): https://en.wikipedia.org/wiki/Cache_replacement_policies#Time_aware_least_recently_used_(TLRU)\n\nFIFO (First-in First-out)\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe ``FIFOCache`` is a First-in First-out cache where keys are evicted in order of arrival when the cache is full.\nAccessing a key does not change the order of eviction.\n\n.. code-block:: python\n\n @cache(FIFOCache(size=50))\n def func(...)\n ...\n\nLIFO (First-in First-out)\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe ``LIFOCache`` is a Last-in First-out cache where keys are evicted in reverse order of arrival when the cache is\nfull. Accessing a key does not change the order of eviction.\n\n.. code-block:: python\n\n @cache(LIFOCache(size=50))\n def func(...)\n ...\n\nLFU (Least Frequently Used)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe ``LFUCache`` is a Least Frequently Used cache where keys which have been accessed the least number of times are\nevicted when the cache is full. The frequency list structure as described in `\"An O(1) algorithm for implementing the LFU cache eviction scheme\"`_\nis implemented.\n\n.. code-block:: python\n\n @cache(LFUCache(size=50))\n def func(...)\n ...\n\n.. _\"An O(1) algorithm for implementing the LFU cache eviction scheme\": http://dhruvbird.com/lfu.pdf\n\nLRU (Least Recently Used)\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe ``LRUCache`` is a Least Recently Used cache where keys which have been accessed the least recently are evicted when\nthe cache is full.\n\n.. code-block:: python\n\n @cache(LRUCache(size=50))\n def func(...)\n ...\n\nMFU (Most Frequently Used)\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe ``MFUCache`` is a Most Frequently Used cache where keys which have been accessed the most number of times are\nevicted when the cache is full. This uses the same frequency list structure as described in LFU.\n\n.. code-block:: python\n\n @cache(MFUCache(size=50))\n def func(...)\n ...\n\nMQ (Multi-Queue)\n^^^^^^^^^^^^^^^^\n\nThe ``MQCache`` is a Multi-Queue cache in which multiple queues are used to hold levels of varying temperature (i.e.\nhighly accessed and less accessed) along with a history buffer (similar to 2Q). This is implemented based on the\npaper \"The Multi-Queue Replacement Algorithm for Second Level Buffer Caches\" which uses a LRU queues for each level. The\naccess count of each item is also recorded and used in determining which queue to promote the item to based on the\n``queue_func`` parameter.\n\nItems are also susceptible to being evicted over time. If an item isn't accessed within a certain time it is bumped\ndown to a lower queue and its expiration time is reset. This continues if the item isn't accessed until it is\neventually evicted to the lowest queue.\n\nThe history buffer is a FIFO queue that keeps track of items recently evicted from the queue. If an item is accessed\nwhile in the history buffer, it is placed in the appropriate queue based on it's previous access frequency + 1.\n\n.. code-block:: python\n\n @cache(MQCache(size=50, buffer_size=10, expire_time=100, num_queues=8,\n queue_func=lambda f: math.log(f, 2), access_based=True))\n def func(...)\n ...\n\nMRU (Most Recently Used)\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe ``MRUCache`` is a Most Recently Used cache where keys which have been accessed the most recently are evicted when\nthe cache is full.\n\n.. code-block:: python\n\n @cache(MRUCache(size=50))\n def func(...)\n ...\n\nNMRU (Not Most Recently Used)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe ``NMRUCache`` is a Not Most Recently Used cache where keys which have not been access the most recently are evicted\nwhen the cache is full. When the cache is full, a random key other than the most recently inserted is removed.\n\n.. code-block:: python\n\n @cache(NMRUCache(size=50))\n def func(...)\n ...\n\nRR (Random Replacement)\n^^^^^^^^^^^^^^^^^^^^^^^\n\nThe ``RRCache`` is a Random Replacement cache where keys are evicted randomly, regardless of access of insertion order.\n\n.. code-block:: python\n\n @cache(RRCache(size=50))\n def func(...)\n ...\n\nSLRU (Segmented Least Recently Used)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe ``SLRUCache`` is a Segmented Least Recently Used cache which is implemented with two queues, a LRU (the protected),\nand a FIFO (the probationary). Items are initially placed into the probationary queue when first placed into the cache.\nIf this cache is full, items are evicted in the order of their arrival. If items are accessed while they are in the\nprobationary queue, they are moved to the protected queue. They stay in this queue until it is full and the key\nwhich has been least recently used is moved back to the probationary queue.\n\nNote that this cache implementation is very similar to the simple 2Q algorithm with the exception that items evicted\nfrom the protected cache are moved to the probationary (opposed to being immediately evicted).\n\n.. code-block:: python\n\n @cache(SLRUCache(protected_size=50, probationary_size=20))\n def func(...)\n ...\n\nStatic\n^^^^^^\n\nThe ``StaticCache`` is a simple cache with no key eviction. Keys are stored permanently, or at least until the cache is\ncleared.\n\n.. code-block:: python\n\n @cache(StaticCache())\n def func(...)\n ...\n\n\nTLRU (Time-aware Least Recently Used)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe ``TLRUCache`` is a Time-aware Least Recently-Used cache where keys are prematurely evicted if their last access time\nis below a minimum limit, the ``expire_time``. Time in this case is either a simple clock that is incremented each time\nthe cache is accessed., or the actual time in seconds that has passed. This is determined by the ``access_based``\nparameter. If ``reset_on_access`` is True, the ``expire_time`` is reset each time the item is accessed; otherwise it is\nexpired from the time of initial insertion in the cache.\n\nThis is implemented with two LRU lists, one for LRU-based expiration and another for time-based expiration. This is\nrequired due to allowing ``reset_on_access`` to be False, thereby allowing items to be expired independent of how they\nare accessed.\n\n.. code-block:: python\n\n @cache(TLRUCache(expire_time=100, size=50, access_based=False, reset_on_access=True))\n def func(...)\n ...\n\nTwoQ (simple 2Q or Two-Queue)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe ``TwoQCache`` is the simple 2Q cache described by the algorithm in \"2Q: A Low Overhead High Performance Buffer\nManagement Replacement Algorithm\". Two queues are used, an LRU (the primary), and a FIFO (the secondary). Items are\ninitially placed into the secondary queue when placed into the cache. If the secondary queue is full, items are evicted\nin the order of their arrival. If items are accessed while they are in the secondary queue, they are moved to the\nprimary queue. They stay in this queue until it is full and the key which has been least recently used is evicted.\n\n.. code-block:: python\n\n @cache(TwoQ(primary_size=50, secondary_size=20))\n def func(...)\n ...\n\nTwoQFull (full 2Q or Two-Queue)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe ``TwoQFullCache`` is the full 2Q cache as described by the algorithm in \"2Q: A Low Overhead High Performance\nBuffer Management Replacement Algorithm\". Three queues are used, an LRU (the primary), and two FIFO (the secondary\nin and out). Items are initially placed into the secondary \"in\" queue when first placed into the cache. If this queue is\nfull, items are moved in the order of their arrival into the secondary \"out\" queue. If items are accessed while they are\nin the secondary \"in\" queue they stay in this queue in their current position.\n\nIf items are accessed while they are in the secondary \"out\" queue they are moved to the primary queue. If this cache\nis full, items are evicted in the order of their arrival. Items in the primary queue stay in this queue until it is\nfull and the key which has been least recently used is evicted. This implementation yields O(1) access and insertion\ntime.\n\n.. code-block:: python\n\n @cache(TwoQFull(primary_size=50, secondary_in_size=20, secondary_out_size=20))\n def func(...)\n ...\n\nAdvanced Usage\n--------------\n\nInstead of using as a decorator, ``cache`` can be used as an instead for wrapping an arbitrary number of function calls.\nThis can be achieved via the ``run`` method.\n\n.. code-block:: python\n\n def func_a():\n ...\n\n def func_b():\n ...\n\n cacher = cache(algorithm=...)\n\n # Using same configured cache instance\n cache.run(func_a, args, kwargs)\n cache.run(func_b, args, kwargs)\n\nBesides using the provided ``run`` method, like any decorator functions can be locally wrapped, passed around, and\nexecuted.\n\n.. code-block:: python\n\n def func():\n ...\n\n cacher = cache(algorithm=...)\n cache_func = cacher(func)\n cache_func(args, kwargs)\n\n # Or as a one-off like so\n cache(...)(func)(args, kwargs)\n\nBoth the ``on_hit`` and ``on_miss`` callback functions that can be passed into ``cache`` can actually be configured to\naccepts different number of parameters depending on the function. They can each either accept 0 parameters, the\nparameters that would be typically passed in, or the wrapped function's args and kwargs in addition to the parameters\ntypically given.\n\nOptionally passing in the args and kwargs allows for building more complex callback functions. Each of the possible\nfunction variations are shown below.\n\n.. code-block:: python\n\n def on_hit(): ...\n def on_hit(error): ...\n def on_hit(error, *args, **kwargs): ...\n\n def on_miss(): ...\n def on_miss(value): ...\n def on_miss(value, *args, **kwargs): ...\n\nIn addition to the ``cache_info()`` and ``cache_clear()`` methods bound to the function, others can be dynamically bound\nto the function depending on the algorithm. None of the current basic implementations use this functionality, but this\nhas a case for when creating one's own or extending the existing algorithms. The dynamic methods are prefixed with\n\"cache_\".\n\n.. code-block:: python\n\n import copy\n\n class FancyCache(LRUCache):\n def __init__(self, size):\n super(size).__init__()\n\n def show_all(self):\n # Retrieve a copy of the current items in the cache\n return copy(self._map)\n\n def dynamic_methods(self):\n return ['show_all']\n\n @cache(FancyCache())\n def func(x, y)\n ...\n\n func(1, 2)\n func(3, 4)\n\n # Dynamic methods are prefixed with 'cache_'\n items = func.cache_show_all()\n\nContribution\n------------\n\nContributions or suggestions are welcome! Feel free to `open an issue`_ if a bug is found or an enhancement is desired,\nor even a `pull request`_.\n\n.. _open an issue: https://github.com/JaredLGillespie/cache.me/issues\n.. _pull request: https://github.com/JaredLGillespie/cache.me/compare\n\nChangelog\n---------\n\nAll changes and versioning information can be found in the `CHANGELOG`_.\n\n.. _CHANGELOG: https://github.com/JaredLGillespie/cache.me/blob/master/CHANGELOG.rst\n\nLicense\n-------\n\nCopyright (c) 2018 Jared Gillespie. See `LICENSE`_ for details.\n\n.. _LICENSE: https://github.com/JaredLGillespie/cache.me/blob/master/LICENSE.txt\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/jaredlgillespie/cache.me", "keywords": "cache.me cache decorator", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "cache.me", "package_url": "https://pypi.org/project/cache.me/", "platform": "", "project_url": "https://pypi.org/project/cache.me/", "project_urls": { "Homepage": "https://github.com/jaredlgillespie/cache.me" }, "release_url": "https://pypi.org/project/cache.me/0.1.1/", "requires_dist": null, "requires_python": "", "summary": "A library for caching function calls.", "version": "0.1.1" }, "last_serial": 4089224, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "ba9b6aca2f5361e19a5c18160b3a757d", "sha256": "480ba767ceb5642d3708794127545bc12b66cef1e9ef943fc0f8c24520b0ec94" }, "downloads": -1, "filename": "cache.me-0.1.0-py3.6.egg", "has_sig": false, "md5_digest": "ba9b6aca2f5361e19a5c18160b3a757d", "packagetype": "bdist_egg", "python_version": "3.6", "requires_python": null, "size": 29241, "upload_time": "2018-07-21T18:23:31", "url": "https://files.pythonhosted.org/packages/ad/44/f0646aac08708409c2f617a57fb1cfb09a5095af42bd06aeb783d3790ae0/cache.me-0.1.0-py3.6.egg" }, { "comment_text": "", "digests": { "md5": "b0005cd92a440bb359ff3f6087db0b1d", "sha256": "a16460a3f8b3edebe59ae6a3de5e36b6559d2d1242c225db2b9ac0dc75286b04" }, "downloads": -1, "filename": "cache.me-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "b0005cd92a440bb359ff3f6087db0b1d", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 17853, "upload_time": "2018-07-21T18:23:30", "url": "https://files.pythonhosted.org/packages/4c/cc/61fdda535f1909bf70feb74a1ddd62ed828a5c177e7041936cea499dd609/cache.me-0.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c54e4cf0af276428323457510e857b24", "sha256": "2ee15345019d5d15ea34c8aeeeef2f5cab71e1fd7435a8fac7231b3b1350e026" }, "downloads": -1, "filename": "cache.me-0.1.0.tar.gz", "has_sig": false, "md5_digest": "c54e4cf0af276428323457510e857b24", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23471, "upload_time": "2018-07-21T18:23:32", "url": "https://files.pythonhosted.org/packages/5f/6c/0a14416dc552ff3f056ccb1801c17c8e1b5ddfc8028d4d5d6035fddfaa63/cache.me-0.1.0.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "328dd772352d95836ae31d45e98cb8e3", "sha256": "c82936c56d66ac8bf9315456c8057228dff8ef9e9723244b91175089258393a6" }, "downloads": -1, "filename": "cache.me-0.1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "328dd772352d95836ae31d45e98cb8e3", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 17862, "upload_time": "2018-07-21T20:26:20", "url": "https://files.pythonhosted.org/packages/cb/1c/a24a830486848554d091c62e1b44e81e39976d1b656492438038692fb7a7/cache.me-0.1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "70c9ef66a629c4065b27fee224806b57", "sha256": "b64960a8e308e77074ffb1dba3289c2848f6641fdb7221931339bc95f3dd2779" }, "downloads": -1, "filename": "cache.me-0.1.1.tar.gz", "has_sig": false, "md5_digest": "70c9ef66a629c4065b27fee224806b57", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23522, "upload_time": "2018-07-21T20:26:21", "url": "https://files.pythonhosted.org/packages/e3/cf/191903f5d9b2b1bedca618fd3a2ac1595fe2c574c20c7cbadae34e4dcad9/cache.me-0.1.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "328dd772352d95836ae31d45e98cb8e3", "sha256": "c82936c56d66ac8bf9315456c8057228dff8ef9e9723244b91175089258393a6" }, "downloads": -1, "filename": "cache.me-0.1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "328dd772352d95836ae31d45e98cb8e3", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 17862, "upload_time": "2018-07-21T20:26:20", "url": "https://files.pythonhosted.org/packages/cb/1c/a24a830486848554d091c62e1b44e81e39976d1b656492438038692fb7a7/cache.me-0.1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "70c9ef66a629c4065b27fee224806b57", "sha256": "b64960a8e308e77074ffb1dba3289c2848f6641fdb7221931339bc95f3dd2779" }, "downloads": -1, "filename": "cache.me-0.1.1.tar.gz", "has_sig": false, "md5_digest": "70c9ef66a629c4065b27fee224806b57", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23522, "upload_time": "2018-07-21T20:26:21", "url": "https://files.pythonhosted.org/packages/e3/cf/191903f5d9b2b1bedca618fd3a2ac1595fe2c574c20c7cbadae34e4dcad9/cache.me-0.1.1.tar.gz" } ] }