Skip to content

Commit

Permalink
Remove pip-tools, use uv for all operations
Browse files Browse the repository at this point in the history
Remove pip even from uv venvs
  • Loading branch information
nazq committed Jul 21, 2024
1 parent d3a8440 commit 7bbb42d
Show file tree
Hide file tree
Showing 15 changed files with 131 additions and 672 deletions.
5 changes: 2 additions & 3 deletions rye/src/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use once_cell::sync::Lazy;
use tempfile::tempdir_in;

use crate::config::Config;
use crate::piptools::LATEST_PIP;
use crate::platform::{
get_app_dir, get_canonical_py_path, get_python_bin_within, get_toolchain_python_bin,
list_known_toolchains,
Expand Down Expand Up @@ -154,8 +153,8 @@ pub fn ensure_self_venv_with_toolchain(
let uv_venv = uv.venv(&venv_dir, &py_bin, &version, None)?;
// write our marker
uv_venv.write_marker()?;
// update pip and our requirements
uv_venv.update(LATEST_PIP, SELF_REQUIREMENTS)?;
// update our requirements
uv_venv.update_requirements(SELF_REQUIREMENTS)?;

// Update the shims
let shims = app_dir.join("shims");
Expand Down
218 changes: 13 additions & 205 deletions rye/src/cli/add.rs
Original file line number Diff line number Diff line change
@@ -1,63 +1,22 @@
use std::env;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::str::FromStr;

use anyhow::{anyhow, bail, Context, Error};
use clap::{Parser, ValueEnum};
use pep440_rs::{Operator, Version, VersionSpecifier, VersionSpecifiers};
use pep440_rs::{Operator, VersionSpecifier, VersionSpecifiers};
use pep508_rs::{Requirement, VersionOrUrl};
use serde::Deserialize;
use url::Url;

use crate::bootstrap::ensure_self_venv;
use crate::config::Config;
use crate::consts::VENV_BIN;
use crate::lock::KeyringProvider;
use crate::pyproject::{BuildSystem, DependencyKind, ExpandedSources, PyProject};
use crate::sources::py::PythonVersion;
use crate::sync::{autosync, sync, SyncOptions};
use crate::utils::{format_requirement, get_venv_python_bin, set_proxy_variables, CommandOutput};
use crate::utils::{format_requirement, get_venv_python_bin, CommandOutput};
use crate::uv::UvBuilder;

const PACKAGE_FINDER_SCRIPT: &str = r#"
import sys
import json
from unearth.finder import PackageFinder
from unearth.session import PyPISession
from packaging.version import Version
py_ver = sys.argv[1]
package = sys.argv[2]
sources = json.loads(sys.argv[3])
pre = len(sys.argv) > 4 and sys.argv[4] == "--pre"
finder = PackageFinder(
index_urls=[x[0] for x in sources["index_urls"]],
find_links=sources["find_links"],
trusted_hosts=sources["trusted_hosts"],
)
if py_ver:
finder.target_python.py_ver = tuple(map(int, py_ver.split('.')))
choices = iter(finder.find_matches(package))
if not pre:
choices = (m for m in choices if not(m.version and Version(m.version).is_prerelease))
print(json.dumps([x.as_json() for x in choices]))
"#;

#[derive(Deserialize, Debug)]
struct Match {
name: String,
version: Option<String>,
link: Option<Link>,
}

#[derive(Deserialize, Debug)]
struct Link {
requires_python: Option<String>,
}

#[derive(Parser, Debug)]
pub struct ReqExtras {
/// Install the given package from this git repository
Expand Down Expand Up @@ -240,8 +199,7 @@ pub struct Args {

pub fn execute(cmd: Args) -> Result<(), Error> {
let output = CommandOutput::from_quiet_and_verbose(cmd.quiet, cmd.verbose);
let self_venv = ensure_self_venv(output).context("error bootstrapping venv")?;
let python_path = self_venv.join(VENV_BIN).join("python");
let _ = ensure_self_venv(output).context("error bootstrapping venv")?;
let cfg = Config::current();

let mut pyproject_toml = PyProject::discover()?;
Expand Down Expand Up @@ -272,34 +230,16 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
}

if !cmd.excluded {
if cfg.use_uv() {
sync(SyncOptions::python_only().pyproject(None))
.context("failed to sync ahead of add")?;
resolve_requirements_with_uv(
&pyproject_toml,
&py_ver,
&mut requirements,
cmd.pre,
output,
&default_operator,
cmd.keyring_provider,
)?;
} else {
if cmd.keyring_provider != KeyringProvider::Disabled {
bail!("`--keyring-provider` option requires the uv backend");
}
for requirement in &mut requirements {
resolve_requirements_with_unearth(
&pyproject_toml,
&python_path,
&py_ver,
requirement,
cmd.pre,
output,
&default_operator,
)?;
}
}
sync(SyncOptions::python_only().pyproject(None)).context("failed to sync ahead of add")?;
resolve_requirements_with_uv(
&pyproject_toml,
&py_ver,
&mut requirements,
cmd.pre,
output,
&default_operator,
cmd.keyring_provider,
)?;
}

for requirement in &requirements {
Expand Down Expand Up @@ -332,138 +272,6 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
Ok(())
}

fn resolve_requirements_with_unearth(
pyproject_toml: &PyProject,
python_path: &PathBuf,
py_ver: &PythonVersion,
requirement: &mut Requirement,
pre: bool,
output: CommandOutput,
default_operator: &Operator,
) -> Result<(), Error> {
let matches = find_best_matches_with_unearth(
pyproject_toml,
python_path,
Some(py_ver),
requirement,
pre,
)?;
if matches.is_empty() {
let all_matches =
find_best_matches_with_unearth(pyproject_toml, python_path, None, requirement, pre)
.unwrap_or_default();
if all_matches.is_empty() {
// if we did not consider pre-releases, maybe we could find it by doing so. In
// that case give the user a helpful warning before erroring.
if !pre {
let all_pre_matches = find_best_matches_with_unearth(
pyproject_toml,
python_path,
None,
requirement,
true,
)
.unwrap_or_default();
if let Some(pre) = all_pre_matches.into_iter().next() {
warn!(
"{} ({}) was found considering pre-releases. Pass --pre to allow use.",
pre.name,
pre.version.unwrap_or_default()
);
}
bail!(
"did not find package '{}' without using pre-releases.",
format_requirement(requirement)
);
} else {
bail!("did not find package '{}'", format_requirement(requirement));
}
} else {
if output != CommandOutput::Quiet {
echo!("Available package versions:");
for pkg in all_matches {
echo!(
" {} ({}) requires Python {}",
pkg.name,
pkg.version.unwrap_or_default(),
pkg.link
.as_ref()
.and_then(|x| x.requires_python.as_ref())
.map_or("unknown", |x| x as &str)
);
}
echo!("A possible solution is to raise the version in `requires-python` in `pyproject.toml`.");
}
bail!(
"did not find a version of package '{}' compatible with this version of Python.",
format_requirement(requirement)
);
}
}
let m = matches.into_iter().next().unwrap();
if m.version.is_some() && requirement.version_or_url.is_none() {
let version = Version::from_str(m.version.as_ref().unwrap())
.map_err(|msg| anyhow!("invalid version: {}", msg))?;
requirement.version_or_url = Some(VersionOrUrl::VersionSpecifier(
VersionSpecifiers::from_iter(Some(
VersionSpecifier::new(
// local versions or versions with only one component cannot
// use ~= but need to use ==.
match *default_operator {
_ if version.is_local() => Operator::Equal,
Operator::TildeEqual if version.release.len() < 2 => {
Operator::GreaterThanEqual
}
ref other => other.clone(),
},
Version::from_str(m.version.as_ref().unwrap())
.map_err(|msg| anyhow!("invalid version: {}", msg))?,
false,
)
.map_err(|msg| anyhow!("invalid version specifier: {}", msg))?,
)),
));
}
requirement.name = m.name;
Ok(())
}

fn find_best_matches_with_unearth(
pyproject: &PyProject,
python_path: &PathBuf,
py_ver: Option<&PythonVersion>,
requirement: &Requirement,
pre: bool,
) -> Result<Vec<Match>, Error> {
let mut unearth = Command::new(python_path);
let sources = ExpandedSources::from_sources(&pyproject.sources()?)?;

unearth
.arg("-c")
.arg(PACKAGE_FINDER_SCRIPT)
.arg(match py_ver {
Some(ver) => ver.format_simple(),
None => "".into(),
})
.arg(&format_requirement(requirement).to_string())
.arg(serde_json::to_string(&sources)?);
if pre {
unearth.arg("--pre");
}
set_proxy_variables(&mut unearth);
let unearth = unearth.stdout(Stdio::piped()).output()?;
if unearth.status.success() {
Ok(serde_json::from_slice(&unearth.stdout)?)
} else {
let log = String::from_utf8_lossy(&unearth.stderr);
bail!(
"failed to resolve package {}\n{}",
format_requirement(requirement),
log
);
}
}

fn resolve_requirements_with_uv(
pyproject_toml: &PyProject,
py_ver: &PythonVersion,
Expand Down
20 changes: 8 additions & 12 deletions rye/src/cli/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use clap::Parser;
use console::style;

use crate::bootstrap::ensure_self_venv;
use crate::config::Config;
use crate::pyproject::{locate_projects, PyProject};
use crate::utils::{get_venv_python_bin, prepend_path_to_path_env, CommandOutput, IoPathContext};
use crate::uv::UvBuilder;
Expand Down Expand Up @@ -63,7 +62,6 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
}
}

let use_uv = Config::current().use_uv();
let projects = locate_projects(project, cmd.all, &cmd.package[..])?;

for project in projects {
Expand All @@ -86,16 +84,14 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
.arg(&out)
.arg(&*project.root_path());

if use_uv {
// we need to ensure uv is available to use without installing it into self_venv
let uv = UvBuilder::new().with_output(output).ensure_exists()?;
let uv_dir = uv
.uv_bin()
.parent()
.ok_or_else(|| anyhow!("Could not find uv binary in self venv: empty path"))?;
build_cmd.env("PATH", prepend_path_to_path_env(uv_dir)?);
build_cmd.arg("--installer=uv");
}
// we need to ensure uv is available to use without installing it into self_venv
let uv = UvBuilder::new().with_output(output).ensure_exists()?;
let uv_dir = uv
.uv_bin()
.parent()
.ok_or_else(|| anyhow!("Could not find uv binary in self venv: empty path"))?;
build_cmd.env("PATH", prepend_path_to_path_env(uv_dir)?);
build_cmd.arg("--installer=uv");

if cmd.wheel {
build_cmd.arg("--wheel");
Expand Down
48 changes: 15 additions & 33 deletions rye/src/cli/list.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
use std::path::PathBuf;
use std::process::Command;

use anyhow::{bail, Error};
use anyhow::Error;
use clap::Parser;

use crate::bootstrap::ensure_self_venv;
use crate::config::Config;
use crate::consts::VENV_BIN;
use crate::pyproject::PyProject;
use crate::utils::{get_venv_python_bin, CommandOutput};
use crate::uv::{UvBuilder, UvWithVenv};
Expand All @@ -25,36 +22,21 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
if !python.is_file() {
return Ok(());
}
let self_venv = ensure_self_venv(CommandOutput::Normal)?;
let _ = ensure_self_venv(CommandOutput::Normal)?;

if Config::current().use_uv() {
let uv = UvBuilder::new()
.with_output(CommandOutput::Normal)
.ensure_exists()?;
if !project.rye_managed() {
UvWithVenv::new(uv, &project.venv_path(), &project.venv_python_version()?).freeze()?;
} else {
uv.venv(
&project.venv_path(),
&python,
&project.venv_python_version()?,
None,
)?
.freeze()?;
}
let uv = UvBuilder::new()
.with_output(CommandOutput::Normal)
.ensure_exists()?;
if !project.rye_managed() {
UvWithVenv::new(uv, &project.venv_path(), &project.venv_python_version()?).freeze()?;
} else {
let status = Command::new(self_venv.join(VENV_BIN).join("pip"))
.arg("--python")
.arg(&python)
.arg("freeze")
.env("PYTHONWARNINGS", "ignore")
.env("PIP_DISABLE_PIP_VERSION_CHECK", "1")
.status()?;

if !status.success() {
bail!("failed to print dependencies via pip");
}
};

uv.venv(
&project.venv_path(),
&python,
&project.venv_python_version()?,
None,
)?
.freeze()?;
}
Ok(())
}
Loading

0 comments on commit 7bbb42d

Please sign in to comment.