Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests for hard and soft links #12590

Merged
merged 2 commits into from
Aug 2, 2024
Merged

Add tests for hard and soft links #12590

merged 2 commits into from
Aug 2, 2024

Conversation

MichaReiser
Copy link
Member

@MichaReiser MichaReiser commented Jul 31, 2024

Summary

The goal I set out for was to add support for symlinks and hard links when watching files. I no longer think we should pursue this goal, at least not now. This PR has mainly become a documentation PR and it adds a few tests demonstrating the behavior on different systems/platforms.

Why not support watching symlinks and hardlinks? Primarily, because I don't think there's sufficient use to justify the complexity. I tested both VS Code and Pycharm and both show stale results after making changes to a hard-linked file or updating a symlinked file. This is as good as Red Knot is today. Specifically, the following changes are not supported:

  • Changing a symlinked file only updates the symlink target or source, but not both
  • Changing a hardlinked file only updates the specific file to which the change was written. Creating or removing hard links are regular file events and they are supported on all platforms (this is important because uv uses hardlinks)

The other reason is that the platform specific file watcher APIs don't generate the necessary events. I compared the events with @parcel/watcher (used by VS Code, and parcel), and fs.watch (nodejs) and the generated events match. The only system that I found that supported hard links well is Pyright which is based on chokidar. I'm not a 100% sure how chokidar makes it work but I belive it does a directory traversal to find changes rather than relying on the events only. We could do that as well, but it seems rather expensive to support a not so common workflow.

There are ways how we could support symlinks and hardlinks without traversing the directory.

  • We could keep a map from Handle to Vec<File> to lookup all files pointing to the same hard link. There's some complexity how we would support this with our System abstraction
  • We could keep a reverse map from real_path to Vec<File> for all symlinked files.

I think any approach (or traversing the directory to find changes) is worth exploring if this is something that users run into often.

It is probably also worth exploring watchman as a possible notify backend to support very large projects.

Side note: Ruff doesn't follow symlinks today. Pyright has support for it.

Test Plan

This PR is only tests 😆

@MichaReiser MichaReiser force-pushed the watcher-link-tests branch 5 times, most recently from af1f272 to 08adb19 Compare July 31, 2024 10:31
Copy link
Contributor

github-actions bot commented Jul 31, 2024

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

@MichaReiser MichaReiser force-pushed the watcher-link-tests branch 2 times, most recently from 21696c5 to a8ec90d Compare July 31, 2024 12:45
@MichaReiser MichaReiser added the red-knot Multi-file analysis & type inference label Jul 31, 2024
@MichaReiser MichaReiser force-pushed the watcher-link-tests branch 4 times, most recently from 43c98c9 to 084c2a3 Compare July 31, 2024 13:44
@MichaReiser MichaReiser marked this pull request as ready for review July 31, 2024 13:48
@@ -1153,7 +1151,9 @@ mod tests {
#[test]
#[cfg(target_family = "unix")]
fn symlink() -> anyhow::Result<()> {
use crate::db::tests::TestDb;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes a few unused warnings on windows

.expect("Expected watch changes but observed none.")
}

fn try_stop_watch(&mut self, timeout: Duration) -> Option<Vec<watch::ChangeEvent>> {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope this will help to improve the stability of the tests. We can now use a high timeout for functions that expect at least a single event and a smaller timeout for functions that don't.

fn next_io_tick() {
let temp = tempfile::tempfile().unwrap();
/// Updates the content of a file and ensures that the last modified file time is updated.
fn update_file(path: impl AsRef<SystemPath>, content: &str) -> anyhow::Result<()> {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's hope this fixes the other instability

Copy link
Member

@AlexWaygood AlexWaygood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of this I've only skimmed, but it LGTM overall. I have a few questions and a few nits:

crates/red_knot/tests/file_watching.rs Show resolved Hide resolved
crates/red_knot/tests/file_watching.rs Outdated Show resolved Hide resolved
crates/red_knot/tests/file_watching.rs Outdated Show resolved Hide resolved
crates/red_knot/tests/file_watching.rs Outdated Show resolved Hide resolved
crates/red_knot/tests/file_watching.rs Outdated Show resolved Hide resolved
crates/red_knot/tests/file_watching.rs Outdated Show resolved Hide resolved
crates/red_knot/tests/file_watching.rs Outdated Show resolved Hide resolved
crates/red_knot/tests/file_watching.rs Outdated Show resolved Hide resolved
crates/red_knot/tests/file_watching.rs Outdated Show resolved Hide resolved
crates/red_knot/tests/file_watching.rs Outdated Show resolved Hide resolved
crates/red_knot/tests/file_watching.rs Outdated Show resolved Hide resolved
crates/red_knot/tests/file_watching.rs Outdated Show resolved Hide resolved
@MichaReiser MichaReiser enabled auto-merge (squash) August 2, 2024 10:09
@MichaReiser MichaReiser merged commit 966563c into main Aug 2, 2024
18 checks passed
@MichaReiser MichaReiser deleted the watcher-link-tests branch August 2, 2024 10:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
red-knot Multi-file analysis & type inference
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants