mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 18:28:24 +00:00
Fix file watching on macOS if a module-search path is a symlink (#12634)
This commit is contained in:
parent
38e178e914
commit
341a25eec1
11 changed files with 158 additions and 91 deletions
|
@ -43,6 +43,16 @@ pub trait System: Debug {
|
|||
/// This function will traverse symbolic links to query information about the destination file.
|
||||
fn path_metadata(&self, path: &SystemPath) -> Result<Metadata>;
|
||||
|
||||
/// Returns the canonical, absolute form of a path with all intermediate components normalized
|
||||
/// and symbolic links resolved.
|
||||
///
|
||||
/// # Errors
|
||||
/// This function will return an error in the following situations, but is not limited to just these cases:
|
||||
/// * `path` does not exist.
|
||||
/// * A non-final component in `path` is not a directory.
|
||||
/// * the symlink target path is not valid Unicode.
|
||||
fn canonicalize_path(&self, path: &SystemPath) -> Result<SystemPathBuf>;
|
||||
|
||||
/// Reads the content of the file at `path` into a [`String`].
|
||||
fn read_to_string(&self, path: &SystemPath) -> Result<String>;
|
||||
|
||||
|
|
|
@ -94,6 +94,10 @@ impl MemoryFileSystem {
|
|||
metadata(self, path.as_ref())
|
||||
}
|
||||
|
||||
pub fn canonicalize(&self, path: impl AsRef<SystemPath>) -> SystemPathBuf {
|
||||
SystemPathBuf::from_utf8_path_buf(self.normalize_path(path))
|
||||
}
|
||||
|
||||
pub fn is_file(&self, path: impl AsRef<SystemPath>) -> bool {
|
||||
let by_path = self.inner.by_path.read().unwrap();
|
||||
let normalized = self.normalize_path(path.as_ref());
|
||||
|
|
|
@ -63,6 +63,12 @@ impl System for OsSystem {
|
|||
})
|
||||
}
|
||||
|
||||
fn canonicalize_path(&self, path: &SystemPath) -> Result<SystemPathBuf> {
|
||||
path.as_utf8_path()
|
||||
.canonicalize_utf8()
|
||||
.map(SystemPathBuf::from_utf8_path_buf)
|
||||
}
|
||||
|
||||
fn read_to_string(&self, path: &SystemPath) -> Result<String> {
|
||||
std::fs::read_to_string(path.as_std_path())
|
||||
}
|
||||
|
|
|
@ -733,26 +733,30 @@ impl ruff_cache::CacheKey for SystemVirtualPathBuf {
|
|||
/// let paths = vec![SystemPath::new("/a/b/c"), SystemPath::new("/a/b"), SystemPath::new("/a/beta"), SystemPath::new("/a/b/c")];
|
||||
/// assert_eq!(deduplicate_nested_paths(paths).collect::<Vec<_>>(), &[SystemPath::new("/a/b"), SystemPath::new("/a/beta")]);
|
||||
/// ```
|
||||
pub fn deduplicate_nested_paths<'a, I>(paths: I) -> DeduplicatedNestedPathsIter<'a>
|
||||
pub fn deduplicate_nested_paths<P, I>(paths: I) -> DeduplicatedNestedPathsIter<P>
|
||||
where
|
||||
I: IntoIterator<Item = &'a SystemPath>,
|
||||
I: IntoIterator<Item = P>,
|
||||
P: AsRef<SystemPath>,
|
||||
{
|
||||
DeduplicatedNestedPathsIter::new(paths)
|
||||
}
|
||||
|
||||
pub struct DeduplicatedNestedPathsIter<'a> {
|
||||
inner: std::vec::IntoIter<&'a SystemPath>,
|
||||
next: Option<&'a SystemPath>,
|
||||
pub struct DeduplicatedNestedPathsIter<P> {
|
||||
inner: std::vec::IntoIter<P>,
|
||||
next: Option<P>,
|
||||
}
|
||||
|
||||
impl<'a> DeduplicatedNestedPathsIter<'a> {
|
||||
impl<P> DeduplicatedNestedPathsIter<P>
|
||||
where
|
||||
P: AsRef<SystemPath>,
|
||||
{
|
||||
fn new<I>(paths: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = &'a SystemPath>,
|
||||
I: IntoIterator<Item = P>,
|
||||
{
|
||||
let mut paths = paths.into_iter().collect::<Vec<_>>();
|
||||
// Sort the path to ensure that e.g. `/a/b/c`, comes right after `/a/b`.
|
||||
paths.sort_unstable();
|
||||
paths.sort_unstable_by(|left, right| left.as_ref().cmp(right.as_ref()));
|
||||
|
||||
let mut iter = paths.into_iter();
|
||||
|
||||
|
@ -763,15 +767,18 @@ impl<'a> DeduplicatedNestedPathsIter<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for DeduplicatedNestedPathsIter<'a> {
|
||||
type Item = &'a SystemPath;
|
||||
impl<P> Iterator for DeduplicatedNestedPathsIter<P>
|
||||
where
|
||||
P: AsRef<SystemPath>,
|
||||
{
|
||||
type Item = P;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let current = self.next.take()?;
|
||||
|
||||
for next in self.inner.by_ref() {
|
||||
// Skip all paths that have the same prefix as the current path
|
||||
if !next.starts_with(current) {
|
||||
if !next.as_ref().starts_with(current.as_ref()) {
|
||||
self.next = Some(next);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,8 @@ use ruff_python_trivia::textwrap;
|
|||
|
||||
use crate::files::File;
|
||||
use crate::system::{
|
||||
DirectoryEntry, MemoryFileSystem, Metadata, Result, System, SystemPath, SystemVirtualPath,
|
||||
DirectoryEntry, MemoryFileSystem, Metadata, Result, System, SystemPath, SystemPathBuf,
|
||||
SystemVirtualPath,
|
||||
};
|
||||
use crate::Db;
|
||||
|
||||
|
@ -140,6 +141,13 @@ impl System for TestSystem {
|
|||
TestSystemInner::Stub(fs) => Ok(Box::new(fs.read_directory(path)?)),
|
||||
}
|
||||
}
|
||||
|
||||
fn canonicalize_path(&self, path: &SystemPath) -> Result<SystemPathBuf> {
|
||||
match &self.inner {
|
||||
TestSystemInner::System(fs) => fs.canonicalize_path(path),
|
||||
TestSystemInner::Stub(fs) => Ok(fs.canonicalize(path)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for databases that use [`TestSystem`].
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue