URI Normalization Inconsistency Causes Workspace Cache Misses on Windows
Summary
pylsp-rope refactoring operations fail on Windows when the LSP client sends URIs with uppercase drive letters (RFC 8089 standard file:///C:/). The root cause is inconsistent URI normalization: uris.from_fs_path() normalizes drive letters to lowercase, but workspace.put_document() stores URIs without normalization, causing dictionary key mismatches.
Environment
- OS: Windows 10/11
- pylsp version: 1.14.0
- pylsp-rope version: 0.1.17
- LSP Client: Any client following RFC 8089 standard (uppercase drive letters)
Steps to Reproduce
- Setup: Use an LSP client that sends RFC 8089 compliant URIs with uppercase drive letters (
file:///C:/)
- Open: A Python file on Windows
- First refactoring: Perform any refactoring operation (e.g., "Extract Variable") - works fine
- DO NOT SAVE the file - leave it with unsaved changes from the refactoring
- Second refactoring: Attempt another refactoring operation on the same file
- Observe: Second refactoring fails because it operates on the old saved content from disk instead of the current unsaved content in workspace
Root Cause Analysis
The Problem
File: pylsp/uris.py (lines 135-137)
# Normalize drive paths to lower case
if RE_DRIVE_LETTER_PATH.match(path):
path = path[0] + path[1].lower() + path[2:]
This function normalizes Windows drive letters to lowercase (C: → c:).
File: pylsp/workspace.py
put_document() stores URIs exactly as received from client (NO normalization)
get_maybe_document() retrieves URIs exactly as provided (NO normalization)
The Flow That Breaks
- Client sends didOpen:
file:///C:/test.py (uppercase C, RFC 8089 standard)
- pylsp stores:
workspace._docs["file:///C:/test.py"] (uppercase, as received)
- pylsp-rope needs file content:
- Calls
uris.from_fs_path("C:\\test.py")
- Returns
file:///c:/test.py (lowercase c, normalized)
- Calls
workspace.get_maybe_document("file:///c:/test.py")
- Returns
None (key mismatch: "C:" vs "c:")
- Fallback: Reads from filesystem (stale content)
- Result: Refactoring operates on outdated code
Evidence from Logs
Server log shows the problem:
pylsp_rope.project - reading from filesystem: "C:\Users\...\file.py"
Instead of the expected:
pylsp_rope.project - reading from workspace: "C:\Users\...\file.py"
Client-server communication:
Client → Server (didOpen): file:///C:/Users/test.py
Server stores in _docs: file:///C:/Users/test.py (uppercase)
Rope generates URI: file:///c:/Users/test.py (lowercase)
Workspace lookup: _docs.get("file:///c:/...") → None
Impact
- Subsequent refactoring operations fail when file has unsaved changes from previous refactoring
- First refactoring works, but second/third/etc. fail because they operate on stale disk content
- Affects all Windows users using LSP clients that follow RFC 8089 (uppercase drive letters)
- Works by accident if client sends lowercase drive letters (non-standard)
- Silent failure: No error message, refactoring just produces incorrect results based on old content
Affected LSP Clients
- Any LSP client following RFC 8089 standard (uppercase drive letters)
- Potentially: Sublime Text, Vim/Neovim, Emacs, custom editors
- VSCode likely unaffected (may send lowercase URIs)
Cross-Platform Considerations
⚠️ CRITICAL: Any fix must preserve case-sensitivity on Linux/macOS where filesystems are case-sensitive. The normalization should ONLY affect Windows drive letters (C: vs c:), not entire paths.
Safe on Windows: C:\Users\File.py and c:\users\file.py refer to the same file (case-insensitive filesystem)
NOT safe on Linux: /home/User/File.py and /home/user/file.py are DIFFERENT files (case-sensitive filesystem)
Workaround for Users
LSP client developers can work around this by normalizing drive letters to lowercase before sending URIs to pylsp:
# Client-side workaround (Python example)
if sys.platform == 'win32' and uri[8:9].isupper():
uri = f"{uri[:8]}{uri[8].lower()}{uri[9:]}"
However, this violates RFC 8089 standard and should not be necessary.
Related Issues
This may also affect other pylsp functionality that relies on workspace document cache, not just pylsp-rope.
References
Thank you for maintaining pylsp! This is a subtle but impactful bug that affects Windows users following LSP standards. Happy to provide additional information or testing if needed.