Skip to content

馃悰 fix(singleton): reject conflicting async and lease options - #765

Merged
gaborbernat merged 3 commits into
tox-dev:mainfrom
kokotatan:fix-async-singleton-options
Oct 3, 2026
Merged

gaborbernat merged 3 commits into
tox-dev:mainfrom
kokotatan:fix-async-singleton-options

Conversation

@kokotatan

@kokotatan kokotatan commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Reusing a singleton lock ignored every option a subclass adds on top of the base ones. AsyncFileLock kept the first loop, executor and run_in_executor it got, and SoftFileLease kept the first lease_duration, heartbeat_interval and on_compromise:

from filelock import AsyncFileLock

first = AsyncFileLock("example.lock", is_singleton=True, run_in_executor=False)
second = AsyncFileLock("example.lock", is_singleton=True, run_in_executor=True)
assert first is second
assert second.run_in_executor is False

The documented singleton contract says conflicting parameters raise ValueError. The metaclass sees these options as plain **kwargs, and the subclass alone knows their defaults and how its __init__ resolves them, so each class reports its own mismatches from _singleton_extra_mismatches and merges what super() returns. 馃敀 That lets AsyncSoftFileLease check its lease and async options together. Loops, executors and callbacks compare by identity, as on_acquired does, and an omitted heartbeat_interval resolves to a third of the duration before the comparison.

AI assistance: implemented and tested with Codex, then independently reviewed with Codex and Claude Opus.

@gaborbernat gaborbernat changed the title Reject conflicting async singleton options 馃悰 fix(singleton): reject conflicting async and lease options Oct 3, 2026
@gaborbernat
gaborbernat force-pushed the fix-async-singleton-options branch from 36abf77 to 0cc652e Compare October 3, 2026 13:42
@gaborbernat
gaborbernat enabled auto-merge (squash) October 3, 2026 16:42
The base metaclass forwards a subclass's own options as kwargs and never
compared them on reuse, so the async-only hook left SoftFileLease and
AsyncSoftFileLease returning the cached lock for a conflicting
lease_duration, heartbeat_interval or on_compromise. SoftFileLease now
checks those, resolving an omitted heartbeat the way __init__ does, and
both overrides chain through super() so AsyncSoftFileLease checks its
lease and async options together.

The hook takes the metaclass's own _ExtraValue kwargs and returns each
mismatch as the two strings the error message prints, so no annotation
falls back to object for the mixed loop, executor, callback and float
values.

The tests drive one table per lock family through all three async lock
types and both lease types, check reuse with matching options, and drop
the assertions that could not fail on the original bug.
@gaborbernat
gaborbernat force-pushed the fix-async-singleton-options branch from 0cc652e to f441db1 Compare October 3, 2026 17:04
@gaborbernat
gaborbernat merged commit c9c65ab into tox-dev:main Oct 3, 2026
47 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.

2 participants