virtualenv_rs/
packages.rs1use crate::bare::VenvPaths;
2use crate::interpreter::InterpreterInfo;
3use crate::{crate_cache_dir, Error};
4use camino::{FromPathBufError, Utf8Path, Utf8PathBuf};
5use fs_err as fs;
6use fs_err::File;
7use install_wheel_rs::{install_wheel, InstallLocation, WheelFilename};
8#[cfg(feature = "parallel")]
9use rayon::iter::{IntoParallelIterator, ParallelIterator};
10use std::io;
11use std::io::BufWriter;
12use std::str::FromStr;
13use tempfile::NamedTempFile;
14use tracing::info;
15
16pub fn download_wheel_cached(filename: &str, url: &str) -> Result<Utf8PathBuf, Error> {
17 let wheels_cache = crate_cache_dir()?.join("wheels");
18 let cached_wheel = wheels_cache.join(filename);
19 if cached_wheel.is_file() {
20 info!("Using cached wheel at {cached_wheel}");
21 return Ok(cached_wheel);
22 }
23
24 info!("Downloading wheel from {url} to {cached_wheel}");
25 fs::create_dir_all(&wheels_cache)?;
26 let mut tempfile = NamedTempFile::new_in(wheels_cache)?;
27 let tempfile_path: Utf8PathBuf = tempfile
28 .path()
29 .to_path_buf()
30 .try_into()
31 .map_err(|err: FromPathBufError| err.into_io_error())?;
32 let mut response = minreq::get(url).send_lazy()?;
33 io::copy(&mut response, &mut BufWriter::new(&mut tempfile)).map_err(|err| {
34 Error::WheelDownload {
35 url: url.to_string(),
36 path: tempfile_path.to_path_buf(),
37 err,
38 }
39 })?;
40 tempfile.persist(&cached_wheel)?;
41 Ok(cached_wheel)
42}
43
44pub fn install_base_packages(
46 location: &Utf8Path,
47 info: &InterpreterInfo,
48 paths: &VenvPaths,
49) -> Result<(), Error> {
50 let install_location = InstallLocation::Venv {
51 venv_base: location.canonicalize()?,
52 python_version: (info.major, info.minor),
53 };
54 let install_location = install_location.acquire_lock()?;
55
56 let packages = [
59 ("pip-23.2.1-py3-none-any.whl", "https://files.pythonhosted.org/packages/50/c2/e06851e8cc28dcad7c155f4753da8833ac06a5c704c109313b8d5a62968a/pip-23.2.1-py3-none-any.whl"),
60 ("setuptools-68.2.2-py3-none-any.whl", "https://files.pythonhosted.org/packages/bb/26/7945080113158354380a12ce26873dd6c1ebd88d47f5bc24e2c5bb38c16a/setuptools-68.2.2-py3-none-any.whl"),
61 ("wheel-0.41.2-py3-none-any.whl", "https://files.pythonhosted.org/packages/b8/8b/31273bf66016be6ad22bb7345c37ff350276cfd46e389a0c2ac5da9d9073/wheel-0.41.2-py3-none-any.whl"),
62 ];
63 #[cfg(feature = "rayon")]
64 let iterator = packages.into_par_iter();
65 #[cfg(not(feature = "rayon"))]
66 let iterator = packages.into_iter();
67 iterator
68 .map(|(filename, url)| {
69 let wheel_file = download_wheel_cached(filename, url)?;
70 let parsed_filename = WheelFilename::from_str(filename).unwrap();
71 install_wheel(
72 &install_location,
73 File::open(wheel_file)?,
74 parsed_filename,
75 false,
76 &[],
77 "",
79 paths.interpreter.as_std_path(),
80 )
81 .map_err(|err| Error::InstallWheel {
82 package: filename.to_string(),
83 err,
84 })?;
85 Ok(())
86 })
87 .collect::<Result<Vec<()>, Error>>()?;
88 Ok(())
89}