Skip to content

Commit

Permalink
Docs and API
Browse files Browse the repository at this point in the history
  • Loading branch information
konstin committed Oct 5, 2023
1 parent 1b19b2c commit e70f3d8
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 25 deletions.
39 changes: 27 additions & 12 deletions crates/install-wheel-rs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
//! Takes a wheel and installs it, either in a venv or for monotrail
//!
//! ```no_run
//! use std::path::Path;
//! use install_wheel_rs::install_wheel_in_venv;
//!
//! install_wheel_in_venv(
//! "Django-4.2.6-py3-none-any.whl",
//! ".venv",
//! ".venv/bin/python",
//! (3, 8),
//! ).unwrap();
//! ```
use std::fs::File;
// The pub ones are reused by monotrail
pub use install_location::{normalize_name, InstallLocation, LockedDir};
use platform_info::PlatformInfoError;
use std::fs::File;
use std::io;
use std::path::Path;
use std::str::FromStr;
use thiserror::Error;
use zip::result::ZipError;

pub use install_location::{normalize_name, InstallLocation, LockedDir};
pub use wheel::{
get_script_launcher, install_wheel, parse_key_value_file, read_record_file, relative_to,
Script, MONOTRAIL_SCRIPT_SHEBANG,
Script, SHEBANG_PYTHON,
};
pub use wheel_tags::{Arch, CompatibleTags, Os, WheelFilename};
use zip::result::ZipError;

mod install_location;
#[cfg(feature = "python_bindings")]
Expand Down Expand Up @@ -71,22 +83,25 @@ impl Error {

/// High level API: Install a wheel in a virtualenv
///
/// The python interpreter is used for compiling to byte code, the python version for computing
/// the site packages path on unix.
///
/// Returns the tag of the wheel
pub fn install_wheel_in_venv(
wheel: &Path,
venv: &Path,
interpreter: &Path,
major: u8,
minor: u8,
wheel: impl AsRef<Path>,
venv: impl AsRef<Path>,
interpreter: impl AsRef<Path>,
major_minor: (u8, u8),
) -> Result<String, Error> {
let venv_base = venv.canonicalize()?;
let venv_base = venv.as_ref().canonicalize()?;
let location = InstallLocation::Venv {
venv_base,
python_version: (major, minor),
python_version: major_minor,
};
let locked_dir = location.acquire_lock()?;

let filename = wheel
.as_ref()
.file_name()
.ok_or_else(|| Error::InvalidWheel("Expected a file".to_string()))?
.to_string_lossy();
Expand Down
2 changes: 1 addition & 1 deletion crates/install-wheel-rs/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ fn main() -> Result<(), Error> {
&[],
// Only relevant for monotrail style installation
"",
&location.get_python(),
location.get_python(),
)?;
Ok(())
})
Expand Down
14 changes: 9 additions & 5 deletions crates/install-wheel-rs/src/wheel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ use zip::result::ZipError;
use zip::write::FileOptions;
use zip::{ZipArchive, ZipWriter};

pub const MONOTRAIL_SCRIPT_SHEBANG: &str = "#!/usr/bin/env python";
/// `#!/usr/bin/env python`
pub const SHEBANG_PYTHON: &str = "#!/usr/bin/env python";

pub const LAUNCHER_T32: &[u8] = include_bytes!("../windows-launcher/t32.exe");
pub const LAUNCHER_T64: &[u8] = include_bytes!("../windows-launcher/t64.exe");
Expand Down Expand Up @@ -55,7 +56,8 @@ struct DirectUrl {
url: String,
}

/// A script from pyproject.toml
/// A script defining the name of the runnable entrypoint and the module and function that should be
/// run.
#[cfg(feature = "python_bindings")]
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[pyo3::pyclass(dict)]
Expand All @@ -68,6 +70,8 @@ pub struct Script {
pub function: String,
}

/// A script defining the name of the runnable entrypoint and the module and function that should be
/// run.
#[cfg(not(feature = "python_bindings"))]
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct Script {
Expand Down Expand Up @@ -361,7 +365,7 @@ fn get_shebang(location: &InstallLocation<LockedDir>) -> String {
} else {
// This will use the monotrail binary moonlighting as python. `python` alone doesn't,
// we need env to find the python link we put in PATH
MONOTRAIL_SCRIPT_SHEBANG.to_string()
SHEBANG_PYTHON.to_string()
}
}

Expand Down Expand Up @@ -1016,7 +1020,7 @@ pub fn install_wheel(
// it for validation later
_extras: &[String],
unique_version: &str,
sys_executable: &Path,
sys_executable: impl AsRef<Path>,
) -> Result<String, Error> {
let name = &filename.distribution;
let _my_span = span!(Level::DEBUG, "install_wheel", name = name.as_str());
Expand Down Expand Up @@ -1142,7 +1146,7 @@ pub fn install_wheel(
&site_packages,
unpacked_paths,
location.get_python_version(),
&sys_executable,
sys_executable.as_ref(),
name.as_str(),
&mut record,
)?;
Expand Down
32 changes: 31 additions & 1 deletion crates/install-wheel-rs/src/wheel_tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,36 @@ use std::process::{Command, Stdio};
use std::str::FromStr;
use tracing::trace;

/// The name of a wheel split into its parts ([PEP 491](https://peps.python.org/pep-0491/))
///
/// Ignores the build tag atm.
///
/// ```
/// use std::str::FromStr;
/// use install_wheel_rs::WheelFilename;
///
/// let filename = WheelFilename::from_str("foo-1.0-py32-none-any.whl").unwrap();
/// assert_eq!(filename, WheelFilename {
/// distribution: "foo".to_string(),
/// version: "1.0".to_string(),
/// python_tag: vec!["py32".to_string()],
/// abi_tag: vec!["none".to_string()],
/// platform_tag: vec!["any".to_string()]
/// });
/// let filename = WheelFilename::from_str(
/// "numpy-1.26.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl"
/// ).unwrap();
/// assert_eq!(filename, WheelFilename {
/// distribution: "numpy".to_string(),
/// version: "1.26.0".to_string(),
/// python_tag: vec!["cp312".to_string()],
/// abi_tag: vec!["cp312".to_string()],
/// platform_tag: vec![
/// "manylinux_2_17_aarch64".to_string(),
/// "manylinux2014_aarch64".to_string()
/// ]
/// });
/// ```
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct WheelFilename {
pub distribution: String,
Expand Down Expand Up @@ -74,7 +104,7 @@ impl WheelFilename {
})
}

/// effectively undoes the wheel filename parsing step
/// Effectively undoes the wheel filename parsing step
pub fn get_tag(&self) -> String {
format!(
"{}-{}-{}",
Expand Down
5 changes: 2 additions & 3 deletions crates/monotrail/src/inject_and_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::standalone_python::provision_python;
use crate::DEFAULT_PYTHON_VERSION;
use anyhow::{bail, format_err, Context};
use fs_err as fs;
use install_wheel_rs::{get_script_launcher, Script, MONOTRAIL_SCRIPT_SHEBANG};
use install_wheel_rs::{get_script_launcher, Script, SHEBANG_PYTHON};
use libc::{c_int, c_void, wchar_t};
use libloading::Library;
use std::collections::BTreeMap;
Expand Down Expand Up @@ -529,8 +529,7 @@ pub fn prepare_execve_environment(
// install the root project, we also didn't install the root scripts and never generated the
// wrapper scripts. We add them here instead.
for (script_name, script) in root_scripts {
let launcher =
get_script_launcher(&script.module, &script.function, MONOTRAIL_SCRIPT_SHEBANG);
let launcher = get_script_launcher(&script.module, &script.function, SHEBANG_PYTHON);
fs::write(path_dir.join(script_name), &launcher)
.with_context(|| format!("Failed to write launcher for {}", script_name))?;
}
Expand Down
6 changes: 3 additions & 3 deletions crates/monotrail/src/monotrail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::utils::{cache_dir, get_dir_content};
use anyhow::{bail, Context};
use fs_err as fs;
use fs_err::{DirEntry, File};
use install_wheel_rs::{CompatibleTags, InstallLocation, Script, MONOTRAIL_SCRIPT_SHEBANG};
use install_wheel_rs::{CompatibleTags, InstallLocation, Script, SHEBANG_PYTHON};
use pep508_rs::MarkerEnvironment;
use serde::Serialize;
use std::collections::{BTreeMap, HashMap};
Expand Down Expand Up @@ -658,9 +658,9 @@ pub fn is_python_script(executable: &Path) -> anyhow::Result<bool> {
.context("the executable file was right there and is now unreadable ಠ_ಠ")?;
// scripts might be binaries, so we read an exact number of bytes instead of the first line as string
let mut start = Vec::new();
start.resize(MONOTRAIL_SCRIPT_SHEBANG.as_bytes().len(), 0);
start.resize(SHEBANG_PYTHON.as_bytes().len(), 0);
executable_file.read_exact(&mut start)?;
let is_python_script = start == MONOTRAIL_SCRIPT_SHEBANG.as_bytes();
let is_python_script = start == SHEBANG_PYTHON.as_bytes();
Ok(is_python_script)
}

Expand Down

0 comments on commit e70f3d8

Please sign in to comment.