Skip to content

feat: add cache_contains() for read-only key lookup#746

Merged
rodrigobnogueira merged 3 commits intoaio-libs:masterfrom
rahulkaushal04:feature/cache-contains
Mar 16, 2026
Merged

feat: add cache_contains() for read-only key lookup#746
rodrigobnogueira merged 3 commits intoaio-libs:masterfrom
rahulkaushal04:feature/cache-contains

Conversation

@rahulkaushal04
Copy link
Copy Markdown
Contributor

@rahulkaushal04 rahulkaushal04 commented Mar 4, 2026

Add cache_contains() method for checking if arguments are cached

What do these changes do?

This adds a cache_contains(*args, **kwargs) -> bool method to alru_cache. It lets you check whether a specific set of arguments is currently in the cache. It returns True if the key exists and False otherwise. It does not count as a hit or miss, does not trigger execution, and does not change LRU ordering.

Are there changes in behavior for the user?

This is a purely additive change. No existing behavior is modified. Users who do not use cache_contains are completely unaffected.

For users who need this, it replaces two clunky workarounds that both have side effects:

Before: cache_info() gives a total count, not per-key info

from async_lru import alru_cache

@alru_cache(maxsize=128)
async def get_user(user_id):
    return {"id": user_id}

await get_user(1)
await get_user(2)
await get_user(3)

info = get_user.cache_info()
print(info.currsize)  # 3, but is user 2 one of them? No way to tell.

Before: cache_invalidate() removes the entry as a side effect

from async_lru import alru_cache

@alru_cache(maxsize=128)
async def get_user(user_id):
    return {"id": user_id}

await get_user(42)
was_cached = get_user.cache_invalidate(42)  # True, but now the entry is gone
print(was_cached)                            # True
print(get_user.cache_info().currsize)        # 0 - entry was removed

After: clean read-only lookup

from async_lru import alru_cache

@alru_cache(maxsize=128)
async def get_user(user_id):
    return {"id": user_id, "name": f"User {user_id}"}

await get_user(42)

print(get_user.cache_contains(42))   # True
print(get_user.cache_contains(99))   # False, nothing happens

print(get_user.cache_info())
# CacheInfo(hits=0, misses=1, maxsize=128, currsize=1)  counters unchanged

# Pre-warm only keys that are not already cached
active_users = [1, 2, 42, 3]
for uid in active_users:
    if not get_user.cache_contains(uid):
        await get_user(uid)

print(get_user.cache_info().currsize)  # 4

Works on instance methods too:

from async_lru import alru_cache

class UserService:
    @alru_cache(maxsize=64)
    async def fetch(self, user_id):
        return {"id": user_id}

svc = UserService()
await svc.fetch("abc")

print(svc.fetch.cache_contains("abc"))  # True
print(svc.fetch.cache_contains("xyz"))  # False

Related issue number

None. This is a new feature proposal with implementation included.

Checklist

  • I think the code is well written
  • Unit tests for the changes exist
  • Documentation reflects the changes

Comment thread tests/test_cache_contains.py
Comment thread tests/test_cache_contains.py Outdated
@rodrigobnogueira
Copy link
Copy Markdown
Member

Hi @rahulkaushal04 . Could you please add some documentation to the README.rst file? Probably something similar to the cache_invalidate example.

Comment thread tests/test_cache_contains.py Outdated
@rahulkaushal04
Copy link
Copy Markdown
Contributor Author

Hi @rodrigobnogueira, I have addressed all the review feedback:

  • Fixed the Flake8 I201 import group separator
  • Added type hints (Callable[..., None] / -> None) to all test functions
  • Removed the extra blank line in test_cache_contains_respects_typed_flag
  • Added cache_contains() documentation to README.rst (similar to the cache_invalidate example)

Could you please take another look? Thanks!

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 16, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.78%. Comparing base (e2ddf7a) to head (252aa4f).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #746      +/-   ##
==========================================
+ Coverage   97.59%   97.78%   +0.18%     
==========================================
  Files          15       16       +1     
  Lines        1081     1172      +91     
  Branches       60       61       +1     
==========================================
+ Hits         1055     1146      +91     
  Misses         23       23              
  Partials        3        3              
Flag Coverage Δ
unit 96.84% <100.00%> (+0.26%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Mar 16, 2026

Merging this PR will not alter performance

✅ 63 untouched benchmarks
⏩ 4 skipped benchmarks1


Comparing rahulkaushal04:feature/cache-contains (252aa4f) with master (e2ddf7a)

Open in CodSpeed

Footnotes

  1. 4 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@rodrigobnogueira rodrigobnogueira merged commit 63760a4 into aio-libs:master Mar 16, 2026
27 checks passed
@rodrigobnogueira
Copy link
Copy Markdown
Member

Thanks @rahulkaushal04 👏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants