Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 27, 2026

Overview

Adds full asyncio/coroutine support to Cachier, enabling the existing @cachier decorator to transparently cache both synchronous and asynchronous functions without requiring separate decorators or code changes.

Implementation

Core Changes (src/cachier/core.py):

  • Added async detection via inspect.iscoroutinefunction()
  • Created parallel async execution path: _call_async(), _calc_entry_async(), _function_thread_async()
  • Decorator automatically returns async wrapper for coroutines, sync wrapper for regular functions
  • Removed blocking wait_on_entry_calc() in async path - concurrent calls execute in parallel to avoid event loop blocking
  • Storage layer (cores) remains synchronous - no changes needed
  • Simplified async stale entry handling by removing unreachable code path

Testing (tests/test_async_core.py):

  • 30 comprehensive tests (consolidated, no duplicates) covering async scenarios:
    • Basic caching with memory and pickle backends
    • stale_after, next_time, max_age parameters
    • Concurrent async calls and cache behavior
    • Class methods and different argument types
    • allow_none, ignore_cache, overwrite_cache parameters
    • Verbose mode and debug output
    • Global caching enable/disable control
    • Cleanup stale entries functionality
    • Exception handling in background tasks
    • Entry size limit exceeded
    • Edge cases: negative max_age, entry processing, stale entry handling
  • Backward compatibility validation (sync functions still work)
  • Test docstrings refer to behavior/case rather than line numbers
  • Clean test structure with class-level pytest marks where applicable

Examples (examples/async_example.py):

  • Practical demonstrations of async caching
  • HTTP request caching example
  • Stale-after and concurrent request scenarios

Features

All existing Cachier features work with async functions:

  • ✅ All backends: pickle, memory, MongoDB, Redis, SQL
  • ✅ Stale cache handling (stale_after, next_time)
  • ✅ Max age parameter for cache freshness
  • ✅ Cache control (ignore_cache, overwrite_cache)
  • ✅ Verbose mode for debugging
  • ✅ Global caching enable/disable
  • ✅ Automatic cleanup of stale entries
  • ✅ None value caching control (allow_none)
  • ✅ Exception handling in background tasks
  • ✅ Entry size limit handling

Usage

from cachier import cachier
import asyncio
from datetime import timedelta

@cachier(backend="memory", stale_after=timedelta(hours=1))
async def fetch_user(user_id: int) -> dict:
    async with httpx.AsyncClient() as client:
        response = await client.get(f"https://api.example.com/users/{user_id}")
        return response.json()

# First call: makes HTTP request, caches result
# Second call: returns cached result instantly
user = await fetch_user(42)

Design Notes

  • No Breaking Changes: All existing sync functionality unchanged
  • Async Efficiency: Concurrent async calls with identical arguments execute in parallel initially (no blocking wait), subsequent calls use cache
  • Event Loop Safety: wait_for_calc_timeout not honored for async functions to avoid blocking the event loop
  • Simple Implementation: Storage cores remain synchronous (just I/O operations), keeping implementation clean and maintainable
  • Code Simplification: Removed unreachable edge case code for cleaner async path
  • Clean Tests: Class-level pytest marks reduce duplication

Testing Results

  • ✅ All 30 async tests passing
  • ✅ All existing sync tests passing (backward compatibility verified)
  • ✅ Linting: 0 errors (ruff with 120 char line length)
  • ✅ Type checking: passing (mypy)
  • ✅ Security: 0 alerts (CodeQL)
  • ✅ Complete code coverage for all reachable async paths
  • ✅ No duplicate tests - consolidated and optimized test suite
  • ✅ No unreachable code in async paths
  • ✅ Clean test structure with minimal redundancy

The implementation is production-ready with comprehensive, non-redundant test coverage for all async code paths!

Original prompt

This section details on the original issue you should resolve

<issue_title>Support for coroutines (asyncio)</issue_title>
<issue_description>Hello,

I was trying to use cachier for caching the result of functions, in particular, HTTP requests.

I am using asyncio throughout my code. I noticed that cachier is fully synchronous, as seen here when calling _calc_entry.

It would be great to have support for caching result of coroutines, using await.

Maybe an async PickeCore / decorator? I am not clear on the best way to accomplish this with minimal code duplication, though.</issue_description>

Comments on the Issue (you are @copilot in this section)

@shaypal5 Hey,

That's an interesting use case. Yeah, I'm not sure myself. I think duplicating the decorator in a way that is agnostic to the core being used would be best, but this might also require rewriting the cores themselves to some extent. Also, it might simply be too complicated or plain impossible to have cores support this agnostically, and so each core would perhaps have to have a separate async implementation. But the pickle one is the important one anyway, I think.

I guess you'd have to figure things out a bit more before I can help you with this, as I really don't have the time to dive into this problem myself.</comment_new>
<comment_new>@shaypal5
I'll try to take a look at this in the coming weeks. This is a good time for this, since many new features are being introduced (now with AI increasing my coding output significantly).</comment_new>


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 4 commits January 27, 2026 07:57
Co-authored-by: Borda <6035284+Borda@users.noreply.github.com>
Co-authored-by: Borda <6035284+Borda@users.noreply.github.com>
Co-authored-by: Borda <6035284+Borda@users.noreply.github.com>
Co-authored-by: Borda <6035284+Borda@users.noreply.github.com>
Copilot AI changed the title [WIP] Add support for coroutines in cachier for async caching Add asyncio/coroutine support for caching async functions Jan 27, 2026
Copilot AI requested a review from Borda January 27, 2026 08:14
@Borda Borda merged commit a82fe54 into master Jan 30, 2026
78 of 80 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for coroutines (asyncio)

3 participants