mirror of https://github.com/astral-sh/ruff
[red-knot] Manually implement `Debug` for `VendoredFileSystem` (#11983)
This commit is contained in:
parent
715609663a
commit
92b145e56a
|
|
@ -2202,6 +2202,7 @@ dependencies = [
|
||||||
"countme",
|
"countme",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"filetime",
|
"filetime",
|
||||||
|
"insta",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"ruff_python_ast",
|
"ruff_python_ast",
|
||||||
"ruff_python_parser",
|
"ruff_python_parser",
|
||||||
|
|
|
||||||
|
|
@ -26,4 +26,5 @@ rustc-hash = { workspace = true }
|
||||||
zip = { workspace = true }
|
zip = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
insta = { workspace = true }
|
||||||
once_cell = { workspace = true }
|
once_cell = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::fmt::{self, Debug};
|
||||||
use std::io::{self, Read};
|
use std::io::{self, Read};
|
||||||
use std::sync::{Mutex, MutexGuard};
|
use std::sync::{Mutex, MutexGuard};
|
||||||
|
|
||||||
|
|
@ -16,7 +18,6 @@ type Result<T> = io::Result<T>;
|
||||||
///
|
///
|
||||||
/// "Files" in the `VendoredFileSystem` are read-only and immutable.
|
/// "Files" in the `VendoredFileSystem` are read-only and immutable.
|
||||||
/// Directories are supported, but symlinks and hardlinks cannot exist.
|
/// Directories are supported, but symlinks and hardlinks cannot exist.
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct VendoredFileSystem {
|
pub struct VendoredFileSystem {
|
||||||
inner: VendoredFileSystemInner,
|
inner: VendoredFileSystemInner,
|
||||||
}
|
}
|
||||||
|
|
@ -78,6 +79,74 @@ impl VendoredFileSystem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for VendoredFileSystem {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let locked_inner = self.inner.lock();
|
||||||
|
if f.alternate() {
|
||||||
|
let mut paths: Vec<String> = locked_inner
|
||||||
|
.borrow()
|
||||||
|
.0
|
||||||
|
.file_names()
|
||||||
|
.map(String::from)
|
||||||
|
.collect();
|
||||||
|
paths.sort();
|
||||||
|
let debug_info: BTreeMap<String, ZipFileDebugInfo> = paths
|
||||||
|
.iter()
|
||||||
|
.map(|path| {
|
||||||
|
(
|
||||||
|
path.to_owned(),
|
||||||
|
ZipFileDebugInfo::from(locked_inner.borrow_mut().0.by_name(path).unwrap()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
f.debug_struct("VendoredFileSystem")
|
||||||
|
.field("inner_mutex_poisoned", &self.inner.0.is_poisoned())
|
||||||
|
.field("paths", &paths)
|
||||||
|
.field("data_by_path", &debug_info)
|
||||||
|
.finish()
|
||||||
|
} else {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"VendoredFileSystem(<{} paths>)",
|
||||||
|
locked_inner.borrow().len()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Private struct only used in `Debug` implementations
|
||||||
|
///
|
||||||
|
/// This could possibly be unified with the `Metadata` struct,
|
||||||
|
/// but that is deliberately kept small, and only exposes metadata
|
||||||
|
/// that users of the `VendoredFileSystem` could realistically need.
|
||||||
|
/// For debugging purposes, however, we want to have all information
|
||||||
|
/// available.
|
||||||
|
#[allow(unused)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ZipFileDebugInfo {
|
||||||
|
crc32_hash: u32,
|
||||||
|
compressed_size: u64,
|
||||||
|
uncompressed_size: u64,
|
||||||
|
compression_method: zip::CompressionMethod,
|
||||||
|
kind: FileType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<ZipFile<'a>> for ZipFileDebugInfo {
|
||||||
|
fn from(value: ZipFile<'a>) -> Self {
|
||||||
|
Self {
|
||||||
|
crc32_hash: value.crc32(),
|
||||||
|
compressed_size: value.compressed_size(),
|
||||||
|
uncompressed_size: value.size(),
|
||||||
|
compression_method: value.compression(),
|
||||||
|
kind: if value.is_dir() {
|
||||||
|
FileType::Directory
|
||||||
|
} else {
|
||||||
|
FileType::File
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum FileType {
|
pub enum FileType {
|
||||||
/// The path exists in the zip archive and represents a vendored file
|
/// The path exists in the zip archive and represents a vendored file
|
||||||
|
|
@ -126,7 +195,6 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct VendoredFileSystemInner(Mutex<RefCell<VendoredZipArchive>>);
|
struct VendoredFileSystemInner(Mutex<RefCell<VendoredZipArchive>>);
|
||||||
|
|
||||||
type LockedZipArchive<'a> = MutexGuard<'a, RefCell<VendoredZipArchive>>;
|
type LockedZipArchive<'a> = MutexGuard<'a, RefCell<VendoredZipArchive>>;
|
||||||
|
|
@ -160,6 +228,10 @@ impl VendoredZipArchive {
|
||||||
fn lookup_path(&mut self, path: &NormalizedVendoredPath) -> Result<ZipFile> {
|
fn lookup_path(&mut self, path: &NormalizedVendoredPath) -> Result<ZipFile> {
|
||||||
Ok(self.0.by_name(path.as_str())?)
|
Ok(self.0.by_name(path.as_str())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A path that has been normalized via the `normalize_vendored_path` function.
|
/// A path that has been normalized via the `normalize_vendored_path` function.
|
||||||
|
|
@ -213,8 +285,10 @@ fn normalize_vendored_path(path: &VendoredPath) -> NormalizedVendoredPath {
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
|
use insta::assert_snapshot;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use zip::{write::FileOptions, CompressionMethod, ZipWriter};
|
use zip::write::FileOptions;
|
||||||
|
use zip::{CompressionMethod, ZipWriter};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
@ -254,6 +328,59 @@ mod tests {
|
||||||
VendoredFileSystem::new(&MOCK_ZIP_ARCHIVE).unwrap()
|
VendoredFileSystem::new(&MOCK_ZIP_ARCHIVE).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn filesystem_debug_implementation() {
|
||||||
|
assert_snapshot!(
|
||||||
|
format!("{:?}", mock_typeshed()),
|
||||||
|
@"VendoredFileSystem(<4 paths>)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn filesystem_debug_implementation_alternate() {
|
||||||
|
assert_snapshot!(format!("{:#?}", mock_typeshed()), @r###"
|
||||||
|
VendoredFileSystem {
|
||||||
|
inner_mutex_poisoned: false,
|
||||||
|
paths: [
|
||||||
|
"stdlib/",
|
||||||
|
"stdlib/asyncio/",
|
||||||
|
"stdlib/asyncio/tasks.pyi",
|
||||||
|
"stdlib/functools.pyi",
|
||||||
|
],
|
||||||
|
data_by_path: {
|
||||||
|
"stdlib/": ZipFileDebugInfo {
|
||||||
|
crc32_hash: 0,
|
||||||
|
compressed_size: 0,
|
||||||
|
uncompressed_size: 0,
|
||||||
|
compression_method: Stored,
|
||||||
|
kind: Directory,
|
||||||
|
},
|
||||||
|
"stdlib/asyncio/": ZipFileDebugInfo {
|
||||||
|
crc32_hash: 0,
|
||||||
|
compressed_size: 0,
|
||||||
|
uncompressed_size: 0,
|
||||||
|
compression_method: Stored,
|
||||||
|
kind: Directory,
|
||||||
|
},
|
||||||
|
"stdlib/asyncio/tasks.pyi": ZipFileDebugInfo {
|
||||||
|
crc32_hash: 2826547428,
|
||||||
|
compressed_size: 24,
|
||||||
|
uncompressed_size: 15,
|
||||||
|
compression_method: Zstd,
|
||||||
|
kind: File,
|
||||||
|
},
|
||||||
|
"stdlib/functools.pyi": ZipFileDebugInfo {
|
||||||
|
crc32_hash: 1099005079,
|
||||||
|
compressed_size: 34,
|
||||||
|
uncompressed_size: 25,
|
||||||
|
compression_method: Zstd,
|
||||||
|
kind: File,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
fn test_directory(dirname: &str) {
|
fn test_directory(dirname: &str) {
|
||||||
let mock_typeshed = mock_typeshed();
|
let mock_typeshed = mock_typeshed();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue