fix: add __exit__ to BaseAsyncFileLock and fix stored event loop in __del__#518
fix: add __exit__ to BaseAsyncFileLock and fix stored event loop in __del__#518naarob wants to merge 3 commits intotox-dev:mainfrom
Conversation
gaborbernat
left a comment
There was a problem hiding this comment.
Please add tests that would catch a regression of this.
…ed loop __del__)
10 parametrised tests covering both bugs fixed in this PR:
Bug 1 — missing __exit__ on BaseAsyncFileLock:
- test_sync_with_raises_not_implemented_error_not_attribute_error
Uses `with` (sync) on an async lock; must raise NotImplementedError,
not AttributeError (which happened when __exit__ was absent).
- test_exit_method_exists_on_async_lock
Asserts that __exit__ is defined and callable on the class.
- test_exit_raises_not_implemented_error
Calls __exit__ directly; must raise NotImplementedError.
Bug 2 — stored event loop in __del__:
- test_del_after_loop_close_does_not_raise
Acquires/releases in a dedicated thread with its own loop, closes
the loop, then calls __del__ — must not raise RuntimeError.
- test_gc_after_loop_close_does_not_raise
Same scenario via gc.collect() to simulate real GC behaviour.
Both test groups run for AsyncFileLock and AsyncSoftFileLock.
Uses threading to isolate the test loop from pytest-asyncio's loop.
338 upstream tests pass.
|
Added regression tests in Bug 1 — missing
Bug 2 — stored event loop in
Both groups run for |
for more information, see https://pre-commit.ci
|
CI failing you need to fix the linter too. |
|
Thanks for the feedback @gaborbernat — the CI failure and the review are valid. Root cause of the type failure The original fix added Fix applied Added an explicit def __exit__(self, exc_type, exc_value, traceback) -> NoReturn:
msg = "Do not use `with` for asyncio locks, use `async with` instead."
raise NotImplementedError(msg)Tests The regression suite ( Pushing the corrected |
|
Hey, just wanted to let you know that the CI is still failing. |
Summary
Two issues fixed in
src/filelock/asyncio.py:Fix 1 —
BaseAsyncFileLock.__exit__was missingBaseAsyncFileLockdefines__enter__to raiseNotImplementedErrorwith a helpful message ('Do not use \with` for asyncio locks, use `async with` instead'). However,exit` was not defined.Problem: Python checks for
__exit__before calling__enter__. Without__exit__, users see an opaqueAttributeErrorinstead of the helpfulNotImplementedErrormessage.Fix: Add
__exit__that raisesNotImplementedErrorwith the same message, symmetrically.Fix 2 — Stored event loop causes
RuntimeErrorin__del__BaseAsyncFileLock.__del__usedself.loop or asyncio.get_running_loop(). If the stored loop (self.loop) is closed — which happens when reusing a lock instance across different event loops — this raisesRuntimeError: Event loop is closedduring garbage collection.Fix: Use
asyncio.get_running_loop()with a proper fallback to the stored loop (only if not closed), and suppress all exceptions in__del__since GC cleanup must never raise.Confirmed with regression test: 338/338 tests pass, 35 skipped.