[red-knot] Use web-time instead of FileTime::now (#16967)

## Summary

`std::time::now` isn't available on `wasm32-unknown-unknown` but it is
used by `FileTime::now`.

This PR replaces the usages of `FileTime::now` with a target specific
helper function that we already had in the memory file system.
Fixes https://github.com/astral-sh/ruff/issues/16966

## Test Plan

Tested that the playground no longer crash when adding an extra-path
This commit is contained in:
Micha Reiser 2025-03-25 14:03:30 +01:00 committed by GitHub
parent 4975c2f027
commit 8d16a5c8c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 38 additions and 36 deletions

View file

@ -12,7 +12,7 @@ use red_knot_python_semantic::{resolve_module, ModuleName, PythonPlatform};
use ruff_db::files::{system_path_to_file, File, FileError}; use ruff_db::files::{system_path_to_file, File, FileError};
use ruff_db::source::source_text; use ruff_db::source::source_text;
use ruff_db::system::{ use ruff_db::system::{
OsSystem, System, SystemPath, SystemPathBuf, UserConfigDirectoryOverrideGuard, file_time_now, OsSystem, System, SystemPath, SystemPathBuf, UserConfigDirectoryOverrideGuard,
}; };
use ruff_db::{Db as _, Upcast}; use ruff_db::{Db as _, Upcast};
use ruff_python_ast::PythonVersion; use ruff_python_ast::PythonVersion;
@ -462,7 +462,7 @@ fn update_file(path: impl AsRef<SystemPath>, content: &str) -> anyhow::Result<()
std::thread::sleep(Duration::from_nanos(10)); std::thread::sleep(Duration::from_nanos(10));
filetime::set_file_handle_times(&file, None, Some(filetime::FileTime::now()))?; filetime::set_file_handle_times(&file, None, Some(file_time_now()))?;
} }
} }

View file

@ -1,3 +1,5 @@
use crate::system::file_time_now;
/// A number representing the revision of a file. /// A number representing the revision of a file.
/// ///
/// Two revisions that don't compare equal signify that the file has been modified. /// Two revisions that don't compare equal signify that the file has been modified.
@ -16,7 +18,7 @@ impl FileRevision {
} }
pub fn now() -> Self { pub fn now() -> Self {
Self::from(filetime::FileTime::now()) Self::from(file_time_now())
} }
pub const fn zero() -> Self { pub const fn zero() -> Self {
@ -53,13 +55,12 @@ impl From<filetime::FileTime> for FileRevision {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use filetime::FileTime;
use super::*; use super::*;
#[test] #[test]
fn revision_from_file_time() { fn revision_from_file_time() {
let file_time = FileTime::now(); let file_time = file_time_now();
let revision = FileRevision::from(file_time); let revision = FileRevision::from(file_time);
let revision = revision.as_u128(); let revision = revision.as_u128();

View file

@ -7,6 +7,7 @@ pub use os::testing::UserConfigDirectoryOverrideGuard;
#[cfg(feature = "os")] #[cfg(feature = "os")]
pub use os::OsSystem; pub use os::OsSystem;
use filetime::FileTime;
use ruff_notebook::{Notebook, NotebookError}; use ruff_notebook::{Notebook, NotebookError};
use std::error::Error; use std::error::Error;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
@ -346,3 +347,27 @@ pub enum GlobErrorKind {
IOError(io::Error), IOError(io::Error),
NonUtf8Path, NonUtf8Path,
} }
#[cfg(not(target_arch = "wasm32"))]
pub fn file_time_now() -> FileTime {
FileTime::now()
}
#[cfg(target_arch = "wasm32")]
pub fn file_time_now() -> FileTime {
// Copied from FileTime::from_system_time()
let time = web_time::SystemTime::now();
time.duration_since(web_time::UNIX_EPOCH)
.map(|d| FileTime::from_unix_time(d.as_secs() as i64, d.subsec_nanos()))
.unwrap_or_else(|e| {
let until_epoch = e.duration();
let (sec_offset, nanos) = if until_epoch.subsec_nanos() == 0 {
(0, 0)
} else {
(-1, 1_000_000_000 - until_epoch.subsec_nanos())
};
FileTime::from_unix_time(-(until_epoch.as_secs() as i64) + sec_offset, nanos)
})
}

View file

@ -7,8 +7,8 @@ use filetime::FileTime;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use crate::system::{ use crate::system::{
walk_directory, DirectoryEntry, FileType, GlobError, GlobErrorKind, Metadata, Result, file_time_now, walk_directory, DirectoryEntry, FileType, GlobError, GlobErrorKind, Metadata,
SystemPath, SystemPathBuf, SystemVirtualPath, SystemVirtualPathBuf, Result, SystemPath, SystemPathBuf, SystemVirtualPath, SystemVirtualPathBuf,
}; };
use super::walk_directory::{ use super::walk_directory::{
@ -163,7 +163,7 @@ impl MemoryFileSystem {
let file = get_or_create_file(&mut by_path, &normalized)?; let file = get_or_create_file(&mut by_path, &normalized)?;
file.content = content.to_string(); file.content = content.to_string();
file.last_modified = now(); file.last_modified = file_time_now();
Ok(()) Ok(())
} }
@ -215,7 +215,7 @@ impl MemoryFileSystem {
std::collections::hash_map::Entry::Vacant(entry) => { std::collections::hash_map::Entry::Vacant(entry) => {
entry.insert(File { entry.insert(File {
content: content.to_string(), content: content.to_string(),
last_modified: now(), last_modified: file_time_now(),
}); });
} }
std::collections::hash_map::Entry::Occupied(mut entry) => { std::collections::hash_map::Entry::Occupied(mut entry) => {
@ -310,7 +310,7 @@ impl MemoryFileSystem {
let mut by_path = self.inner.by_path.write().unwrap(); let mut by_path = self.inner.by_path.write().unwrap();
let normalized = self.normalize_path(path.as_ref()); let normalized = self.normalize_path(path.as_ref());
get_or_create_file(&mut by_path, &normalized)?.last_modified = now(); get_or_create_file(&mut by_path, &normalized)?.last_modified = file_time_now();
Ok(()) Ok(())
} }
@ -486,7 +486,7 @@ fn create_dir_all(
path.push(component); path.push(component);
let entry = paths.entry(path.clone()).or_insert_with(|| { let entry = paths.entry(path.clone()).or_insert_with(|| {
Entry::Directory(Directory { Entry::Directory(Directory {
last_modified: now(), last_modified: file_time_now(),
}) })
}); });
@ -513,7 +513,7 @@ fn get_or_create_file<'a>(
let entry = paths.entry(normalized.to_path_buf()).or_insert_with(|| { let entry = paths.entry(normalized.to_path_buf()).or_insert_with(|| {
Entry::File(File { Entry::File(File {
content: String::new(), content: String::new(),
last_modified: now(), last_modified: file_time_now(),
}) })
}); });
@ -695,30 +695,6 @@ enum WalkerState {
Nested { path: SystemPathBuf, depth: usize }, Nested { path: SystemPathBuf, depth: usize },
} }
#[cfg(not(target_arch = "wasm32"))]
fn now() -> FileTime {
FileTime::now()
}
#[cfg(target_arch = "wasm32")]
fn now() -> FileTime {
// Copied from FileTime::from_system_time()
let time = web_time::SystemTime::now();
time.duration_since(web_time::UNIX_EPOCH)
.map(|d| FileTime::from_unix_time(d.as_secs() as i64, d.subsec_nanos()))
.unwrap_or_else(|e| {
let until_epoch = e.duration();
let (sec_offset, nanos) = if until_epoch.subsec_nanos() == 0 {
(0, 0)
} else {
(-1, 1_000_000_000 - until_epoch.subsec_nanos())
};
FileTime::from_unix_time(-(until_epoch.as_secs() as i64) + sec_offset, nanos)
})
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::io::ErrorKind; use std::io::ErrorKind;