Skip to content

Bugfixes#23

Merged
claudious96 merged 6 commits into
mainfrom
fix/various-0.2.2
Apr 29, 2026
Merged

Bugfixes#23
claudious96 merged 6 commits into
mainfrom
fix/various-0.2.2

Conversation

@claudious96

@claudious96 claudious96 commented Apr 29, 2026

Copy link
Copy Markdown
Owner

This PR fixes:

  1. (calendar-queue) count of _unfinished_tasks not being updated after calling delete_items
  2. (calendar-queue) fix unnecessary timer recreation when queue has one item
  3. (calendar) document stop() irreversibility and add reset()
  4. (tests) fix fragile loop.time() mock in test_far_schedule
  5. (tests) add test for Calendar.reset()
  6. (actions) increase too strict tolerance causing CI failures

@codecov

codecov Bot commented Apr 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
src/calendar_queue/calendar.py 94.00% <100.00%> (+0.25%) ⬆️
src/calendar_queue/calendar_queue.py 85.47% <100.00%> (+0.51%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Each put_nowait increments _unfinished_tasks.  delete_items removed items
from the heap without ever decrementing the counter, so queue.join() would
block forever if any items were deleted before being consumed.

Decrement _unfinished_tasks by the number of deleted items (clamped to the
current counter value) and set _finished when the count reaches zero,
matching the behaviour of task_done().
_update_timer guarded the timestamp comparison with 'qsize() > 1', so
whenever there was exactly one item in the queue it always cancelled and
recreated the timer, even if it was already set for the correct time.

The qsize check was never load-bearing: the only thing that matters is
whether the existing timer is still pending (when() > loop.time()).  Drop
the condition so the comparison runs for any queue size.
@claudious96 claudious96 changed the title Docs and fixes for 0.2.2 Bugfixes Apr 29, 2026
stop() permanently sets the internal asyncio.Event, causing all future
iterations to raise StopAsyncIteration immediately.
This was undocumented and left no way to reuse a Calendar instance after
stopping.

Add reset() which clears the event, re-enabling iteration.
The previous approach patched loop.time() with a fixed return_value.
Because asyncio.wait_for and call_later both rely on loop.time() for
their own scheduling, a constant mock breaks those internals. The test
only passed because cq.get() happened to short-circuit before any
timeout logic ran.

Replace the patch with a direct manipulation of the internal timer:
cancel the far-future handle and schedule a new one at delay=0, which
fires on the next event loop tick.
This simulates the item becoming due without touching asyncio's clock.
Verifies that after stop() is called mid-iteration, further iteration is
immediately blocked, and that reset() re-enables the async iterator so
remaining scheduled events are emitted correctly.
Signed-off-by: Claudio Usai <claudio.usai@stardustsystems.net>
@claudious96 claudious96 merged commit bdf59f9 into main Apr 29, 2026
29 checks passed
@claudious96 claudious96 deleted the fix/various-0.2.2 branch April 29, 2026 13:03
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.

1 participant