## Summary Fixes https://github.com/astral-sh/ty/issues/556. On Windows, system installations have different layouts to virtual environments. In Windows virtual environments, the Python executable is found at `<sys.prefix>/Scripts/python.exe`. But in Windows system installations, the Python executable is found at `<sys.prefix>/python.exe`. That means that Windows users were able to point to Python executables inside virtual environments with the `--python` flag, but they weren't able to point to Python executables inside system installations. This PR fixes that issue. It also makes a couple of other changes: - Nearly all `sys.prefix` resolution is moved inside `site_packages.rs`. That was the original design of the `site-packages` resolution logic, but features implemented since the initial implementation have added some resolution and validation to `resolver.rs` inside the module resolver. That means that we've ended up with a somewhat confusing code structure and a situation where several checks are unnecessarily duplicated between the two modules. - I noticed that we had quite bad error messages if you e.g. pointed to a path that didn't exist on disk with `--python` (we just gave a somewhat impenetrable message saying that we "failed to canonicalize" the path). I improved the error messages here and added CLI tests for `--python` and the `environment.python` configuration setting. ## Test Plan - Existing tests pass - Added new CLI tests - I manually checked that virtual-environment discovery still works if no configuration is given - Micha did some manual testing to check that pointing `--python` to a system-installation executable now works on Windows
106 lines
2.8 KiB
Rust
106 lines
2.8 KiB
Rust
use anyhow::Result;
|
|
use std::sync::Arc;
|
|
use zip::CompressionMethod;
|
|
|
|
use ruff_db::files::{File, Files};
|
|
use ruff_db::system::{OsSystem, System, SystemPathBuf};
|
|
use ruff_db::vendored::{VendoredFileSystem, VendoredFileSystemBuilder};
|
|
use ruff_db::{Db as SourceDb, Upcast};
|
|
use ruff_python_ast::PythonVersion;
|
|
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
|
|
use ty_python_semantic::{
|
|
Db, Program, ProgramSettings, PythonPath, PythonPlatform, PythonVersionSource,
|
|
PythonVersionWithSource, SearchPathSettings, SysPrefixPathOrigin, default_lint_registry,
|
|
};
|
|
|
|
static EMPTY_VENDORED: std::sync::LazyLock<VendoredFileSystem> = std::sync::LazyLock::new(|| {
|
|
let mut builder = VendoredFileSystemBuilder::new(CompressionMethod::Stored);
|
|
builder.add_file("stdlib/VERSIONS", "\n").unwrap();
|
|
builder.finish().unwrap()
|
|
});
|
|
|
|
#[salsa::db]
|
|
#[derive(Default, Clone)]
|
|
pub struct ModuleDb {
|
|
storage: salsa::Storage<Self>,
|
|
files: Files,
|
|
system: OsSystem,
|
|
rule_selection: Arc<RuleSelection>,
|
|
}
|
|
|
|
impl ModuleDb {
|
|
/// Initialize a [`ModuleDb`] from the given source root.
|
|
pub fn from_src_roots(
|
|
src_roots: Vec<SystemPathBuf>,
|
|
python_version: PythonVersion,
|
|
venv_path: Option<SystemPathBuf>,
|
|
) -> Result<Self> {
|
|
let mut search_paths = SearchPathSettings::new(src_roots);
|
|
if let Some(venv_path) = venv_path {
|
|
search_paths.python_path =
|
|
PythonPath::sys_prefix(venv_path, SysPrefixPathOrigin::PythonCliFlag);
|
|
}
|
|
|
|
let db = Self::default();
|
|
Program::from_settings(
|
|
&db,
|
|
ProgramSettings {
|
|
python_version: Some(PythonVersionWithSource {
|
|
version: python_version,
|
|
source: PythonVersionSource::default(),
|
|
}),
|
|
python_platform: PythonPlatform::default(),
|
|
search_paths,
|
|
},
|
|
)?;
|
|
|
|
Ok(db)
|
|
}
|
|
}
|
|
|
|
impl Upcast<dyn SourceDb> for ModuleDb {
|
|
fn upcast(&self) -> &(dyn SourceDb + 'static) {
|
|
self
|
|
}
|
|
fn upcast_mut(&mut self) -> &mut (dyn SourceDb + 'static) {
|
|
self
|
|
}
|
|
}
|
|
|
|
#[salsa::db]
|
|
impl SourceDb for ModuleDb {
|
|
fn vendored(&self) -> &VendoredFileSystem {
|
|
&EMPTY_VENDORED
|
|
}
|
|
|
|
fn system(&self) -> &dyn System {
|
|
&self.system
|
|
}
|
|
|
|
fn files(&self) -> &Files {
|
|
&self.files
|
|
}
|
|
|
|
fn python_version(&self) -> PythonVersion {
|
|
Program::get(self).python_version(self)
|
|
}
|
|
}
|
|
|
|
#[salsa::db]
|
|
impl Db for ModuleDb {
|
|
fn is_file_open(&self, file: File) -> bool {
|
|
!file.path(self).is_vendored_path()
|
|
}
|
|
|
|
fn rule_selection(&self) -> &RuleSelection {
|
|
&self.rule_selection
|
|
}
|
|
|
|
fn lint_registry(&self) -> &LintRegistry {
|
|
default_lint_registry()
|
|
}
|
|
}
|
|
|
|
#[salsa::db]
|
|
impl salsa::Database for ModuleDb {}
|