Skip to content

Add __len__ and __bool__ to cache iterators - #74

Merged
awolverp merged 4 commits into
awolverp:mainfrom
Malkiz223:iterator_len_bool
Aug 10, 2026
Merged

Add __len__ and __bool__ to cache iterators#74
awolverp merged 4 commits into
awolverp:mainfrom
Malkiz223:iterator_len_bool

Conversation

@Malkiz223

Copy link
Copy Markdown
Contributor

Closes #73

The iterator types behind keys(), values() and items() get __len__ and __bool__. len() says how many items the iterator will yield: for a fresh cache.keys() that is the number of live entries, the same number a dict view would report, and it goes down as a stored iterator is consumed, like any Python iterator. operator.length_hint() picks it up automatically, and bool() of an empty iterator becomes False instead of True, which is the point of the change.

On Cache, FIFOCache, LRUCache, LFUCache and RRCache the count is O(1). On TTLCache and VTTLCache it walks the entries and skips the expired ones, so it is O(n): the number has to match what the walk actually yields. There len(cache.keys()) can be smaller than len(cache), because an expired entry can sit behind a live one, and only the walk skips it. cachetools counts cheaper because its update moves the entry to the back, so everything expired sits at the head. In cachebox an update refreshes expires_at in place, so an exact count is either a walk on every len(), or a separate counter paid for on every insert. I picked the walk: len() is called rarely, inserts happen all the time.

If the cache changed after the iterator was created, len() and bool() raise the same RuntimeError that __next__ already raises: the iterator's existing rule extended to the new methods, not a new restriction.

Six new tests: four in the shared mixin, so they run for all seven types, and two for the expired-entry cases on TTLCache and VTTLCache.

@awolverp

awolverp commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Thank you. There's a lot of changes, I’ll review and merge them very soon. I have doubts about some things and need to check them.
for example, if the __next__ method is called and then __len__ is called, does it return the correct value?

In cases where the RawVecDequeIter struct is used, I have doubts about this.

Comment thread cachebox/_core.pyi Outdated
Comment thread cachebox/_core.pyi Outdated
Malkiz223 and others added 3 commits August 10, 2026 00:06
@Malkiz223

Copy link
Copy Markdown
Contributor Author

Thanks for the quick reply and for the trust! Please take your time, there is no rush at all.

The doubt is fair, and that exact case was not covered, so I have just added a test for it: test_iterator_len_correct_across_ring_wrap for FIFOCache and TTLCache, the only two users of RawVecDequeIter. It overfills the cache so the ring buffer wraps and both halves are in use, then checks len() after every single next(). Check it whenever it is convenient.

@awolverp
awolverp merged commit e7a0785 into awolverp:main Aug 10, 2026
12 checks passed
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.

len() raises TypeError and bool() is always True on keys()/values()/items()

3 participants