install_wheel_rs/
lib.rs

1//! Takes a wheel and installs it, either in a venv or for monotrail
2
3use std::fs::File;
4// The pub ones are reused by monotrail
5pub use install_location::{normalize_name, InstallLocation, LockedDir};
6use platform_info::PlatformInfoError;
7use std::io;
8use std::path::Path;
9use std::str::FromStr;
10use thiserror::Error;
11pub use wheel::{
12    get_script_launcher, install_wheel, parse_key_value_file, read_record_file, relative_to,
13    Script, MONOTRAIL_SCRIPT_SHEBANG,
14};
15pub use wheel_tags::{Arch, CompatibleTags, Os, WheelFilename};
16use zip::result::ZipError;
17
18mod install_location;
19#[cfg(feature = "python_bindings")]
20mod python_bindings;
21mod wheel;
22mod wheel_tags;
23
24#[derive(Error, Debug)]
25pub enum Error {
26    #[error(transparent)]
27    IO(#[from] io::Error),
28    /// This shouldn't actually be possible to occur
29    #[error("Failed to serialize direct_url.json ಠ_ಠ")]
30    DirectUrlSerdeJson(#[source] serde_json::Error),
31    /// Tags/metadata didn't match platform
32    #[error("The wheel is incompatible with the current platform {os} {arch}")]
33    IncompatibleWheel { os: Os, arch: Arch },
34    /// The wheel is broken
35    #[error("The wheel is invalid: {0}")]
36    InvalidWheel(String),
37    /// pyproject.toml or poetry.lock are broken
38    #[error("The poetry dependency specification (pyproject.toml or poetry.lock) is broken (try `poetry update`?): {0}")]
39    InvalidPoetry(String),
40    /// Doesn't follow file name schema
41    #[error("The wheel filename \"{0}\" is invalid: {1}")]
42    InvalidWheelFileName(String, String),
43    #[error("Failed to read the wheel file {0}")]
44    Zip(String, #[source] ZipError),
45    #[error("Failed to run python subcommand")]
46    PythonSubcommand(#[source] io::Error),
47    #[error("Failed to move data files")]
48    WalkDir(#[from] walkdir::Error),
49    #[error("RECORD file doesn't match wheel contents: {0}")]
50    RecordFile(String),
51    #[error("RECORD file is invalid")]
52    RecordCsv(#[from] csv::Error),
53    #[error("Broken virtualenv: {0}")]
54    BrokenVenv(String),
55    #[error("Failed to detect the operating system version: {0}")]
56    OsVersionDetection(String),
57    #[error("Failed to detect the current platform")]
58    PlatformInfo(#[source] PlatformInfoError),
59    #[error("Invalid version specification, only none or == is supported")]
60    Pep440,
61}
62
63/// High level API: Install a wheel in a virtualenv
64///
65/// Returns the tag of the wheel
66pub fn install_wheel_in_venv(
67    wheel: &Path,
68    venv: &Path,
69    interpreter: &Path,
70    major: u8,
71    minor: u8,
72) -> Result<String, Error> {
73    let venv_base = venv.canonicalize()?;
74    let location = InstallLocation::Venv {
75        venv_base,
76        python_version: (major, minor),
77    };
78    let locked_dir = location.acquire_lock()?;
79
80    let filename = wheel
81        .file_name()
82        .ok_or_else(|| Error::InvalidWheel("Expected a file".to_string()))?
83        .to_string_lossy();
84    let filename = WheelFilename::from_str(&filename)?;
85    let compatible_tags = CompatibleTags::current(location.get_python_version())?;
86    filename.compatibility(&compatible_tags)?;
87
88    install_wheel(
89        &locked_dir,
90        File::open(wheel)?,
91        filename,
92        false,
93        &[],
94        // Only relevant for monotrail style installation
95        "",
96        interpreter,
97    )
98}