{"id":21858242,"url":"https://github.com/simpleart/recursive_cache","last_synced_at":"2025-09-12T02:42:59.561Z","repository":{"id":111556863,"uuid":"389736071","full_name":"SimpleArt/recursive_cache","owner":"SimpleArt","description":"Implementation of a cache which enables  deep recursion.","archived":false,"fork":false,"pushed_at":"2021-07-26T21:50:34.000Z","size":20,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-21T19:48:46.676Z","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/SimpleArt.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}},"created_at":"2021-07-26T18:45:02.000Z","updated_at":"2021-07-26T21:50:36.000Z","dependencies_parsed_at":"2023-03-13T13:39:01.723Z","dependency_job_id":null,"html_url":"https://github.com/SimpleArt/recursive_cache","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/SimpleArt/recursive_cache","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimpleArt%2Frecursive_cache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimpleArt%2Frecursive_cache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimpleArt%2Frecursive_cache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimpleArt%2Frecursive_cache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SimpleArt","download_url":"https://codeload.github.com/SimpleArt/recursive_cache/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimpleArt%2Frecursive_cache/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274744062,"owners_count":25341136,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-12T02:00:09.324Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":"2024-11-28T02:44:26.825Z","updated_at":"2025-09-12T02:42:59.553Z","avatar_url":"https://github.com/SimpleArt.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"Python is well known for its conservative restriction on the amount of recursive calls one may have at any given moment. One way to work around this is to cache results to avoid recursing deeper when unnecessary. The issue with this approach is that you need to start finishing recursive calls and caching their results before the amount of recursive calls becomes too large. This module seeks to provide an alternative caching mechanism which allows deep recursion that goes well beyond the recursion limit.\n\nExamples may be seen in the `examples.py` file.\n\n# Deep Recursion\n\nRecursion may be done deeper than the recursion limit without modifying the recursion limit or call stack. Here's an example of a memoized fibonacci function involving recursion deeper than `functools.lru_cache` can handle:\n\n```python\n@recursive_cache\ndef fib(n):\n    return n if n \u003c 2 else fib(n-2) + fib(n-1)\n\nprint(fib(3000))\n```\n\n# Iterators\n\nIterators, such as generators, cannot be easily handled. This is because they are lazily evaluated and wait until a value is requested to begin computation. Furthermore, they are one-time-use objects which must be re-evaluated on every call. However, this does mean deep recursion with iterators is impossible. It just means that any deep recursion must be taken care of *before* the iterator starts. This might mean using `itertools.chain` and generator expressions instead of generators.\n\nFor example, the following generator does not successfully run due to blocking recursion when used:\n\n```python\nfrom number_theory import is_prime\n\n@recursive_cache\ndef generator(n):\n    if n \u003e= 0:\n        for i in range(n):\n            if is_prime(i):\n                yield i\n        yield from generator(n-1)\n```\n\nThe following is a corrected version using `return` and `filter` to perform the recursion before any iteration occurs while still allowing mostly lazy iterators:\n\n```python\nfrom itertools import chain\nfrom number_theory import is_prime\n\n@recursive_cache\ndef generator(n):\n    if n \u003e= 0:\n        return chain(filter(is_prime, range(n)), generator(n-1))\n    return iter([])\n```\n\nOr using generator expressions:\n\n```python\nfrom itertools import chain\nfrom number_theory import is_prime\n\n@recursive_cache\ndef generator(n):\n    if n \u003e= 0:\n        return chain((i for i in range(n) if is_prime(i)), generator(n-1))\n    return iter([])\n```\n\nAnother solution is to wrap the generator and pre-compute anything that occurs recursively outside:\n\n```python\nfrom itertools import chain\nfrom number_theory import is_prime\n\n# Isolate the generator from the recursion.\ndef helper(n, iterable):\n    for i in range(n):\n        if is_prime(i):\n            yield i\n    yield from iterable\n\n# Isolate the recursion from the generator.\n@recursive_cache\ndef generator(n):\n    return helper(n, generator(n-1)) if n \u003e= 0 else iter([])\n```\n\n# Exceptions/Traceback\n\nAdditionally, exception traceback is reduced by default to make recursive tracebacks more understandable. Note that normally such tracebacks will be fully expanded and then truncated to the recursion limit, which is extremely unhelpful if the majority of it is the same few lines repeated thousands of times.\n\n```python\n\u003e\u003e\u003e @recursive_cache\n... def foo(n):\n...     if n \u003c 0:\n...         raise ValueError\n...     return bar(n-1)\n...\n\u003e\u003e\u003e @recursive_cache\n... def bar(n):\n...     return foo(n-1)\n...\n\u003e\u003e\u003e foo(3000)\nrecursive_cache.InfoException:\n  ...\n\nThe above exception was the direct cause of the following exception:\n\nTraceback (most recent call last):\n  ...\nrecursive_cache.InfoException: [Previous lines caused an exception in the below recursion]\n\nThe above exception was the direct cause of the following exception:\n\nTraceback (most recent call last):\n  ...\nrecursive_cache.InfoException: [Previous 2 lines repeated 1499 more times and caused an exception in the code below which called it]\n\nThe above exception was the direct cause of the following exception:\n\nTraceback (most recent call last):\n  ...\nValueError\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimpleart%2Frecursive_cache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimpleart%2Frecursive_cache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimpleart%2Frecursive_cache/lists"}