Fix rx.asset(shared=True) crash on Windows without Developer Mode

AP
5d ago
+233-18

All Submissions:

  • Have you followed the guidelines stated in CONTRIBUTING.md file?
  • Have you checked to ensure there aren't any other open Pull Requests for the desired changed?

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Description

On Windows without Developer Mode (or admin rights), creating a symlink raises OSError: [WinError 1314] A required privilege is not held by the client. _link_shared_asset handles Windows rename races meticulously but not symlink-creation denial, so rx.asset(path="x.js", shared=True) — reached from any component render — crashes the whole app compile on a default Windows machine. Reproduced on Windows 10 x64, Python 3.12, current main:

OSError: [WinError 1314] A required privilege is not held by the client: '...src.js' -> '...\.e79838f0...tmp'

No GitHub issue found for this; the docs make no mention of a Windows requirement.

Fix

When symlink creation is denied, stage the link as a hardlink (shares the source content, no privileges needed on the same drive), and as a copy when that is also denied (source on another drive). The existing atomic staged-rename and retry logic is unchanged — it operates on tmp_file either way.

_links_to is extended so the idempotency guarantee survives the fallbacks: a hardlink matches by inode (Path.samefile), a copy by content digest (_content_digest). That preserves two behaviors callers rely on: a second link is a no-op so file watchers see no change, and an edited source re-copies on the next compile instead of serving stale content forever.

Tests

3 new tests in tests/units/assets/test_assets.py, all platform-independent (symlink denial is simulated by monkeypatching os.symlink, matching the file's existing competing-process pattern):

  • test_link_shared_asset_hardlink_fallback — content shared, idempotent second call (inode unchanged). Failed before this PR (OSError 1314 propagates), passes after.
  • test_link_shared_asset_copy_fallback — copy created when os.link is also denied. Failed before (same), passes after.
  • test_link_shared_asset_copy_refreshes_edited_source — edited source re-copies. Failed before, passes after.

Validated end-to-end on a real Windows machine without Developer Mode: _link_shared_asset previously raised 1314, now produces a working hardlink; 5 previously-failing tests in the file (which link through the product path) now pass locally too. The 12 remaining local failures all create symlinks inside the tests themselves and pass on Linux CI.

  • ruff check / ruff format --check clean; pyright clean.

Docs

docs/assets/overview.md alert updated with the Windows fallback behavior.

Changes To Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

After these steps, you're ready to open a pull request.

a. Descriptive title above.
b. Description above.
c. No linked issue — self-found via code inspection on Windows.