Skip to content

fix(locking): use portalocker for cross-platform advisory locks#15

Draft
abpatramsft wants to merge 1 commit intoevo-hq:mainfrom
abpatramsft:windows-locking-fix
Draft

fix(locking): use portalocker for cross-platform advisory locks#15
abpatramsft wants to merge 1 commit intoevo-hq:mainfrom
abpatramsft:windows-locking-fix

Conversation

@abpatramsft
Copy link
Copy Markdown

Summary

  • Swaps fcntl.flock in plugins/evo/src/evo/locking.py for portalocker, which dispatches to fcntl.flock on POSIX and LockFileEx on Windows — fixing the ModuleNotFoundError: No module named 'fcntl' that blocks evo --version and every downstream command on Windows (Not working on Windows #14).
  • Adds portalocker>=2.8.0 to plugins/evo/pyproject.toml and regenerates plugins/evo/uv.lock.
  • Adds tests/unit/test_locking.py covering acquire/release, timeout under contention, and cross-thread reacquire.

Why portalocker over conditional fcntl/msvcrt

portalocker is the de-facto standard cross-platform file-locking library (pip itself depends on it) and lets the lock call site stay a single code path. The alternative — a sys.platform == "win32" branch importing msvcrt.locking — saves one dependency at the cost of doubling the code and forcing any future contributor to reason about two locking APIs. Happy to switch to the conditional-import approach if you'd prefer to keep the dep list minimal.

Semantics preserved

The exclusive, non-blocking, bounded-polling behavior is identical to the pre-patch implementation:

  • portalocker.LOCK_EX | portalocker.LOCK_NB mirrors fcntl.LOCK_EX | fcntl.LOCK_NB
  • portalocker.LockException replaces BlockingIOError in the retry loop
  • LockTimeoutError (the public contract) is unchanged — callers catching it keep working

Test plan

  • python3 tests/unit/test_locking.py — 3/3 pass on Windows 11 / Python 3.10
  • python3 tests/unit/test_core.py — 7/7 still pass (no regression)
  • evo --version runs cleanly on Windows (previously crashed on import)
  • from evo import dashboard + static-asset smoke check from ci.yml still passes
  • POSIX validation — haven't run on Linux/macOS; portalocker's POSIX path is fcntl.flock, so behavior should be byte-identical, but worth a reviewer check

Not in this PR

  • Windows CI matrix. .github/workflows/ci.yml currently pins ubuntu-latest and uses bash-specific shell (unzip, /tmp/smoke). Adding windows-latest is doable but is a larger diff touching the smoke-test step — happy to send it as a follow-up PR once this lands, since the locking fix is the blocker.
  • uv.lock regeneration. Pulled in via uv lock after adding the dep; includes transitive pywin32 on Windows platform markers. No version changes to existing deps.

Refs #14

Replace the fcntl-only implementation with portalocker, which dispatches
to fcntl.flock on POSIX and LockFileEx on Windows. Preserves the
existing exclusive/non-blocking/bounded-polling semantics and the
LockTimeoutError contract, so all callers keep working unchanged.

Closes the import-time crash on Windows where evo --version (and every
downstream command) failed with ModuleNotFoundError: No module named
'fcntl'.

Adds tests/unit/test_locking.py covering acquire/release, timeout under
contention, and cross-thread reacquire. Passes on Windows 11 / Python
3.10.

Refs evo-hq#14

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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