mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 14:21:53 +00:00
[red-knot] Allow multiple site-packages
search paths (#12609)
This commit is contained in:
parent
9aa43d5f91
commit
fbab04fbe1
11 changed files with 143 additions and 80 deletions
|
@ -104,7 +104,7 @@ pub fn main() -> anyhow::Result<()> {
|
||||||
extra_paths,
|
extra_paths,
|
||||||
workspace_root: workspace_metadata.root().to_path_buf(),
|
workspace_root: workspace_metadata.root().to_path_buf(),
|
||||||
custom_typeshed: custom_typeshed_dir,
|
custom_typeshed: custom_typeshed_dir,
|
||||||
site_packages: None,
|
site_packages: vec![],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -181,7 +181,7 @@ where
|
||||||
extra_paths: vec![],
|
extra_paths: vec![],
|
||||||
workspace_root: workspace_path.to_path_buf(),
|
workspace_root: workspace_path.to_path_buf(),
|
||||||
custom_typeshed: None,
|
custom_typeshed: None,
|
||||||
site_packages: None,
|
site_packages: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -697,7 +697,7 @@ fn search_path() -> anyhow::Result<()> {
|
||||||
extra_paths: vec![],
|
extra_paths: vec![],
|
||||||
workspace_root: workspace_path.to_path_buf(),
|
workspace_root: workspace_path.to_path_buf(),
|
||||||
custom_typeshed: None,
|
custom_typeshed: None,
|
||||||
site_packages: Some(root_path.join("site_packages")),
|
site_packages: vec![root_path.join("site_packages")],
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -734,7 +734,7 @@ fn add_search_path() -> anyhow::Result<()> {
|
||||||
|
|
||||||
// Register site-packages as a search path.
|
// Register site-packages as a search path.
|
||||||
case.update_search_path_settings(|settings| SearchPathSettings {
|
case.update_search_path_settings(|settings| SearchPathSettings {
|
||||||
site_packages: Some(site_packages.clone()),
|
site_packages: vec![site_packages.clone()],
|
||||||
..settings.clone()
|
..settings.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -757,14 +757,14 @@ fn remove_search_path() -> anyhow::Result<()> {
|
||||||
extra_paths: vec![],
|
extra_paths: vec![],
|
||||||
workspace_root: workspace_path.to_path_buf(),
|
workspace_root: workspace_path.to_path_buf(),
|
||||||
custom_typeshed: None,
|
custom_typeshed: None,
|
||||||
site_packages: Some(root_path.join("site_packages")),
|
site_packages: vec![root_path.join("site_packages")],
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Remove site packages from the search path settings.
|
// Remove site packages from the search path settings.
|
||||||
let site_packages = case.root_path().join("site_packages");
|
let site_packages = case.root_path().join("site_packages");
|
||||||
case.update_search_path_settings(|settings| SearchPathSettings {
|
case.update_search_path_settings(|settings| SearchPathSettings {
|
||||||
site_packages: None,
|
site_packages: vec![],
|
||||||
..settings.clone()
|
..settings.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1175,7 +1175,7 @@ mod unix {
|
||||||
extra_paths: vec![],
|
extra_paths: vec![],
|
||||||
workspace_root: workspace.to_path_buf(),
|
workspace_root: workspace.to_path_buf(),
|
||||||
custom_typeshed: None,
|
custom_typeshed: None,
|
||||||
site_packages: Some(workspace.join(".venv/lib/python3.12/site-packages")),
|
site_packages: vec![workspace.join(".venv/lib/python3.12/site-packages")],
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
|
@ -477,12 +477,6 @@ impl SearchPath {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Does this search path point to the `site-packages` directory?
|
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn is_site_packages(&self) -> bool {
|
|
||||||
matches!(&*self.0, SearchPathInner::SitePackages(_))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_valid_extension(&self, extension: &str) -> bool {
|
fn is_valid_extension(&self, extension: &str) -> bool {
|
||||||
if self.is_standard_library() {
|
if self.is_standard_library() {
|
||||||
extension == "pyi"
|
extension == "pyi"
|
||||||
|
|
|
@ -160,12 +160,6 @@ fn try_resolve_module_resolution_settings(
|
||||||
SearchPath::vendored_stdlib()
|
SearchPath::vendored_stdlib()
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(site_packages) = site_packages {
|
|
||||||
files.try_add_root(db.upcast(), site_packages, FileRootKind::LibrarySearchPath);
|
|
||||||
|
|
||||||
static_search_paths.push(SearchPath::site_packages(system, site_packages.clone())?);
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO vendor typeshed's third-party stubs as well as the stdlib and fallback to them as a final step
|
// TODO vendor typeshed's third-party stubs as well as the stdlib and fallback to them as a final step
|
||||||
|
|
||||||
let target_version = program.target_version(db.upcast());
|
let target_version = program.target_version(db.upcast());
|
||||||
|
@ -191,6 +185,7 @@ fn try_resolve_module_resolution_settings(
|
||||||
Ok(ModuleResolutionSettings {
|
Ok(ModuleResolutionSettings {
|
||||||
target_version,
|
target_version,
|
||||||
static_search_paths,
|
static_search_paths,
|
||||||
|
site_packages_paths: site_packages.to_owned(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,36 +195,55 @@ pub(crate) fn module_resolution_settings(db: &dyn Db) -> ModuleResolutionSetting
|
||||||
try_resolve_module_resolution_settings(db).unwrap()
|
try_resolve_module_resolution_settings(db).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Collect all dynamic search paths:
|
/// Collect all dynamic search paths. For each `site-packages` path:
|
||||||
/// search paths listed in `.pth` files in the `site-packages` directory
|
/// - Collect that `site-packages` path
|
||||||
|
/// - Collect any search paths listed in `.pth` files in that `site-packages` directory
|
||||||
/// due to editable installations of third-party packages.
|
/// due to editable installations of third-party packages.
|
||||||
|
///
|
||||||
|
/// The editable-install search paths for the first `site-packages` directory
|
||||||
|
/// should come between the two `site-packages` directories when it comes to
|
||||||
|
/// module-resolution priority.
|
||||||
#[salsa::tracked(return_ref)]
|
#[salsa::tracked(return_ref)]
|
||||||
pub(crate) fn editable_install_resolution_paths(db: &dyn Db) -> Vec<SearchPath> {
|
pub(crate) fn dynamic_resolution_paths(db: &dyn Db) -> Vec<SearchPath> {
|
||||||
let settings = module_resolution_settings(db);
|
let ModuleResolutionSettings {
|
||||||
let static_search_paths = &settings.static_search_paths;
|
target_version: _,
|
||||||
|
static_search_paths,
|
||||||
|
site_packages_paths,
|
||||||
|
} = module_resolution_settings(db);
|
||||||
|
|
||||||
let site_packages = static_search_paths
|
let mut dynamic_paths = Vec::new();
|
||||||
|
|
||||||
|
if site_packages_paths.is_empty() {
|
||||||
|
return dynamic_paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut existing_paths: FxHashSet<_> = static_search_paths
|
||||||
.iter()
|
.iter()
|
||||||
.find(|path| path.is_site_packages());
|
.filter_map(|path| path.as_system_path())
|
||||||
|
.map(Cow::Borrowed)
|
||||||
|
.collect();
|
||||||
|
|
||||||
let Some(site_packages) = site_packages else {
|
let files = db.files();
|
||||||
return Vec::new();
|
let system = db.system();
|
||||||
};
|
|
||||||
|
|
||||||
let site_packages = site_packages
|
|
||||||
.as_system_path()
|
|
||||||
.expect("Expected site-packages never to be a VendoredPath!");
|
|
||||||
|
|
||||||
let mut dynamic_paths = Vec::default();
|
|
||||||
|
|
||||||
|
for site_packages_dir in site_packages_paths {
|
||||||
|
if !existing_paths.insert(Cow::Borrowed(site_packages_dir)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let site_packages_root = files.try_add_root(
|
||||||
|
db.upcast(),
|
||||||
|
site_packages_dir,
|
||||||
|
FileRootKind::LibrarySearchPath,
|
||||||
|
);
|
||||||
// This query needs to be re-executed each time a `.pth` file
|
// This query needs to be re-executed each time a `.pth` file
|
||||||
// is added, modified or removed from the `site-packages` directory.
|
// is added, modified or removed from the `site-packages` directory.
|
||||||
// However, we don't use Salsa queries to read the source text of `.pth` files;
|
// However, we don't use Salsa queries to read the source text of `.pth` files;
|
||||||
// we use the APIs on the `System` trait directly. As such, add a dependency on the
|
// we use the APIs on the `System` trait directly. As such, add a dependency on the
|
||||||
// site-package directory's revision.
|
// site-package directory's revision.
|
||||||
if let Some(site_packages_root) = db.files().root(db.upcast(), site_packages) {
|
site_packages_root.revision(db.upcast());
|
||||||
let _ = site_packages_root.revision(db.upcast());
|
|
||||||
}
|
dynamic_paths
|
||||||
|
.push(SearchPath::site_packages(system, site_packages_dir.to_owned()).unwrap());
|
||||||
|
|
||||||
// As well as modules installed directly into `site-packages`,
|
// As well as modules installed directly into `site-packages`,
|
||||||
// the directory may also contain `.pth` files.
|
// the directory may also contain `.pth` files.
|
||||||
|
@ -237,8 +251,8 @@ pub(crate) fn editable_install_resolution_paths(db: &dyn Db) -> Vec<SearchPath>
|
||||||
// containing a (relative or absolute) path.
|
// containing a (relative or absolute) path.
|
||||||
// Each of these paths may point to an editable install of a package,
|
// Each of these paths may point to an editable install of a package,
|
||||||
// so should be considered an additional search path.
|
// so should be considered an additional search path.
|
||||||
let Ok(pth_file_iterator) = PthFileIterator::new(db, site_packages) else {
|
let Ok(pth_file_iterator) = PthFileIterator::new(db, site_packages_dir) else {
|
||||||
return dynamic_paths;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The Python documentation specifies that `.pth` files in `site-packages`
|
// The Python documentation specifies that `.pth` files in `site-packages`
|
||||||
|
@ -247,14 +261,6 @@ pub(crate) fn editable_install_resolution_paths(db: &dyn Db) -> Vec<SearchPath>
|
||||||
let mut all_pth_files: Vec<PthFile> = pth_file_iterator.collect();
|
let mut all_pth_files: Vec<PthFile> = pth_file_iterator.collect();
|
||||||
all_pth_files.sort_by(|a, b| a.path.cmp(&b.path));
|
all_pth_files.sort_by(|a, b| a.path.cmp(&b.path));
|
||||||
|
|
||||||
let mut existing_paths: FxHashSet<_> = static_search_paths
|
|
||||||
.iter()
|
|
||||||
.filter_map(|path| path.as_system_path())
|
|
||||||
.map(Cow::Borrowed)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
dynamic_paths.reserve(all_pth_files.len());
|
|
||||||
|
|
||||||
for pth_file in &all_pth_files {
|
for pth_file in &all_pth_files {
|
||||||
for installation in pth_file.editable_installations() {
|
for installation in pth_file.editable_installations() {
|
||||||
if existing_paths.insert(Cow::Owned(
|
if existing_paths.insert(Cow::Owned(
|
||||||
|
@ -264,6 +270,7 @@ pub(crate) fn editable_install_resolution_paths(db: &dyn Db) -> Vec<SearchPath>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dynamic_paths
|
dynamic_paths
|
||||||
}
|
}
|
||||||
|
@ -293,7 +300,7 @@ impl<'db> Iterator for SearchPathIterator<'db> {
|
||||||
|
|
||||||
static_paths.next().or_else(|| {
|
static_paths.next().or_else(|| {
|
||||||
dynamic_paths
|
dynamic_paths
|
||||||
.get_or_insert_with(|| editable_install_resolution_paths(*db).iter())
|
.get_or_insert_with(|| dynamic_resolution_paths(*db).iter())
|
||||||
.next()
|
.next()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -403,9 +410,18 @@ impl<'db> Iterator for PthFileIterator<'db> {
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub(crate) struct ModuleResolutionSettings {
|
pub(crate) struct ModuleResolutionSettings {
|
||||||
target_version: TargetVersion,
|
target_version: TargetVersion,
|
||||||
|
|
||||||
/// Search paths that have been statically determined purely from reading Ruff's configuration settings.
|
/// Search paths that have been statically determined purely from reading Ruff's configuration settings.
|
||||||
/// These shouldn't ever change unless the config settings themselves change.
|
/// These shouldn't ever change unless the config settings themselves change.
|
||||||
static_search_paths: Vec<SearchPath>,
|
static_search_paths: Vec<SearchPath>,
|
||||||
|
|
||||||
|
/// site-packages paths are not included in the above field:
|
||||||
|
/// if there are multiple site-packages paths, editable installations can appear
|
||||||
|
/// *between* the site-packages paths on `sys.path` at runtime.
|
||||||
|
/// That means we can't know where a second or third `site-packages` path should sit
|
||||||
|
/// in terms of module-resolution priority until we've discovered the editable installs
|
||||||
|
/// for the first `site-packages` path
|
||||||
|
site_packages_paths: Vec<SystemPathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleResolutionSettings {
|
impl ModuleResolutionSettings {
|
||||||
|
@ -630,6 +646,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
use ruff_db::Db;
|
use ruff_db::Db;
|
||||||
|
|
||||||
|
use crate::db::tests::TestDb;
|
||||||
use crate::module::ModuleKind;
|
use crate::module::ModuleKind;
|
||||||
use crate::module_name::ModuleName;
|
use crate::module_name::ModuleName;
|
||||||
use crate::testing::{FileSpec, MockedTypeshed, TestCase, TestCaseBuilder};
|
use crate::testing::{FileSpec, MockedTypeshed, TestCase, TestCaseBuilder};
|
||||||
|
@ -1180,7 +1197,7 @@ mod tests {
|
||||||
extra_paths: vec![],
|
extra_paths: vec![],
|
||||||
workspace_root: src.clone(),
|
workspace_root: src.clone(),
|
||||||
custom_typeshed: Some(custom_typeshed.clone()),
|
custom_typeshed: Some(custom_typeshed.clone()),
|
||||||
site_packages: Some(site_packages.clone()),
|
site_packages: vec![site_packages],
|
||||||
};
|
};
|
||||||
|
|
||||||
Program::new(&db, TargetVersion::Py38, search_paths);
|
Program::new(&db, TargetVersion::Py38, search_paths);
|
||||||
|
@ -1578,7 +1595,7 @@ not_a_directory
|
||||||
&FilePath::system("/y/src/bar.py")
|
&FilePath::system("/y/src/bar.py")
|
||||||
);
|
);
|
||||||
let events = db.take_salsa_events();
|
let events = db.take_salsa_events();
|
||||||
assert_const_function_query_was_not_run(&db, editable_install_resolution_paths, &events);
|
assert_const_function_query_was_not_run(&db, dynamic_resolution_paths, &events);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1656,4 +1673,53 @@ not_a_directory
|
||||||
assert!(!search_paths
|
assert!(!search_paths
|
||||||
.contains(&&SearchPath::editable(db.system(), SystemPathBuf::from("/src")).unwrap()));
|
.contains(&&SearchPath::editable(db.system(), SystemPathBuf::from("/src")).unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_site_packages_with_editables() {
|
||||||
|
let mut db = TestDb::new();
|
||||||
|
|
||||||
|
let venv_site_packages = SystemPathBuf::from("/venv-site-packages");
|
||||||
|
let site_packages_pth = venv_site_packages.join("foo.pth");
|
||||||
|
let system_site_packages = SystemPathBuf::from("/system-site-packages");
|
||||||
|
let editable_install_location = SystemPathBuf::from("/x/y/a.py");
|
||||||
|
let system_site_packages_location = system_site_packages.join("a.py");
|
||||||
|
|
||||||
|
db.memory_file_system()
|
||||||
|
.create_directory_all("/src")
|
||||||
|
.unwrap();
|
||||||
|
db.write_files([
|
||||||
|
(&site_packages_pth, "/x/y"),
|
||||||
|
(&editable_install_location, ""),
|
||||||
|
(&system_site_packages_location, ""),
|
||||||
|
])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Program::new(
|
||||||
|
&db,
|
||||||
|
TargetVersion::default(),
|
||||||
|
SearchPathSettings {
|
||||||
|
extra_paths: vec![],
|
||||||
|
workspace_root: SystemPathBuf::from("/src"),
|
||||||
|
custom_typeshed: None,
|
||||||
|
site_packages: vec![venv_site_packages, system_site_packages],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// The editable installs discovered from the `.pth` file in the first `site-packages` directory
|
||||||
|
// take precedence over the second `site-packages` directory...
|
||||||
|
let a_module_name = ModuleName::new_static("a").unwrap();
|
||||||
|
let a_module = resolve_module(&db, a_module_name.clone()).unwrap();
|
||||||
|
assert_eq!(a_module.file().path(&db), &editable_install_location);
|
||||||
|
|
||||||
|
db.memory_file_system()
|
||||||
|
.remove_file(&site_packages_pth)
|
||||||
|
.unwrap();
|
||||||
|
File::sync_path(&mut db, &site_packages_pth);
|
||||||
|
|
||||||
|
// ...But now that the `.pth` file in the first `site-packages` directory has been deleted,
|
||||||
|
// the editable install no longer exists, so the module now resolves to the file in the
|
||||||
|
// second `site-packages` directory
|
||||||
|
let a_module = resolve_module(&db, a_module_name).unwrap();
|
||||||
|
assert_eq!(a_module.file().path(&db), &system_site_packages_location);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,9 @@ pub(crate) struct TestCase<T> {
|
||||||
pub(crate) db: TestDb,
|
pub(crate) db: TestDb,
|
||||||
pub(crate) src: SystemPathBuf,
|
pub(crate) src: SystemPathBuf,
|
||||||
pub(crate) stdlib: T,
|
pub(crate) stdlib: T,
|
||||||
|
// Most test cases only ever need a single `site-packages` directory,
|
||||||
|
// so this is a single directory instead of a `Vec` of directories,
|
||||||
|
// like it is in `ruff_db::Program`.
|
||||||
pub(crate) site_packages: SystemPathBuf,
|
pub(crate) site_packages: SystemPathBuf,
|
||||||
pub(crate) target_version: TargetVersion,
|
pub(crate) target_version: TargetVersion,
|
||||||
}
|
}
|
||||||
|
@ -223,7 +226,7 @@ impl TestCaseBuilder<MockedTypeshed> {
|
||||||
extra_paths: vec![],
|
extra_paths: vec![],
|
||||||
workspace_root: src.clone(),
|
workspace_root: src.clone(),
|
||||||
custom_typeshed: Some(typeshed.clone()),
|
custom_typeshed: Some(typeshed.clone()),
|
||||||
site_packages: Some(site_packages.clone()),
|
site_packages: vec![site_packages.clone()],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -276,7 +279,7 @@ impl TestCaseBuilder<VendoredTypeshed> {
|
||||||
extra_paths: vec![],
|
extra_paths: vec![],
|
||||||
workspace_root: src.clone(),
|
workspace_root: src.clone(),
|
||||||
custom_typeshed: None,
|
custom_typeshed: None,
|
||||||
site_packages: Some(site_packages.clone()),
|
site_packages: vec![site_packages.clone()],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -179,7 +179,7 @@ mod tests {
|
||||||
SearchPathSettings {
|
SearchPathSettings {
|
||||||
extra_paths: vec![],
|
extra_paths: vec![],
|
||||||
workspace_root: SystemPathBuf::from("/src"),
|
workspace_root: SystemPathBuf::from("/src"),
|
||||||
site_packages: None,
|
site_packages: vec![],
|
||||||
custom_typeshed: None,
|
custom_typeshed: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -1515,7 +1515,7 @@ mod tests {
|
||||||
SearchPathSettings {
|
SearchPathSettings {
|
||||||
extra_paths: Vec::new(),
|
extra_paths: Vec::new(),
|
||||||
workspace_root: SystemPathBuf::from("/src"),
|
workspace_root: SystemPathBuf::from("/src"),
|
||||||
site_packages: None,
|
site_packages: vec![],
|
||||||
custom_typeshed: None,
|
custom_typeshed: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1532,7 +1532,7 @@ mod tests {
|
||||||
SearchPathSettings {
|
SearchPathSettings {
|
||||||
extra_paths: Vec::new(),
|
extra_paths: Vec::new(),
|
||||||
workspace_root: SystemPathBuf::from("/src"),
|
workspace_root: SystemPathBuf::from("/src"),
|
||||||
site_packages: None,
|
site_packages: vec![],
|
||||||
custom_typeshed: Some(SystemPathBuf::from(typeshed)),
|
custom_typeshed: Some(SystemPathBuf::from(typeshed)),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -326,7 +326,7 @@ mod tests {
|
||||||
SearchPathSettings {
|
SearchPathSettings {
|
||||||
extra_paths: Vec::new(),
|
extra_paths: Vec::new(),
|
||||||
workspace_root,
|
workspace_root,
|
||||||
site_packages: None,
|
site_packages: vec![],
|
||||||
custom_typeshed: None,
|
custom_typeshed: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,7 +14,7 @@ fn setup_db(workspace_root: SystemPathBuf) -> anyhow::Result<RootDatabase> {
|
||||||
extra_paths: vec![],
|
extra_paths: vec![],
|
||||||
workspace_root,
|
workspace_root,
|
||||||
custom_typeshed: None,
|
custom_typeshed: None,
|
||||||
site_packages: None,
|
site_packages: vec![],
|
||||||
};
|
};
|
||||||
let settings = ProgramSettings {
|
let settings = ProgramSettings {
|
||||||
target_version: TargetVersion::default(),
|
target_version: TargetVersion::default(),
|
||||||
|
|
|
@ -74,7 +74,7 @@ fn setup_case() -> Case {
|
||||||
search_paths: SearchPathSettings {
|
search_paths: SearchPathSettings {
|
||||||
extra_paths: vec![],
|
extra_paths: vec![],
|
||||||
workspace_root: workspace_root.to_path_buf(),
|
workspace_root: workspace_root.to_path_buf(),
|
||||||
site_packages: None,
|
site_packages: vec![],
|
||||||
custom_typeshed: None,
|
custom_typeshed: None,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -81,5 +81,5 @@ pub struct SearchPathSettings {
|
||||||
pub custom_typeshed: Option<SystemPathBuf>,
|
pub custom_typeshed: Option<SystemPathBuf>,
|
||||||
|
|
||||||
/// The path to the user's `site-packages` directory, where third-party packages from ``PyPI`` are installed.
|
/// The path to the user's `site-packages` directory, where third-party packages from ``PyPI`` are installed.
|
||||||
pub site_packages: Option<SystemPathBuf>,
|
pub site_packages: Vec<SystemPathBuf>,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue