Search in both purelib and platlib for site-packages population (#2537)

## Summary

In reality, there's no such thing as the `site-packages` directory for a
given virtualenv. Rather, Python defines both `purelib` and `platlib`,
where the former is for pure-Python packages and the latter is for
packages that contain native code. These are almost always set to the
same thing... but they don't _have_ to be, and in fact of Fedora they
are not.

This PR changes the `site_packages` method to return an iterator of
directories.

Closes https://github.com/astral-sh/uv/issues/2527.
This commit is contained in:
Charlie Marsh 2024-03-18 20:06:16 -07:00 committed by GitHub
parent 142b2de191
commit ba14f69676
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 67 additions and 51 deletions

View file

@ -42,8 +42,9 @@ impl<'a> SitePackages<'a> {
let mut by_name = FxHashMap::default(); let mut by_name = FxHashMap::default();
let mut by_url = FxHashMap::default(); let mut by_url = FxHashMap::default();
for site_packages in venv.site_packages() {
// Read the site-packages directory. // Read the site-packages directory.
let site_packages = match fs::read_dir(venv.site_packages()) { let site_packages = match fs::read_dir(site_packages) {
Ok(site_packages) => site_packages, Ok(site_packages) => site_packages,
Err(err) if err.kind() == std::io::ErrorKind::NotFound => { Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
return Ok(Self { return Ok(Self {
@ -62,8 +63,10 @@ impl<'a> SitePackages<'a> {
if entry.file_type()?.is_dir() { if entry.file_type()?.is_dir() {
let path = entry.path(); let path = entry.path();
let Some(dist_info) = InstalledDist::try_from_path(&path) let Some(dist_info) =
.with_context(|| format!("Failed to read metadata: from {}", path.display()))? InstalledDist::try_from_path(&path).with_context(|| {
format!("Failed to read metadata: from {}", path.display())
})?
else { else {
continue; continue;
}; };
@ -85,6 +88,7 @@ impl<'a> SitePackages<'a> {
distributions.push(Some(dist_info)); distributions.push(Some(dist_info));
} }
} }
}
Ok(Self { Ok(Self {
venv, venv,

View file

@ -88,9 +88,18 @@ impl PythonEnvironment {
self.interpreter.sys_executable() self.interpreter.sys_executable()
} }
/// Returns the path to the `site-packages` directory inside a virtual environment. /// Returns an iterator over the `site-packages` directories inside a virtual environment.
pub fn site_packages(&self) -> &Path { ///
self.interpreter.purelib() /// In most cases, `purelib` and `platlib` will be the same, and so the iterator will contain
/// a single element; however, in some distributions, they may be different.
pub fn site_packages(&self) -> impl Iterator<Item = &Path> {
std::iter::once(self.interpreter.purelib()).chain(
if self.interpreter.purelib() == self.interpreter.platlib() {
None
} else {
Some(self.interpreter.platlib())
},
)
} }
/// Returns the path to the `bin` directory inside a virtual environment. /// Returns the path to the `bin` directory inside a virtual environment.

View file

@ -129,14 +129,17 @@ pub(super) async fn compile_bytecode(
printer: Printer, printer: Printer,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let start = std::time::Instant::now(); let start = std::time::Instant::now();
let files = compile_tree(venv.site_packages(), venv.python_executable(), cache.root()) let mut files = 0;
for site_packages in venv.site_packages() {
files += compile_tree(site_packages, venv.python_executable(), cache.root())
.await .await
.with_context(|| { .with_context(|| {
format!( format!(
"Failed to bytecode compile {}", "Failed to bytecode-compile Python file in: {}",
venv.site_packages().simplified_display() site_packages.simplified_display()
) )
})?; })?;
}
let s = if files == 1 { "" } else { "s" }; let s = if files == 1 { "" } else { "s" };
writeln!( writeln!(
printer.stderr(), printer.stderr(),

View file

@ -2948,7 +2948,7 @@ fn compile_invalid_pyc_invalidation_mode() -> Result<()> {
Resolved 1 package in [TIME] Resolved 1 package in [TIME]
Downloaded 1 package in [TIME] Downloaded 1 package in [TIME]
Installed 1 package in [TIME] Installed 1 package in [TIME]
error: Failed to bytecode compile [SITE-PACKAGES] error: Failed to bytecode-compile Python file in: [SITE-PACKAGES]
Caused by: Python process stderr: Caused by: Python process stderr:
Invalid value for PYC_INVALIDATION_MODE "bogus", valid are "TIMESTAMP", "CHECKED_HASH", "UNCHECKED_HASH": Invalid value for PYC_INVALIDATION_MODE "bogus", valid are "TIMESTAMP", "CHECKED_HASH", "UNCHECKED_HASH":
Caused by: Bytecode compilation failed, expected "[SITE-PACKAGES]/[FIRST-FILE]", received: "" Caused by: Bytecode compilation failed, expected "[SITE-PACKAGES]/[FIRST-FILE]", received: ""