mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Respect data scripts in uv tool install
(#4693)
## Summary Packages that provide scripts that _aren't_ Python entrypoints need to respected in `uv tool install`. For example, Ruff ships a script in `ruff-0.5.0.data/scripts`. Unfortunately, the `.data` directory doesn't exist in the virtual environment at all (it's removed, per the spec, after install). So this PR changes the entry point detection to look at the `RECORD` file, which is the only evidence that the scripts were installed. Closes https://github.com/astral-sh/uv/issues/4691. ## Test Plan `cargo run uv tool install ruff` (snapshot tests to-come)
This commit is contained in:
parent
081f092781
commit
324e9fe5cf
9 changed files with 89 additions and 80 deletions
|
@ -11,11 +11,10 @@ use zip::result::ZipError;
|
|||
use pep440_rs::Version;
|
||||
use platform_tags::{Arch, Os};
|
||||
use pypi_types::Scheme;
|
||||
pub use script::{scripts_from_ini, Script};
|
||||
pub use uninstall::{uninstall_egg, uninstall_legacy_editable, uninstall_wheel, Uninstall};
|
||||
use uv_fs::Simplified;
|
||||
use uv_normalize::PackageName;
|
||||
pub use wheel::{parse_wheel_file, LibKind};
|
||||
pub use wheel::{parse_wheel_file, read_record_file, LibKind};
|
||||
|
||||
pub mod linker;
|
||||
pub mod metadata;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Like `wheel.rs`, but for installing wheels that have already been unzipped, rather than
|
||||
//! reading from a zip file.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use std::time::SystemTime;
|
||||
|
||||
|
@ -143,24 +143,6 @@ pub fn install_wheel(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Determine the absolute path to an entrypoint script.
|
||||
pub fn entrypoint_path(entrypoint: &Script, layout: &Layout) -> PathBuf {
|
||||
if cfg!(windows) {
|
||||
// On windows we actually build an .exe wrapper
|
||||
let script_name = entrypoint
|
||||
.name
|
||||
// FIXME: What are the in-reality rules here for names?
|
||||
.strip_suffix(".py")
|
||||
.unwrap_or(&entrypoint.name)
|
||||
.to_string()
|
||||
+ ".exe";
|
||||
|
||||
layout.scheme.scripts.join(script_name)
|
||||
} else {
|
||||
layout.scheme.scripts.join(&entrypoint.name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the `dist-info` directory in an unzipped wheel.
|
||||
///
|
||||
/// See: <https://github.com/PyO3/python-pkginfo-rs>
|
||||
|
|
|
@ -8,9 +8,9 @@ use serde::{Deserialize, Serialize};
|
|||
/// tqdm-4.62.3.dist-info/RECORD,,
|
||||
/// ```
|
||||
#[derive(Deserialize, Serialize, PartialOrd, PartialEq, Ord, Eq)]
|
||||
pub(crate) struct RecordEntry {
|
||||
pub(crate) path: String,
|
||||
pub(crate) hash: Option<String>,
|
||||
pub struct RecordEntry {
|
||||
pub path: String,
|
||||
pub hash: Option<String>,
|
||||
#[allow(dead_code)]
|
||||
pub(crate) size: Option<u64>,
|
||||
pub size: Option<u64>,
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@ use crate::{wheel, Error};
|
|||
/// A script defining the name of the runnable entrypoint and the module and function that should be
|
||||
/// run.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
|
||||
pub struct Script {
|
||||
pub name: String,
|
||||
pub module: String,
|
||||
pub function: String,
|
||||
pub(crate) struct Script {
|
||||
pub(crate) name: String,
|
||||
pub(crate) module: String,
|
||||
pub(crate) function: String,
|
||||
}
|
||||
|
||||
impl Script {
|
||||
|
@ -64,7 +64,7 @@ impl Script {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn scripts_from_ini(
|
||||
pub(crate) fn scripts_from_ini(
|
||||
extras: Option<&[String]>,
|
||||
python_minor: u8,
|
||||
ini: String,
|
||||
|
|
|
@ -17,7 +17,6 @@ use zip::ZipWriter;
|
|||
use pypi_types::DirectUrl;
|
||||
use uv_fs::{relative_to, Simplified};
|
||||
|
||||
use crate::linker::entrypoint_path;
|
||||
use crate::record::RecordEntry;
|
||||
use crate::script::Script;
|
||||
use crate::{Error, Layout};
|
||||
|
@ -247,6 +246,24 @@ fn get_script_executable(python_executable: &Path, is_gui: bool) -> PathBuf {
|
|||
}
|
||||
}
|
||||
|
||||
/// Determine the absolute path to an entrypoint script.
|
||||
fn entrypoint_path(entrypoint: &Script, layout: &Layout) -> PathBuf {
|
||||
if cfg!(windows) {
|
||||
// On windows we actually build an .exe wrapper
|
||||
let script_name = entrypoint
|
||||
.name
|
||||
// FIXME: What are the in-reality rules here for names?
|
||||
.strip_suffix(".py")
|
||||
.unwrap_or(&entrypoint.name)
|
||||
.to_string()
|
||||
+ ".exe";
|
||||
|
||||
layout.scheme.scripts.join(script_name)
|
||||
} else {
|
||||
layout.scheme.scripts.join(&entrypoint.name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the wrapper scripts in the bin folder of the venv for launching console scripts.
|
||||
pub(crate) fn write_script_entrypoints(
|
||||
layout: &Layout,
|
||||
|
@ -632,7 +649,7 @@ pub(crate) fn extra_dist_info(
|
|||
|
||||
/// Reads the record file
|
||||
/// <https://www.python.org/dev/peps/pep-0376/#record>
|
||||
pub(crate) fn read_record_file(record: &mut impl Read) -> Result<Vec<RecordEntry>, Error> {
|
||||
pub fn read_record_file(record: &mut impl Read) -> Result<Vec<RecordEntry>, Error> {
|
||||
csv::ReaderBuilder::new()
|
||||
.has_headers(false)
|
||||
.escape(Some(b'"'))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue