{"id":30883395,"url":"https://github.com/globocom/generic_cache","last_synced_at":"2026-02-23T08:04:13.578Z","repository":{"id":66161202,"uuid":"145581012","full_name":"globocom/generic_cache","owner":"globocom","description":"A Python utility / library to facilitate caching functions results'","archived":false,"fork":false,"pushed_at":"2018-08-23T20:59:58.000Z","size":23,"stargazers_count":9,"open_issues_count":0,"forks_count":2,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-09-08T09:49:43.105Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/globocom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-08-21T15:09:14.000Z","updated_at":"2021-02-10T23:56:46.000Z","dependencies_parsed_at":"2023-03-12T11:17:07.382Z","dependency_job_id":null,"html_url":"https://github.com/globocom/generic_cache","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/globocom/generic_cache","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globocom%2Fgeneric_cache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globocom%2Fgeneric_cache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globocom%2Fgeneric_cache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globocom%2Fgeneric_cache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/globocom","download_url":"https://codeload.github.com/globocom/generic_cache/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globocom%2Fgeneric_cache/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29739770,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-23T07:44:07.782Z","status":"ssl_error","status_checked_at":"2026-02-23T07:44:07.432Z","response_time":90,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-09-08T09:45:21.460Z","updated_at":"2026-02-23T08:04:13.573Z","avatar_url":"https://github.com/globocom.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Welcome to Generic Cache\n\nATTENTION: This is still a work in progress documentation.\n\nThis library is intended to ease caching the results of functions or method calls.\n\n## TL;DR\n\nBasically this library offers a decorator factory for you to use in your functions.\nThen generic_cache will handle key creation and cache lookup for you.\n\n#### Example\n```python\nimport time\nfrom generic_cache.decorator import CacheDecorator\nfrom generic_cache.key_builder import AttrsMethodKeyBuilder\n\ncache_backend = #... More on this below\ncache_decorator = CacheDecorator(\"SummerCache.\", cache_backend, AttrsMethodKeyBuilder(['id_number']))\n\nclass Summer:\n    def __init__(self, id_number, dummy='dummy'):\n        self.id_number = id_number\n        self.dummy = dummy\n    \n    @cache_decorator(\"long_id_sum_cache\")\n    def long_id_sum(self, other_number):\n        time.sleep(5)\n        return self.id_number + other_number\n\nsummer = Summer(42)\n\n# Long 5 secs wait for 43 result\nresult = summer.long_id_sum(1)\n\n# Cached 43 result\nresult = summer.long_id_sum(1)\n\n# Still Cached, it doesn't care about new instances.\nresult = Summer(42).long_id_sum(1)\n```\n\n## What Generic Cache differs from Python functools LRU Cache?\n\nThe major difference is that you can program your own cache Backend.\nPython functools [native cache](https://docs.python.org/3/library/functools.html#functools.lru_cache) uses LRU policy.\nSo, if you would like to have, for example,  a cache that uses a Heap Tree you can\ncreate it by simply extending [BaseBackend](https://github.com/globocom/generic_cache/blob/master/generic_cache/backend.py#L8) class\nand pass it to the cache decorator.\n\n## Glad you are still here, let's dive deeper!\n\nTo instantiate a decorator you will need 3 things:\n\n1. A key_prefix which is a string that will be used to generate ao keys created by the decorator\n1. a cache_backend. This could be any cache api that follows the implementation of `generic_cache.backend.BaseBackend` (it is based on django cache api, but not restricted to it)\n1. A key builder (you can grab one of the key builders available at `generic_cache.key_builder` or build one that suits your needs)\n\nTo use the decorator you will also need to pass the key_type (`\"long_id_sum_cache\"` in previous example). This will also be used to build the key.\n\nOnce this three demands are satisfied you're ready to start caching.\n\nIn our previous example we used `AttrsMethodKeyBuilder`. This key_builder will build keys based on the name/values\nof the method arguments plus the name/values of a set of the instance attributes.\nSo, from above, when we do:\n```python\nresult = summer.long_id_sum(1)\n```\nthe steps taken to evaluate the function is:\n1. Build the key (in this case the final key built will be the string `\"SummerCache.long_id_sum_cache__other_number_1__id_42\")\n1. Use the cache_backend to get a value from the cache.\n1. If a value is found return the cached value\n1. If no value is found. Call the actual function.\n1. Cache the value with the built key.\n1. Return the value\n\nAs you could see `AttrsMethodKeyBuilder` uses the methods arguments and instance attributes to build its keys. Other\nkey builders might be better for one's use case. `generic_cache` ships also with `MethodKeyBuilder` which only considers\nthe method arguments and `FunctionKeyBuilder`, which is analogous, but intended to use on functions not class/instance methods.\n\n#### Example of how `AttrsMethodKeyBuilder` works\n```python\nsummer = Summer(1)\n\n# Not cached\nsummer.long_id_sum(1)\n\n# Cached\nsummer.long_id_sum(1)\n\n# Not Cached, changed argument value\nsummer.long_id_sum(2)\n\n# Cached\nsummer.long_id_sum(2)\n\nsame_summer = Summer(1)\n# Cached\nsame_summer.long_id_sum(1)\n\nother_summer = Summer(2)\n\n# Not cached, attribute `id_number` is different.\nother_summer.long_id_sum(1)\n\ndifferent_but_same_summer = Summer(1, 'other_dummy_value')\n\n# Cached. dummy attribute changed, but is not used for key building.\ndifferent_but_same_summer.long_id_sum(1)\n```\n\nYou can use the same decorator on multiple functions.\n```python\nclass Example:\n    @cache_dec(\"method\")\n    def method(self):\n        # ....\n\n    @cache_dec(\"other_method\")\n    def other_method(self):\n        # ....\n```\n\n## Key management\n\u003e There are only two hard things in Computer Science: cache invalidation and naming things.\n\u003e\n\u003e -- Phil Karlton\n\n`generic_cache` comes with a handful of tools for cache invalidation.\n\n### Timeouts\n`CacheDecorator` accepts a `default_timeout` argument which will be used on all its created keys.\n```python\ncache_decorator = CacheDecorator(\"SummerCache.\", cache_backend, AttrsMethodKeyBuilder(['id_number']), default_timeout=1000)\n```\n\nWhen decorating a function you can also set a `key_timeout`. Which will be used to all keys built on that function.\n```python\n@cache_decorator(\"my_func_cache\", key_timeout=1500)\ndef my_func():\n    pass\n```\n\n### Key Versions\nNow suppose you have this function\n```python\n@cache_decorator(\"sum_a_number\", timeout=1000)\ndef sum_a_number(other_number):\n    return 1 + other_number\n```\n\nAnd its implementation has now changed to:\n```python\ndef sum_a_number(other_number):\n    return 10 + other_number\n```\nThis implementation change affects the return value of the function, but you will probably have cached values of the previous implementation.\nThe secret is to use key_versions when this happens:\n\n```python\n@cache_decorator(\"sum_a_number\", timeout=1000, key_version=\"v1.1\")\ndef sum_a_number(other_number):\n    return 10 + other_number\n```\n\nNow every generated key will have the version appendend. Which won't cause the cache to lookup old implementation keys. If the implementation\nchanges again. Just bump the key_version.\n\n### Flushing\nSuppose you have a `User` class which caches the result of `get_photo`:\n\n```python\nclass User:\n    def __init__(self, id):\n        self.id = id\n\n    @cache_decorator(\"get_photo\")\n    def get_photo(self, photo_type):\n        # Implementation...\n\nuser = User('user_id_1')\n# Happily using my cached function!\nphoto = user.get_photo('avatar')\n```\n\nNow somewhere in your system there is an upload in your user photo avatar and you know you must invalidate cache.\nThankfully `CacheDecorator` will enhance the function with a `cache` attribute that you can use to `flush` the cached value.\n```python\nuser = User('user_id_1')\n# Cached\nuser.get_photo('avatar')\n\n# Somewhere after a new photo is uploaded\n\n# You call the flush method with the same arguments you would call the cached function.\n# WARNING for class/instance methods will you have to pass the class/instance as the first argument\nuser.get_photo.cache.flush(user, 'avatar')\n\n# No longer cached\nuser.get_photo('avatar')\n```\n\n### Disabling Cache\nEvery cached function will accept a `disable_cache` kwarg. If this value is `True` the function will always be evaluated, ignoring cache lookups.\n\nThere is also the `disable_cache_overwrite` which forces the cache not to be updated on that call.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglobocom%2Fgeneric_cache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fglobocom%2Fgeneric_cache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglobocom%2Fgeneric_cache/lists"}