fix imports using symlinked paths are not updated #1336 - #3029
asukaminato0721 wants to merge 7 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes LSP + import-resolution behavior when Python modules are imported via symlinked paths, ensuring edits to the real (target) file propagate through symlinked imports and that retargeted symlinks invalidate cached import finding.
Changes:
- Canonicalize symlinked filesystem hits during module finding so imports resolve to the real target path.
- Prefer in-memory (open/unsaved) contents when the resolved path (or its canonical target) is open.
- Invalidate import finding when a watched symlink path is modified, and add regression tests for symlink behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| pyrefly/lib/test/lsp/lsp_interaction/did_change.rs | Adds an LSP regression test ensuring didChange updates diagnostics through symlinked imports. |
| pyrefly/lib/state/state.rs | Prefers memory-backed module paths for imports and invalidates find-cache on symlink modification events. |
| pyrefly/lib/module/finder.rs | Canonicalizes found module/package paths and adds a unit test for resolving symlink modules to their real paths. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let root = tempfile::tempdir().unwrap(); | ||
| let module_path = root.path().join("test_module.py"); | ||
| let symlink_path = root.path().join("sym.py"); | ||
| let importer_path = root.path().join("test_import.py"); | ||
| std::fs::write(&module_path, "def hello(name: str) -> None:\n pass\n").unwrap(); | ||
| symlink(&module_path, &symlink_path).unwrap(); | ||
| std::fs::write(&importer_path, "from sym import hello\nhello(\"John\")\n").unwrap(); | ||
|
|
||
| let mut interaction = LspInteraction::new_with_indexing_mode(IndexingMode::LazyBlocking); | ||
| interaction.set_root(root.path().to_path_buf()); | ||
| interaction | ||
| .initialize(InitializeSettings { | ||
| configuration: Some(Some( | ||
| json!([{"pyrefly": {"displayTypeErrors": "force-on"}}]), | ||
| )), | ||
| workspace_folders: Some(vec![( | ||
| "test".to_owned(), | ||
| Url::from_file_path(root.path()).unwrap(), | ||
| )]), | ||
| ..Default::default() | ||
| }) | ||
| .unwrap(); |
There was a problem hiding this comment.
This test constructs the tempdir root and opens files using root.path() as-is. If import resolution canonicalizes symlink paths (and canonicalization rewrites the tempdir path on some platforms, e.g. macOS), the in-memory file key may not match the canonicalized import target and the test can fail intermittently by platform. Using a canonicalized root path consistently for file creation + set_root/workspace folder URIs will make the test robust.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
This pull request has been imported. If you are a Meta employee, you can view this in D115442175. (Because this pull request was imported automatically, there will not be any future comments.) |
This comment has been minimized.
This comment has been minimized.
stroxler
left a comment
There was a problem hiding this comment.
Review automatically exported from Phabricator review in Meta.
|
Back to you for a look at the CI failures before we review in more detail |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
stroxler
left a comment
There was a problem hiding this comment.
Review automatically exported from Phabricator review in Meta.
624fae7 to
a51f9bf
Compare
This comment has been minimized.
This comment has been minimized.
a51f9bf to
499aa2d
Compare
|
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅ |
|
This pull request has been merged in 720e0cd. |
|
I reverted this change internally. This PR seemed to include an incidental change (the "live LSP bug" mentioned in the summary) which changed intended behavior. Pyrefly intentionally avoids propagating changes until didSave to avoid performing expensive cross-file checks on every keystroke. |
|
I reopened the issue, since I think the actual symlink problem we meant to solve is not connected to the undesired didChange behavior |
Summary
Fixes #1336
The change has two parts.
Import discovery now canonicalizes symlinked filesystem hits, so import sym resolves through the real target path in the finder.
for the live LSP bug, imported modules now prefer the open in-memory file if the resolved path itself or its canonical target is open, which makes unsaved edits in the real file propagate through symlinked imports.
also invalidate import finding when a watched symlink path changes so retargeted symlinks do not stay cached.
Test Plan
add test