mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
[ty] Disallow std::env and io methods in most ty crates (#20046)
## Summary We use the `System` abstraction in ty to abstract away the host/system on which ty runs. This has a few benefits: * Tests can run in full isolation using a memory system (that uses an in-memory file system) * The LSP has a custom implementation where `read_to_string` returns the content as seen by the editor (e.g. unsaved changes) instead of always returning the content as it is stored on disk * We don't require any file system polyfills for wasm in the browser However, it does require extra care that we don't accidentally use `std::fs` or `std::env` (etc.) methods in ty's code base (which is very easy). This PR sets up Clippy and disallows the most common methods, instead pointing users towards the corresponding `System` methods. The setup is a bit awkward because clippy doesn't support inheriting configurations. That means, a crate can only override the entire workspace configuration or not at all. The approach taken in this PR is: * Configure the disallowed methods at the workspace level * Allow `disallowed_methods` at the workspace level * Enable the lint at the crate level using the warn attribute (in code) The obvious downside is that it won't work if we ever want to disallow other methods, but we can figure that out once we reach that point. What about false positives: Just add an `allow` and move on with your life :) This isn't something that we have to enforce strictly; the goal is to catch accidental misuse. ## Test Plan Clippy found a place where we incorrectly used `std::fs::read_to_string`
This commit is contained in:
parent
5508e8e528
commit
796819e7a0
20 changed files with 192 additions and 42 deletions
|
@ -215,6 +215,8 @@ unexpected_cfgs = { level = "warn", check-cfg = [
|
||||||
|
|
||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
pedantic = { level = "warn", priority = -2 }
|
pedantic = { level = "warn", priority = -2 }
|
||||||
|
# Enabled at the crate level
|
||||||
|
disallowed_methods = "allow"
|
||||||
# Allowed pedantic lints
|
# Allowed pedantic lints
|
||||||
char_lit_as_u8 = "allow"
|
char_lit_as_u8 = "allow"
|
||||||
collapsible_else_if = "allow"
|
collapsible_else_if = "allow"
|
||||||
|
@ -253,6 +255,7 @@ unused_peekable = "warn"
|
||||||
# Diagnostics are not actionable: Enable once https://github.com/rust-lang/rust-clippy/issues/13774 is resolved.
|
# Diagnostics are not actionable: Enable once https://github.com/rust-lang/rust-clippy/issues/13774 is resolved.
|
||||||
large_stack_arrays = "allow"
|
large_stack_arrays = "allow"
|
||||||
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
# Note that we set these explicitly, and these values
|
# Note that we set these explicitly, and these values
|
||||||
# were chosen based on a trade-off between compile times
|
# were chosen based on a trade-off between compile times
|
||||||
|
|
17
clippy.toml
17
clippy.toml
|
@ -24,3 +24,20 @@ ignore-interior-mutability = [
|
||||||
# The expression is read-only.
|
# The expression is read-only.
|
||||||
"ruff_python_ast::hashable::HashableExpr",
|
"ruff_python_ast::hashable::HashableExpr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
disallowed-methods = [
|
||||||
|
{ path = "std::env::var", reason = "Use System::env_var instead in ty crates" },
|
||||||
|
{ path = "std::env::current_dir", reason = "Use System::current_directory instead in ty crates" },
|
||||||
|
{ path = "std::fs::read_to_string", reason = "Use System::read_to_string instead in ty crates" },
|
||||||
|
{ path = "std::fs::metadata", reason = "Use System::path_metadata instead in ty crates" },
|
||||||
|
{ path = "std::fs::canonicalize", reason = "Use System::canonicalize_path instead in ty crates" },
|
||||||
|
{ path = "dunce::canonicalize", reason = "Use System::canonicalize_path instead in ty crates" },
|
||||||
|
{ path = "std::fs::read_dir", reason = "Use System::read_directory instead in ty crates" },
|
||||||
|
{ path = "std::fs::write", reason = "Use WritableSystem::write_file instead in ty crates" },
|
||||||
|
{ path = "std::fs::create_dir_all", reason = "Use WritableSystem::create_directory_all instead in ty crates" },
|
||||||
|
{ path = "std::fs::File::create_new", reason = "Use WritableSystem::create_new_file instead in ty crates" },
|
||||||
|
# Path methods that have System trait equivalents
|
||||||
|
{ path = "std::path::Path::exists", reason = "Use System::path_exists instead in ty crates" },
|
||||||
|
{ path = "std::path::Path::is_dir", reason = "Use System::is_directory instead in ty crates" },
|
||||||
|
{ path = "std::path::Path::is_file", reason = "Use System::is_file instead in ty crates" },
|
||||||
|
]
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
#![warn(
|
||||||
|
clippy::disallowed_methods,
|
||||||
|
reason = "Prefer System trait methods over std methods"
|
||||||
|
)]
|
||||||
|
|
||||||
use crate::files::Files;
|
use crate::files::Files;
|
||||||
use crate::system::System;
|
use crate::system::System;
|
||||||
use crate::vendored::VendoredFileSystem;
|
use crate::vendored::VendoredFileSystem;
|
||||||
|
@ -65,6 +70,10 @@ pub trait Db: salsa::Database {
|
||||||
/// to process work in parallel. For example, to index a directory or checking the files of a project.
|
/// to process work in parallel. For example, to index a directory or checking the files of a project.
|
||||||
/// ty can still spawn more threads for other tasks, e.g. to wait for a Ctrl+C signal or
|
/// ty can still spawn more threads for other tasks, e.g. to wait for a Ctrl+C signal or
|
||||||
/// watching the files for changes.
|
/// watching the files for changes.
|
||||||
|
#[expect(
|
||||||
|
clippy::disallowed_methods,
|
||||||
|
reason = "We don't have access to System here, but this is also only used by the CLI and the server which always run on a real system."
|
||||||
|
)]
|
||||||
pub fn max_parallelism() -> NonZeroUsize {
|
pub fn max_parallelism() -> NonZeroUsize {
|
||||||
std::env::var(EnvVars::TY_MAX_PARALLELISM)
|
std::env::var(EnvVars::TY_MAX_PARALLELISM)
|
||||||
.or_else(|_| std::env::var(EnvVars::RAYON_NUM_THREADS))
|
.or_else(|_| std::env::var(EnvVars::RAYON_NUM_THREADS))
|
||||||
|
|
|
@ -46,7 +46,7 @@ pub type Result<T> = std::io::Result<T>;
|
||||||
/// * File watching isn't supported.
|
/// * File watching isn't supported.
|
||||||
///
|
///
|
||||||
/// Abstracting the system also enables tests to use a more efficient in-memory file system.
|
/// Abstracting the system also enables tests to use a more efficient in-memory file system.
|
||||||
pub trait System: Debug {
|
pub trait System: Debug + Sync + Send {
|
||||||
/// Reads the metadata of the file or directory at `path`.
|
/// Reads the metadata of the file or directory at `path`.
|
||||||
///
|
///
|
||||||
/// This function will traverse symbolic links to query information about the destination file.
|
/// This function will traverse symbolic links to query information about the destination file.
|
||||||
|
@ -197,6 +197,8 @@ pub trait System: Debug {
|
||||||
fn as_any(&self) -> &dyn std::any::Any;
|
fn as_any(&self) -> &dyn std::any::Any;
|
||||||
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
|
||||||
|
|
||||||
|
fn dyn_clone(&self) -> Box<dyn System>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![allow(clippy::disallowed_methods)]
|
||||||
|
|
||||||
use super::walk_directory::{
|
use super::walk_directory::{
|
||||||
self, DirectoryWalker, WalkDirectoryBuilder, WalkDirectoryConfiguration,
|
self, DirectoryWalker, WalkDirectoryBuilder, WalkDirectoryConfiguration,
|
||||||
WalkDirectoryVisitorBuilder, WalkState,
|
WalkDirectoryVisitorBuilder, WalkState,
|
||||||
|
@ -255,6 +257,10 @@ impl System for OsSystem {
|
||||||
fn env_var(&self, name: &str) -> std::result::Result<String, std::env::VarError> {
|
fn env_var(&self, name: &str) -> std::result::Result<String, std::env::VarError> {
|
||||||
std::env::var(name)
|
std::env::var(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dyn_clone(&self) -> Box<dyn System> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OsSystem {
|
impl OsSystem {
|
||||||
|
|
|
@ -146,6 +146,10 @@ impl System for TestSystem {
|
||||||
fn case_sensitivity(&self) -> CaseSensitivity {
|
fn case_sensitivity(&self) -> CaseSensitivity {
|
||||||
self.system().case_sensitivity()
|
self.system().case_sensitivity()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dyn_clone(&self) -> Box<dyn System> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TestSystem {
|
impl Default for TestSystem {
|
||||||
|
@ -394,6 +398,13 @@ impl System for InMemorySystem {
|
||||||
fn case_sensitivity(&self) -> CaseSensitivity {
|
fn case_sensitivity(&self) -> CaseSensitivity {
|
||||||
CaseSensitivity::CaseSensitive
|
CaseSensitivity::CaseSensitive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dyn_clone(&self) -> Box<dyn System> {
|
||||||
|
Box::new(Self {
|
||||||
|
user_config_directory: Mutex::new(self.user_config_directory.lock().unwrap().clone()),
|
||||||
|
memory_fs: self.memory_fs.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WritableSystem for InMemorySystem {
|
impl WritableSystem for InMemorySystem {
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
#![warn(
|
||||||
|
clippy::disallowed_methods,
|
||||||
|
reason = "Prefer System trait methods over std methods in ty crates"
|
||||||
|
)]
|
||||||
|
|
||||||
use std::{collections::HashMap, hash::BuildHasher};
|
use std::{collections::HashMap, hash::BuildHasher};
|
||||||
|
|
||||||
use ordermap::OrderMap;
|
use ordermap::OrderMap;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
#![warn(
|
||||||
|
clippy::disallowed_methods,
|
||||||
|
reason = "Prefer System trait methods over std methods in ty crates"
|
||||||
|
)]
|
||||||
mod completion;
|
mod completion;
|
||||||
mod doc_highlights;
|
mod doc_highlights;
|
||||||
mod docstring;
|
mod docstring;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
#![warn(
|
||||||
|
clippy::disallowed_methods,
|
||||||
|
reason = "Prefer System trait methods over std methods in ty crates"
|
||||||
|
)]
|
||||||
use crate::glob::{GlobFilterCheckMode, IncludeResult};
|
use crate::glob::{GlobFilterCheckMode, IncludeResult};
|
||||||
use crate::metadata::options::{OptionDiagnostic, ToSettingsError};
|
use crate::metadata::options::{OptionDiagnostic, ToSettingsError};
|
||||||
use crate::walk::{ProjectFilesFilter, ProjectFilesWalker};
|
use crate::walk::{ProjectFilesFilter, ProjectFilesWalker};
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
#![allow(
|
||||||
|
clippy::disallowed_methods,
|
||||||
|
reason = "This implementation is specific to real file systems."
|
||||||
|
)]
|
||||||
|
|
||||||
use notify::event::{CreateKind, MetadataKind, ModifyKind, RemoveKind, RenameMode};
|
use notify::event::{CreateKind, MetadataKind, ModifyKind, RemoveKind, RenameMode};
|
||||||
use notify::{EventKind, RecommendedWatcher, RecursiveMode, Watcher as _, recommended_watcher};
|
use notify::{EventKind, RecommendedWatcher, RecursiveMode, Watcher as _, recommended_watcher};
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
#![warn(
|
||||||
|
clippy::disallowed_methods,
|
||||||
|
reason = "Prefer System trait methods over std methods in ty crates"
|
||||||
|
)]
|
||||||
use std::hash::BuildHasherDefault;
|
use std::hash::BuildHasherDefault;
|
||||||
|
|
||||||
use rustc_hash::FxHasher;
|
use rustc_hash::FxHasher;
|
||||||
|
|
|
@ -363,6 +363,11 @@ fn is_python_extension(ext: &str) -> bool {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![expect(
|
||||||
|
clippy::disallowed_methods,
|
||||||
|
reason = "These are tests, so it's fine to do I/O by-passing System."
|
||||||
|
)]
|
||||||
|
|
||||||
use camino::{Utf8Component, Utf8Path};
|
use camino::{Utf8Component, Utf8Path};
|
||||||
use ruff_db::Db as _;
|
use ruff_db::Db as _;
|
||||||
use ruff_db::files::{File, FilePath, FileRootKind};
|
use ruff_db::files::{File, FilePath, FileRootKind};
|
||||||
|
|
|
@ -1115,6 +1115,10 @@ impl fmt::Display for RelaxedModuleName {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![expect(
|
||||||
|
clippy::disallowed_methods,
|
||||||
|
reason = "These are tests, so it's fine to do I/O by-passing System."
|
||||||
|
)]
|
||||||
use ruff_db::Db;
|
use ruff_db::Db;
|
||||||
use ruff_db::files::{File, FilePath, system_path_to_file};
|
use ruff_db::files::{File, FilePath, system_path_to_file};
|
||||||
use ruff_db::system::{DbWithTestSystem as _, DbWithWritableSystem as _};
|
use ruff_db::system::{DbWithTestSystem as _, DbWithWritableSystem as _};
|
||||||
|
|
|
@ -304,6 +304,11 @@ impl fmt::Display for PyVersionRange {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#![expect(
|
||||||
|
clippy::disallowed_methods,
|
||||||
|
reason = "These are tests, so it's fine to do I/O by-passing System."
|
||||||
|
)]
|
||||||
|
|
||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
use std::num::{IntErrorKind, NonZeroU16};
|
use std::num::{IntErrorKind, NonZeroU16};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
|
@ -211,7 +211,7 @@ impl PythonEnvironment {
|
||||||
match VirtualEnvironment::new(path, system) {
|
match VirtualEnvironment::new(path, system) {
|
||||||
Ok(venv) => Ok(Self::Virtual(venv)),
|
Ok(venv) => Ok(Self::Virtual(venv)),
|
||||||
// If there's not a `pyvenv.cfg` marker, attempt to inspect as a system environment
|
// If there's not a `pyvenv.cfg` marker, attempt to inspect as a system environment
|
||||||
Err(SitePackagesDiscoveryError::NoPyvenvCfgFile(path, _))
|
Err(SitePackagesDiscoveryError::NoPyvenvCfgFile(path, _, _))
|
||||||
if !path.origin.must_be_virtual_env() =>
|
if !path.origin.must_be_virtual_env() =>
|
||||||
{
|
{
|
||||||
Ok(Self::System(SystemEnvironment::new(path)))
|
Ok(Self::System(SystemEnvironment::new(path)))
|
||||||
|
@ -378,7 +378,13 @@ impl VirtualEnvironment {
|
||||||
|
|
||||||
let pyvenv_cfg = match system.read_to_string(&pyvenv_cfg_path) {
|
let pyvenv_cfg = match system.read_to_string(&pyvenv_cfg_path) {
|
||||||
Ok(pyvenv_cfg) => pyvenv_cfg,
|
Ok(pyvenv_cfg) => pyvenv_cfg,
|
||||||
Err(err) => return Err(SitePackagesDiscoveryError::NoPyvenvCfgFile(path, err)),
|
Err(err) => {
|
||||||
|
return Err(SitePackagesDiscoveryError::NoPyvenvCfgFile(
|
||||||
|
path,
|
||||||
|
err,
|
||||||
|
system.dyn_clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let parsed_pyvenv_cfg =
|
let parsed_pyvenv_cfg =
|
||||||
|
@ -833,16 +839,26 @@ impl SystemEnvironment {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SitePackagesDiscoveryError {
|
pub enum SitePackagesDiscoveryError {
|
||||||
/// `site-packages` discovery failed because the provided path couldn't be canonicalized.
|
/// `site-packages` discovery failed because the provided path couldn't be canonicalized.
|
||||||
CanonicalizationError(SystemPathBuf, SysPrefixPathOrigin, io::Error),
|
CanonicalizationError(
|
||||||
|
SystemPathBuf,
|
||||||
|
SysPrefixPathOrigin,
|
||||||
|
io::Error,
|
||||||
|
Box<dyn System>,
|
||||||
|
),
|
||||||
|
|
||||||
/// `site-packages` discovery failed because the provided path doesn't appear to point to
|
/// `site-packages` discovery failed because the provided path doesn't appear to point to
|
||||||
/// a Python executable or a `sys.prefix` directory.
|
/// a Python executable or a `sys.prefix` directory.
|
||||||
PathNotExecutableOrDirectory(SystemPathBuf, SysPrefixPathOrigin, Option<io::Error>),
|
PathNotExecutableOrDirectory(
|
||||||
|
SystemPathBuf,
|
||||||
|
SysPrefixPathOrigin,
|
||||||
|
Option<io::Error>,
|
||||||
|
Box<dyn System>,
|
||||||
|
),
|
||||||
|
|
||||||
/// `site-packages` discovery failed because the [`SysPrefixPathOrigin`] indicated that
|
/// `site-packages` discovery failed because the [`SysPrefixPathOrigin`] indicated that
|
||||||
/// the provided path should point to the `sys.prefix` of a virtual environment,
|
/// the provided path should point to the `sys.prefix` of a virtual environment,
|
||||||
/// but there was no file at `<sys.prefix>/pyvenv.cfg`.
|
/// but there was no file at `<sys.prefix>/pyvenv.cfg`.
|
||||||
NoPyvenvCfgFile(SysPrefixPath, io::Error),
|
NoPyvenvCfgFile(SysPrefixPath, io::Error, Box<dyn System>),
|
||||||
|
|
||||||
/// `site-packages` discovery failed because the `pyvenv.cfg` file could not be parsed.
|
/// `site-packages` discovery failed because the `pyvenv.cfg` file could not be parsed.
|
||||||
PyvenvCfgParseError(SystemPathBuf, PyvenvCfgParseErrorKind),
|
PyvenvCfgParseError(SystemPathBuf, PyvenvCfgParseErrorKind),
|
||||||
|
@ -852,11 +868,11 @@ pub enum SitePackagesDiscoveryError {
|
||||||
/// would be relative to the `sys.prefix` path, and we tried to fallback to iterating
|
/// would be relative to the `sys.prefix` path, and we tried to fallback to iterating
|
||||||
/// through the `<sys.prefix>/lib` directory looking for a `site-packages` directory,
|
/// through the `<sys.prefix>/lib` directory looking for a `site-packages` directory,
|
||||||
/// but we came across some I/O error while trying to do so.
|
/// but we came across some I/O error while trying to do so.
|
||||||
CouldNotReadLibDirectory(SysPrefixPath),
|
CouldNotReadLibDirectory(SysPrefixPath, Box<dyn System>),
|
||||||
|
|
||||||
/// We looked everywhere we could think of for the `site-packages` directory,
|
/// We looked everywhere we could think of for the `site-packages` directory,
|
||||||
/// but none could be found despite our best endeavours.
|
/// but none could be found despite our best endeavours.
|
||||||
NoSitePackagesDirFound(SysPrefixPath),
|
NoSitePackagesDirFound(SysPrefixPath, Box<dyn System>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enumeration of ways in which stdlib discovery can fail.
|
/// Enumeration of ways in which stdlib discovery can fail.
|
||||||
|
@ -864,13 +880,13 @@ pub enum SitePackagesDiscoveryError {
|
||||||
pub enum StdlibDiscoveryError {
|
pub enum StdlibDiscoveryError {
|
||||||
/// We looked everywhere we could think of for the standard library's directory,
|
/// We looked everywhere we could think of for the standard library's directory,
|
||||||
/// but none could be found despite our best endeavours.
|
/// but none could be found despite our best endeavours.
|
||||||
NoStdlibFound(SysPrefixPath),
|
NoStdlibFound(SysPrefixPath, Box<dyn System>),
|
||||||
/// Stdlib discovery failed because we're on a Unix system,
|
/// Stdlib discovery failed because we're on a Unix system,
|
||||||
/// we weren't able to figure out from the `pyvenv.cfg` file exactly where the stdlib
|
/// we weren't able to figure out from the `pyvenv.cfg` file exactly where the stdlib
|
||||||
/// would be relative to the `sys.prefix` path, and we tried to fallback to iterating
|
/// would be relative to the `sys.prefix` path, and we tried to fallback to iterating
|
||||||
/// through the `<sys.prefix>/lib` directory looking for a stdlib directory,
|
/// through the `<sys.prefix>/lib` directory looking for a stdlib directory,
|
||||||
/// but we came across some I/O error while trying to do so.
|
/// but we came across some I/O error while trying to do so.
|
||||||
CouldNotReadLibDirectory(SysPrefixPath, io::Error),
|
CouldNotReadLibDirectory(SysPrefixPath, io::Error, Box<dyn System>),
|
||||||
/// We failed to resolve the value of `sys.prefix`.
|
/// We failed to resolve the value of `sys.prefix`.
|
||||||
NoSysPrefixFound(SystemPathBuf),
|
NoSysPrefixFound(SystemPathBuf),
|
||||||
}
|
}
|
||||||
|
@ -878,14 +894,14 @@ pub enum StdlibDiscoveryError {
|
||||||
impl std::error::Error for SitePackagesDiscoveryError {
|
impl std::error::Error for SitePackagesDiscoveryError {
|
||||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
match self {
|
match self {
|
||||||
Self::CanonicalizationError(_, _, io_err) => Some(io_err),
|
Self::CanonicalizationError(_, _, io_err, _) => Some(io_err),
|
||||||
Self::PathNotExecutableOrDirectory(_, _, io_err) => {
|
Self::PathNotExecutableOrDirectory(_, _, io_err, _) => {
|
||||||
io_err.as_ref().map(|e| e as &dyn std::error::Error)
|
io_err.as_ref().map(|e| e as &dyn std::error::Error)
|
||||||
}
|
}
|
||||||
Self::NoPyvenvCfgFile(_, io_err) => Some(io_err),
|
Self::NoPyvenvCfgFile(_, io_err, _) => Some(io_err),
|
||||||
Self::PyvenvCfgParseError(_, _)
|
Self::PyvenvCfgParseError(_, _)
|
||||||
| Self::CouldNotReadLibDirectory(_)
|
| Self::CouldNotReadLibDirectory(_, _)
|
||||||
| Self::NoSitePackagesDirFound(_) => None,
|
| Self::NoSitePackagesDirFound(_, _) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -893,10 +909,15 @@ impl std::error::Error for SitePackagesDiscoveryError {
|
||||||
impl std::fmt::Display for SitePackagesDiscoveryError {
|
impl std::fmt::Display for SitePackagesDiscoveryError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::CanonicalizationError(given_path, origin, _) => {
|
Self::CanonicalizationError(given_path, origin, _, system) => display_error(
|
||||||
display_error(f, origin, given_path, "Failed to canonicalize", None)
|
f,
|
||||||
}
|
origin,
|
||||||
Self::PathNotExecutableOrDirectory(path, origin, _) => {
|
given_path,
|
||||||
|
"Failed to canonicalize",
|
||||||
|
None,
|
||||||
|
&**system,
|
||||||
|
),
|
||||||
|
Self::PathNotExecutableOrDirectory(path, origin, _, system) => {
|
||||||
let thing = if origin.must_point_directly_to_sys_prefix() {
|
let thing = if origin.must_point_directly_to_sys_prefix() {
|
||||||
"directory on disk"
|
"directory on disk"
|
||||||
} else {
|
} else {
|
||||||
|
@ -908,14 +929,16 @@ impl std::fmt::Display for SitePackagesDiscoveryError {
|
||||||
path,
|
path,
|
||||||
&format!("Invalid {origin}"),
|
&format!("Invalid {origin}"),
|
||||||
Some(&format!("does not point to a {thing}")),
|
Some(&format!("does not point to a {thing}")),
|
||||||
|
&**system,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Self::NoPyvenvCfgFile(SysPrefixPath { inner, origin }, _) => display_error(
|
Self::NoPyvenvCfgFile(SysPrefixPath { inner, origin }, _, system) => display_error(
|
||||||
f,
|
f,
|
||||||
origin,
|
origin,
|
||||||
inner,
|
inner,
|
||||||
&format!("Invalid {origin}"),
|
&format!("Invalid {origin}"),
|
||||||
Some("points to a broken venv with no pyvenv.cfg file"),
|
Some("points to a broken venv with no pyvenv.cfg file"),
|
||||||
|
&**system,
|
||||||
),
|
),
|
||||||
Self::PyvenvCfgParseError(path, kind) => {
|
Self::PyvenvCfgParseError(path, kind) => {
|
||||||
write!(
|
write!(
|
||||||
|
@ -923,14 +946,17 @@ impl std::fmt::Display for SitePackagesDiscoveryError {
|
||||||
"Failed to parse the `pyvenv.cfg` file at `{path}` because {kind}"
|
"Failed to parse the `pyvenv.cfg` file at `{path}` because {kind}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Self::CouldNotReadLibDirectory(SysPrefixPath { inner, origin }) => display_error(
|
Self::CouldNotReadLibDirectory(SysPrefixPath { inner, origin }, system) => {
|
||||||
f,
|
display_error(
|
||||||
origin,
|
f,
|
||||||
inner,
|
origin,
|
||||||
"Failed to iterate over the contents of the `lib`/`lib64` directories of the Python installation",
|
inner,
|
||||||
None,
|
"Failed to iterate over the contents of the `lib`/`lib64` directories of the Python installation",
|
||||||
),
|
None,
|
||||||
Self::NoSitePackagesDirFound(SysPrefixPath { inner, origin }) => display_error(
|
&**system,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Self::NoSitePackagesDirFound(SysPrefixPath { inner, origin }, system) => display_error(
|
||||||
f,
|
f,
|
||||||
origin,
|
origin,
|
||||||
inner,
|
inner,
|
||||||
|
@ -938,6 +964,7 @@ impl std::fmt::Display for SitePackagesDiscoveryError {
|
||||||
Some(
|
Some(
|
||||||
"Could not find a `site-packages` directory for this Python installation/executable",
|
"Could not find a `site-packages` directory for this Python installation/executable",
|
||||||
),
|
),
|
||||||
|
&**system,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -946,8 +973,8 @@ impl std::fmt::Display for SitePackagesDiscoveryError {
|
||||||
impl std::error::Error for StdlibDiscoveryError {
|
impl std::error::Error for StdlibDiscoveryError {
|
||||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
match self {
|
match self {
|
||||||
Self::CouldNotReadLibDirectory(_, io_err) => Some(io_err),
|
Self::CouldNotReadLibDirectory(_, io_err, _) => Some(io_err),
|
||||||
Self::NoStdlibFound(_) => None,
|
Self::NoStdlibFound(_, _) => None,
|
||||||
Self::NoSysPrefixFound(_) => None,
|
Self::NoSysPrefixFound(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -962,19 +989,23 @@ impl std::fmt::Display for StdlibDiscoveryError {
|
||||||
"Failed to resolve a `sys.prefix` from the `pyvenv.cfg` file at `{path}`"
|
"Failed to resolve a `sys.prefix` from the `pyvenv.cfg` file at `{path}`"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Self::CouldNotReadLibDirectory(SysPrefixPath { inner, origin }, _) => display_error(
|
Self::CouldNotReadLibDirectory(SysPrefixPath { inner, origin }, _, system) => {
|
||||||
f,
|
display_error(
|
||||||
origin,
|
f,
|
||||||
inner,
|
origin,
|
||||||
"Failed to iterate over the contents of the `lib` directory of the Python installation",
|
inner,
|
||||||
None,
|
"Failed to iterate over the contents of the `lib` directory of the Python installation",
|
||||||
),
|
None,
|
||||||
Self::NoStdlibFound(SysPrefixPath { inner, origin }) => display_error(
|
&**system,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Self::NoStdlibFound(SysPrefixPath { inner, origin }, system) => display_error(
|
||||||
f,
|
f,
|
||||||
origin,
|
origin,
|
||||||
inner,
|
inner,
|
||||||
&format!("Invalid {origin}"),
|
&format!("Invalid {origin}"),
|
||||||
Some("Could not find a stdlib directory for this Python installation/executable"),
|
Some("Could not find a stdlib directory for this Python installation/executable"),
|
||||||
|
&**system,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -986,6 +1017,7 @@ fn display_error(
|
||||||
given_path: &SystemPath,
|
given_path: &SystemPath,
|
||||||
primary_message: &str,
|
primary_message: &str,
|
||||||
secondary_message: Option<&str>,
|
secondary_message: Option<&str>,
|
||||||
|
system: &dyn System,
|
||||||
) -> std::fmt::Result {
|
) -> std::fmt::Result {
|
||||||
let fallback: &mut dyn FnMut() -> std::fmt::Result = &mut || {
|
let fallback: &mut dyn FnMut() -> std::fmt::Result = &mut || {
|
||||||
f.write_str(primary_message)?;
|
f.write_str(primary_message)?;
|
||||||
|
@ -1003,7 +1035,7 @@ fn display_error(
|
||||||
return fallback();
|
return fallback();
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(config_file_source) = std::fs::read_to_string((**config_file_path).as_ref()) else {
|
let Ok(config_file_source) = system.read_to_string(config_file_path) else {
|
||||||
return fallback();
|
return fallback();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1099,7 +1131,10 @@ fn site_packages_directories_from_sys_prefix(
|
||||||
.is_directory(&site_packages)
|
.is_directory(&site_packages)
|
||||||
.then(|| SitePackagesPaths::from([site_packages]))
|
.then(|| SitePackagesPaths::from([site_packages]))
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
SitePackagesDiscoveryError::NoSitePackagesDirFound(sys_prefix_path.to_owned())
|
SitePackagesDiscoveryError::NoSitePackagesDirFound(
|
||||||
|
sys_prefix_path.to_owned(),
|
||||||
|
system.dyn_clone(),
|
||||||
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1206,10 +1241,12 @@ fn site_packages_directories_from_sys_prefix(
|
||||||
if found_at_least_one_lib_dir {
|
if found_at_least_one_lib_dir {
|
||||||
Err(SitePackagesDiscoveryError::NoSitePackagesDirFound(
|
Err(SitePackagesDiscoveryError::NoSitePackagesDirFound(
|
||||||
sys_prefix_path.to_owned(),
|
sys_prefix_path.to_owned(),
|
||||||
|
system.dyn_clone(),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Err(SitePackagesDiscoveryError::CouldNotReadLibDirectory(
|
Err(SitePackagesDiscoveryError::CouldNotReadLibDirectory(
|
||||||
sys_prefix_path.to_owned(),
|
sys_prefix_path.to_owned(),
|
||||||
|
system.dyn_clone(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1238,7 +1275,7 @@ fn real_stdlib_directory_from_sys_prefix(
|
||||||
if cfg!(target_os = "windows") {
|
if cfg!(target_os = "windows") {
|
||||||
let stdlib = sys_prefix_path.join("Lib");
|
let stdlib = sys_prefix_path.join("Lib");
|
||||||
return system.is_directory(&stdlib).then_some(stdlib).ok_or(
|
return system.is_directory(&stdlib).then_some(stdlib).ok_or(
|
||||||
StdlibDiscoveryError::NoStdlibFound(sys_prefix_path.to_owned()),
|
StdlibDiscoveryError::NoStdlibFound(sys_prefix_path.to_owned(), system.dyn_clone()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1274,7 +1311,11 @@ fn real_stdlib_directory_from_sys_prefix(
|
||||||
// must be `lib`, not `lib64`, for the stdlib
|
// must be `lib`, not `lib64`, for the stdlib
|
||||||
.read_directory(&sys_prefix_path.join(UnixLibDir::Lib))
|
.read_directory(&sys_prefix_path.join(UnixLibDir::Lib))
|
||||||
.map_err(|io_err| {
|
.map_err(|io_err| {
|
||||||
StdlibDiscoveryError::CouldNotReadLibDirectory(sys_prefix_path.to_owned(), io_err)
|
StdlibDiscoveryError::CouldNotReadLibDirectory(
|
||||||
|
sys_prefix_path.to_owned(),
|
||||||
|
io_err,
|
||||||
|
system.dyn_clone(),
|
||||||
|
)
|
||||||
})?
|
})?
|
||||||
{
|
{
|
||||||
let Ok(entry) = entry_result else {
|
let Ok(entry) = entry_result else {
|
||||||
|
@ -1299,6 +1340,7 @@ fn real_stdlib_directory_from_sys_prefix(
|
||||||
}
|
}
|
||||||
Err(StdlibDiscoveryError::NoStdlibFound(
|
Err(StdlibDiscoveryError::NoStdlibFound(
|
||||||
sys_prefix_path.to_owned(),
|
sys_prefix_path.to_owned(),
|
||||||
|
system.dyn_clone(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1357,6 +1399,7 @@ impl SysPrefixPath {
|
||||||
unvalidated_path.to_path_buf(),
|
unvalidated_path.to_path_buf(),
|
||||||
origin,
|
origin,
|
||||||
None,
|
None,
|
||||||
|
system.dyn_clone(),
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
sys_prefix
|
sys_prefix
|
||||||
|
@ -1376,12 +1419,14 @@ impl SysPrefixPath {
|
||||||
unvalidated_path,
|
unvalidated_path,
|
||||||
origin,
|
origin,
|
||||||
Some(io_err),
|
Some(io_err),
|
||||||
|
system.dyn_clone(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
SitePackagesDiscoveryError::CanonicalizationError(
|
SitePackagesDiscoveryError::CanonicalizationError(
|
||||||
unvalidated_path,
|
unvalidated_path,
|
||||||
origin,
|
origin,
|
||||||
io_err,
|
io_err,
|
||||||
|
system.dyn_clone(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
return Err(err);
|
return Err(err);
|
||||||
|
@ -1393,6 +1438,7 @@ impl SysPrefixPath {
|
||||||
unvalidated_path.to_path_buf(),
|
unvalidated_path.to_path_buf(),
|
||||||
origin,
|
origin,
|
||||||
None,
|
None,
|
||||||
|
system.dyn_clone(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -298,6 +298,10 @@ impl System for LSPSystem {
|
||||||
fn env_var(&self, name: &str) -> std::result::Result<String, std::env::VarError> {
|
fn env_var(&self, name: &str) -> std::result::Result<String, std::env::VarError> {
|
||||||
self.native_system.env_var(name)
|
self.native_system.env_var(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dyn_clone(&self) -> Box<dyn System> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn not_a_text_document(path: impl Display) -> std::io::Error {
|
fn not_a_text_document(path: impl Display) -> std::io::Error {
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
#![warn(
|
||||||
|
clippy::disallowed_methods,
|
||||||
|
reason = "Prefer System trait methods over std methods in ty crates"
|
||||||
|
)]
|
||||||
pub use env_vars::*;
|
pub use env_vars::*;
|
||||||
|
|
||||||
mod env_vars;
|
mod env_vars;
|
||||||
|
|
|
@ -268,6 +268,10 @@ impl System for MdtestSystem {
|
||||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dyn_clone(&self) -> Box<dyn System> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WritableSystem for MdtestSystem {
|
impl WritableSystem for MdtestSystem {
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
#![warn(
|
||||||
|
clippy::disallowed_methods,
|
||||||
|
reason = "Prefer System trait methods over std methods in ty crates"
|
||||||
|
)]
|
||||||
use ruff_db::vendored::VendoredFileSystem;
|
use ruff_db::vendored::VendoredFileSystem;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
|
|
|
@ -1231,6 +1231,10 @@ impl System for WasmSystem {
|
||||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dyn_clone(&self) -> Box<dyn System> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn not_found() -> std::io::Error {
|
fn not_found() -> std::io::Error {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue