Skip to content

fix: cross-platform config locking (Windows support)#51

Merged
jdwit merged 2 commits into
mainfrom
fix/windows-fcntl-cross-platform-lock
Jun 30, 2026
Merged

fix: cross-platform config locking (Windows support)#51
jdwit merged 2 commits into
mainfrom
fix/windows-fcntl-cross-platform-lock

Conversation

@jdwit

@jdwit jdwit commented Jun 30, 2026

Copy link
Copy Markdown
Owner

Fixes #49.

Problem

Since the v0.2.0 profiles refactor, config.py does import fcntl at module top level. fcntl is Unix-only, so on Windows the import raises ModuleNotFoundError before any command can run. config.py is imported transitively by everything (main.py -> api.py -> config.py), so the entire CLI is unusable on Windows from v0.2.0 through v0.4.1. Last working release on Windows was v0.1.1.

Thanks to @leotulipan for the detailed report and root-cause analysis.

Fix (stdlib only, no new dependency)

  • Import fcntl and msvcrt conditionally; either may be None.
  • Route _config_lock() through _lock_file / _unlock_file helpers: fcntl.flock on POSIX, msvcrt.locking on Windows, graceful no-op if neither is available (acceptable for a single-user CLI).
  • Guard chmod(0o700) in _ensure_private_dir behind os.name == "posix" (on Windows chmod only toggles the read-only bit, so the owner-only guarantee is POSIX-only by nature).
  • The os.open(..., 0o600/0o644) mode bits in the atomic writers are already silently ignored on Windows, so they need no change.

Tests

Added TestCrossPlatformLock:

  • lock round-trips on the host platform,
  • with fcntl patched to None the lock routes to msvcrt.locking (LK_LOCK / LK_UNLCK, 1 byte),
  • with neither primitive present the lock degrades to a no-op instead of crashing,
  • a real set_active_profile mutation succeeds on the simulated-Windows path.

Full suite: 300 passed, ruff clean. (CI on a Windows runner would be the ideal follow-up to lock this in for good.)

The profiles refactor (v0.2.0) added a top-level `import fcntl` to
config.py. fcntl is Unix-only, so on Windows the import failed with
ModuleNotFoundError before any command could run (config.py is imported
transitively by every command).

Import fcntl/msvcrt conditionally and route _config_lock() through small
_lock_file/_unlock_file helpers: fcntl.flock on POSIX, msvcrt.locking on
Windows, no-op degrade if neither is present. Guard the chmod(0o700) in
_ensure_private_dir behind os.name == 'posix' since Windows chmod only
toggles the read-only bit. The os.open mode bits in the atomic writers are
already ignored on Windows, so they need no change.

Fixes #49
@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.50000% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.28%. Comparing base (c4816e1) to head (636834a).

Files with missing lines Patch % Lines
src/ytstudio/config.py 87.50% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main      #51   +/-   ##
=======================================
  Coverage   84.28%   84.28%           
=======================================
  Files          15       15           
  Lines        2500     2520   +20     
  Branches      367      372    +5     
=======================================
+ Hits         2107     2124   +17     
- Misses        262      264    +2     
- Partials      131      132    +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jdwit jdwit merged commit df97bd4 into main Jun 30, 2026
8 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.

import fcntl makes the CLI completely unusable on Windows (regression in v0.2.0)

1 participant