mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-03 05:03:46 +00:00
Respect HTTP client options when reading remote requirements files (#2434)
Uses the base client introduced in #2431 to restore use of our fully configured client when reading remote requirements files. Closes https://github.com/astral-sh/uv/issues/2357 ## Test plan ``` npx http-server --username user --password password cargo run -- pip install -r http://user:password@127.0.0.1:8080/requirements.txt ``` Fails on main succeeds on branch.
This commit is contained in:
parent
9654da418e
commit
c6e181d233
14 changed files with 191 additions and 102 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -3008,6 +3008,7 @@ dependencies = [
|
|||
"pep508_rs",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"reqwest-middleware",
|
||||
"serde",
|
||||
"tempfile",
|
||||
"test-case",
|
||||
|
|
|
|||
|
|
@ -23,11 +23,15 @@ async-recursion = { workspace = true }
|
|||
fs-err = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
reqwest = { workspace = true, optional = true }
|
||||
reqwest-middleware = { workspace = true, optional = true }
|
||||
serde = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
unscanny = { workspace = true }
|
||||
url = { workspace = true }
|
||||
|
||||
[features]
|
||||
http = ["reqwest", "reqwest-middleware"]
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { version = "1.0.80" }
|
||||
assert_fs = { version = "1.1.1" }
|
||||
|
|
|
|||
|
|
@ -49,7 +49,9 @@ use pep508_rs::{
|
|||
expand_env_vars, split_scheme, Extras, Pep508Error, Pep508ErrorSource, Requirement,
|
||||
RequirementsTxtRequirement, Scheme, VerbatimUrl,
|
||||
};
|
||||
use uv_client::Connectivity;
|
||||
#[cfg(feature = "http")]
|
||||
use uv_client::BaseClient;
|
||||
use uv_client::BaseClientBuilder;
|
||||
use uv_fs::{normalize_url_path, Simplified};
|
||||
use uv_normalize::ExtraName;
|
||||
use uv_warnings::warn_user;
|
||||
|
|
@ -332,38 +334,39 @@ impl RequirementsTxt {
|
|||
pub async fn parse(
|
||||
requirements_txt: impl AsRef<Path>,
|
||||
working_dir: impl AsRef<Path>,
|
||||
connectivity: Connectivity,
|
||||
client_builder: &BaseClientBuilder<'_>,
|
||||
) -> Result<Self, RequirementsTxtFileError> {
|
||||
let requirements_txt = requirements_txt.as_ref();
|
||||
let working_dir = working_dir.as_ref();
|
||||
|
||||
let content =
|
||||
if requirements_txt.starts_with("http://") | requirements_txt.starts_with("https://") {
|
||||
#[cfg(not(feature = "reqwest"))]
|
||||
#[cfg(not(feature = "http"))]
|
||||
{
|
||||
return Err(RequirementsTxtFileError {
|
||||
file: requirements_txt.to_path_buf(),
|
||||
error: RequirementsTxtParserError::IO(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Remote file not supported without `reqwest` feature",
|
||||
"Remote file not supported without `http` feature",
|
||||
)),
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "reqwest")]
|
||||
#[cfg(feature = "http")]
|
||||
{
|
||||
match connectivity {
|
||||
Connectivity::Online => read_url_to_string(&requirements_txt).await,
|
||||
Connectivity::Offline => {
|
||||
return Err(RequirementsTxtFileError {
|
||||
file: requirements_txt.to_path_buf(),
|
||||
error: RequirementsTxtParserError::IO(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
format!("Network connectivity is disabled, but a remote requirements file was requested: {}", requirements_txt.display()),
|
||||
)),
|
||||
});
|
||||
}
|
||||
// Avoid constructing a client if network is disabled already
|
||||
if client_builder.is_offline() {
|
||||
return Err(RequirementsTxtFileError {
|
||||
file: requirements_txt.to_path_buf(),
|
||||
error: RequirementsTxtParserError::IO(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
format!("Network connectivity is disabled, but a remote requirements file was requested: {}", requirements_txt.display()),
|
||||
)),
|
||||
});
|
||||
}
|
||||
|
||||
let client = client_builder.build();
|
||||
read_url_to_string(&requirements_txt, client).await
|
||||
}
|
||||
} else {
|
||||
uv_fs::read_to_string_transcode(&requirements_txt)
|
||||
|
|
@ -376,7 +379,7 @@ impl RequirementsTxt {
|
|||
})?;
|
||||
|
||||
let requirements_dir = requirements_txt.parent().unwrap_or(working_dir);
|
||||
let data = Self::parse_inner(&content, working_dir, requirements_dir, connectivity)
|
||||
let data = Self::parse_inner(&content, working_dir, requirements_dir, client_builder)
|
||||
.await
|
||||
.map_err(|err| RequirementsTxtFileError {
|
||||
file: requirements_txt.to_path_buf(),
|
||||
|
|
@ -403,7 +406,7 @@ impl RequirementsTxt {
|
|||
content: &str,
|
||||
working_dir: &Path,
|
||||
requirements_dir: &Path,
|
||||
connectivity: Connectivity,
|
||||
client_builder: &BaseClientBuilder<'async_recursion>,
|
||||
) -> Result<Self, RequirementsTxtParserError> {
|
||||
let mut s = Scanner::new(content);
|
||||
|
||||
|
|
@ -422,7 +425,7 @@ impl RequirementsTxt {
|
|||
} else {
|
||||
requirements_dir.join(filename.as_ref())
|
||||
};
|
||||
let sub_requirements = Self::parse(&sub_file, working_dir, connectivity)
|
||||
let sub_requirements = Self::parse(&sub_file, working_dir, client_builder)
|
||||
.await
|
||||
.map_err(|err| RequirementsTxtParserError::Subfile {
|
||||
source: Box::new(err),
|
||||
|
|
@ -460,13 +463,13 @@ impl RequirementsTxt {
|
|||
} else {
|
||||
requirements_dir.join(filename.as_ref())
|
||||
};
|
||||
let sub_constraints = Self::parse(&sub_file, working_dir, connectivity)
|
||||
let sub_constraints = Self::parse(&sub_file, working_dir, client_builder)
|
||||
.await
|
||||
.map_err(|err| RequirementsTxtParserError::Subfile {
|
||||
source: Box::new(err),
|
||||
start,
|
||||
end,
|
||||
})?;
|
||||
source: Box::new(err),
|
||||
start,
|
||||
end,
|
||||
})?;
|
||||
// Treat any nested requirements or constraints as constraints. This differs
|
||||
// from `pip`, which seems to treat `-r` requirements in constraints files as
|
||||
// _requirements_, but we don't want to support that.
|
||||
|
|
@ -819,8 +822,11 @@ fn parse_value<'a, T>(
|
|||
}
|
||||
|
||||
/// Fetch the contents of a URL and return them as a string.
|
||||
#[cfg(feature = "reqwest")]
|
||||
async fn read_url_to_string(path: impl AsRef<Path>) -> Result<String, RequirementsTxtParserError> {
|
||||
#[cfg(feature = "http")]
|
||||
async fn read_url_to_string(
|
||||
path: impl AsRef<Path>,
|
||||
client: BaseClient,
|
||||
) -> Result<String, RequirementsTxtParserError> {
|
||||
// pip would URL-encode the non-UTF-8 bytes of the string; we just don't support them.
|
||||
let path_utf8 =
|
||||
path.as_ref()
|
||||
|
|
@ -828,7 +834,11 @@ async fn read_url_to_string(path: impl AsRef<Path>) -> Result<String, Requiremen
|
|||
.ok_or_else(|| RequirementsTxtParserError::NonUnicodeUrl {
|
||||
url: path.as_ref().to_owned(),
|
||||
})?;
|
||||
Ok(reqwest::get(path_utf8)
|
||||
|
||||
Ok(client
|
||||
.client()
|
||||
.get(path_utf8)
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?
|
||||
.text()
|
||||
|
|
@ -882,8 +892,8 @@ pub enum RequirementsTxtParserError {
|
|||
NonUnicodeUrl {
|
||||
url: PathBuf,
|
||||
},
|
||||
#[cfg(feature = "reqwest")]
|
||||
Reqwest(reqwest::Error),
|
||||
#[cfg(feature = "http")]
|
||||
Reqwest(reqwest_middleware::Error),
|
||||
}
|
||||
|
||||
impl RequirementsTxtParserError {
|
||||
|
|
@ -935,7 +945,7 @@ impl RequirementsTxtParserError {
|
|||
end: end + offset,
|
||||
},
|
||||
Self::NonUnicodeUrl { url } => Self::NonUnicodeUrl { url },
|
||||
#[cfg(feature = "reqwest")]
|
||||
#[cfg(feature = "http")]
|
||||
Self::Reqwest(err) => Self::Reqwest(err),
|
||||
}
|
||||
}
|
||||
|
|
@ -983,7 +993,7 @@ impl Display for RequirementsTxtParserError {
|
|||
url.display(),
|
||||
)
|
||||
}
|
||||
#[cfg(feature = "reqwest")]
|
||||
#[cfg(feature = "http")]
|
||||
Self::Reqwest(err) => {
|
||||
write!(f, "Error while accessing remote requirements file {err}")
|
||||
}
|
||||
|
|
@ -1005,7 +1015,7 @@ impl std::error::Error for RequirementsTxtParserError {
|
|||
Self::Subfile { source, .. } => Some(source.as_ref()),
|
||||
Self::Parser { .. } => None,
|
||||
Self::NonUnicodeUrl { .. } => None,
|
||||
#[cfg(feature = "reqwest")]
|
||||
#[cfg(feature = "http")]
|
||||
Self::Reqwest(err) => err.source(),
|
||||
}
|
||||
}
|
||||
|
|
@ -1089,7 +1099,7 @@ impl Display for RequirementsTxtFileError {
|
|||
url.display(),
|
||||
)
|
||||
}
|
||||
#[cfg(feature = "reqwest")]
|
||||
#[cfg(feature = "http")]
|
||||
RequirementsTxtParserError::Reqwest(err) => {
|
||||
write!(
|
||||
f,
|
||||
|
|
@ -1113,9 +1123,16 @@ impl From<io::Error> for RequirementsTxtParserError {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "reqwest")]
|
||||
#[cfg(feature = "http")]
|
||||
impl From<reqwest::Error> for RequirementsTxtParserError {
|
||||
fn from(err: reqwest::Error) -> Self {
|
||||
Self::Reqwest(reqwest_middleware::Error::Reqwest(err))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "http")]
|
||||
impl From<reqwest_middleware::Error> for RequirementsTxtParserError {
|
||||
fn from(err: reqwest_middleware::Error) -> Self {
|
||||
Self::Reqwest(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -1172,8 +1189,7 @@ mod test {
|
|||
use tempfile::tempdir;
|
||||
use test_case::test_case;
|
||||
use unscanny::Scanner;
|
||||
|
||||
use uv_client::Connectivity;
|
||||
use uv_client::BaseClientBuilder;
|
||||
use uv_fs::Simplified;
|
||||
|
||||
use crate::{calculate_row_column, EditableRequirement, RequirementsTxt};
|
||||
|
|
@ -1197,9 +1213,10 @@ mod test {
|
|||
let working_dir = workspace_test_data_dir().join("requirements-txt");
|
||||
let requirements_txt = working_dir.join(path);
|
||||
|
||||
let actual = RequirementsTxt::parse(requirements_txt, &working_dir, Connectivity::Offline)
|
||||
.await
|
||||
.unwrap();
|
||||
let actual =
|
||||
RequirementsTxt::parse(requirements_txt, &working_dir, &BaseClientBuilder::new())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let snapshot = format!("parse-{}", path.to_string_lossy());
|
||||
insta::assert_debug_snapshot!(snapshot, actual);
|
||||
|
|
@ -1241,9 +1258,10 @@ mod test {
|
|||
let requirements_txt = temp_dir.path().join(path);
|
||||
fs::write(&requirements_txt, contents).unwrap();
|
||||
|
||||
let actual = RequirementsTxt::parse(&requirements_txt, &working_dir, Connectivity::Offline)
|
||||
.await
|
||||
.unwrap();
|
||||
let actual =
|
||||
RequirementsTxt::parse(&requirements_txt, &working_dir, &BaseClientBuilder::new())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let snapshot = format!("line-endings-{}", path.to_string_lossy());
|
||||
insta::assert_debug_snapshot!(snapshot, actual);
|
||||
|
|
@ -1256,9 +1274,10 @@ mod test {
|
|||
let working_dir = workspace_test_data_dir().join("requirements-txt");
|
||||
let requirements_txt = working_dir.join(path);
|
||||
|
||||
let actual = RequirementsTxt::parse(requirements_txt, &working_dir, Connectivity::Offline)
|
||||
.await
|
||||
.unwrap();
|
||||
let actual =
|
||||
RequirementsTxt::parse(requirements_txt, &working_dir, &BaseClientBuilder::new())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let snapshot = format!("parse-unix-{}", path.to_string_lossy());
|
||||
let pattern = regex::escape(&working_dir.simplified_display().to_string());
|
||||
|
|
@ -1277,9 +1296,10 @@ mod test {
|
|||
let working_dir = workspace_test_data_dir().join("requirements-txt");
|
||||
let requirements_txt = working_dir.join(path);
|
||||
|
||||
let actual = RequirementsTxt::parse(requirements_txt, &working_dir, Connectivity::Offline)
|
||||
.await
|
||||
.unwrap();
|
||||
let actual =
|
||||
RequirementsTxt::parse(requirements_txt, &working_dir, &BaseClientBuilder::new())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let snapshot = format!("parse-windows-{}", path.to_string_lossy());
|
||||
let pattern = regex::escape(
|
||||
|
|
@ -1308,7 +1328,7 @@ mod test {
|
|||
let error = RequirementsTxt::parse(
|
||||
requirements_txt.path(),
|
||||
temp_dir.path(),
|
||||
Connectivity::Offline,
|
||||
&BaseClientBuilder::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
|
@ -1348,7 +1368,7 @@ mod test {
|
|||
let error = RequirementsTxt::parse(
|
||||
requirements_txt.path(),
|
||||
temp_dir.path(),
|
||||
Connectivity::Offline,
|
||||
&BaseClientBuilder::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
|
@ -1384,7 +1404,7 @@ mod test {
|
|||
let error = RequirementsTxt::parse(
|
||||
requirements_txt.path(),
|
||||
temp_dir.path(),
|
||||
Connectivity::Offline,
|
||||
&BaseClientBuilder::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
|
@ -1420,7 +1440,7 @@ mod test {
|
|||
let error = RequirementsTxt::parse(
|
||||
requirements_txt.path(),
|
||||
temp_dir.path(),
|
||||
Connectivity::Offline,
|
||||
&BaseClientBuilder::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
|
@ -1451,7 +1471,7 @@ mod test {
|
|||
let error = RequirementsTxt::parse(
|
||||
requirements_txt.path(),
|
||||
temp_dir.path(),
|
||||
Connectivity::Offline,
|
||||
&BaseClientBuilder::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
|
@ -1484,7 +1504,7 @@ mod test {
|
|||
let error = RequirementsTxt::parse(
|
||||
requirements_txt.path(),
|
||||
temp_dir.path(),
|
||||
Connectivity::Offline,
|
||||
&BaseClientBuilder::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
|
@ -1518,7 +1538,7 @@ mod test {
|
|||
let error = RequirementsTxt::parse(
|
||||
requirements_txt.path(),
|
||||
temp_dir.path(),
|
||||
Connectivity::Offline,
|
||||
&BaseClientBuilder::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
|
@ -1557,7 +1577,7 @@ mod test {
|
|||
let error = RequirementsTxt::parse(
|
||||
requirements_txt.path(),
|
||||
temp_dir.path(),
|
||||
Connectivity::Offline,
|
||||
&BaseClientBuilder::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
|
@ -1600,10 +1620,13 @@ mod test {
|
|||
-r subdir/child.txt
|
||||
"})?;
|
||||
|
||||
let requirements =
|
||||
RequirementsTxt::parse(parent_txt.path(), temp_dir.path(), Connectivity::Offline)
|
||||
.await
|
||||
.unwrap();
|
||||
let requirements = RequirementsTxt::parse(
|
||||
parent_txt.path(),
|
||||
temp_dir.path(),
|
||||
&BaseClientBuilder::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
insta::assert_debug_snapshot!(requirements, @r###"
|
||||
RequirementsTxt {
|
||||
requirements: [
|
||||
|
|
@ -1658,7 +1681,7 @@ mod test {
|
|||
let requirements = RequirementsTxt::parse(
|
||||
requirements_txt.path(),
|
||||
temp_dir.path(),
|
||||
Connectivity::Offline,
|
||||
&BaseClientBuilder::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
@ -1722,7 +1745,7 @@ mod test {
|
|||
let error = RequirementsTxt::parse(
|
||||
requirements_txt.path(),
|
||||
temp_dir.path(),
|
||||
Connectivity::Offline,
|
||||
&BaseClientBuilder::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
|
@ -1775,7 +1798,7 @@ mod test {
|
|||
let error = RequirementsTxt::parse(
|
||||
requirements_txt.path(),
|
||||
temp_dir.path(),
|
||||
Connectivity::Offline,
|
||||
&BaseClientBuilder::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@ pub struct BaseClientBuilder<'a> {
|
|||
platform: Option<&'a Platform>,
|
||||
}
|
||||
|
||||
impl Default for BaseClientBuilder<'_> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl BaseClientBuilder<'_> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
|
@ -88,7 +94,11 @@ impl<'a> BaseClientBuilder<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> BaseClient {
|
||||
pub fn is_offline(&self) -> bool {
|
||||
matches!(self.connectivity, Connectivity::Offline)
|
||||
}
|
||||
|
||||
pub fn build(&self) -> BaseClient {
|
||||
// Create user agent.
|
||||
let mut user_agent_string = format!("uv/{}", version());
|
||||
|
||||
|
|
@ -118,7 +128,7 @@ impl<'a> BaseClientBuilder<'a> {
|
|||
debug!("Using registry request timeout of {}s", timeout);
|
||||
|
||||
// Initialize the base client.
|
||||
let client = self.client.unwrap_or_else(|| {
|
||||
let client = self.client.clone().unwrap_or_else(|| {
|
||||
// Check for the presence of an `SSL_CERT_FILE`.
|
||||
let ssl_cert_file_exists = env::var_os("SSL_CERT_FILE").is_some_and(|path| {
|
||||
let path_exists = Path::new(&path).exists();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
pub use base_client::BaseClient;
|
||||
pub use base_client::{BaseClient, BaseClientBuilder};
|
||||
pub use cached_client::{CacheControl, CachedClient, CachedClientError, DataWithCachePolicy};
|
||||
pub use error::{BetterReqwestError, Error, ErrorKind};
|
||||
pub use flat_index::{FlatDistributions, FlatIndex, FlatIndexClient, FlatIndexError};
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::{ExtrasSpecification, RequirementsSource};
|
|||
use distribution_types::{FlatIndexLocation, IndexUrl};
|
||||
use pep508_rs::{Requirement, RequirementsTxtRequirement};
|
||||
use requirements_txt::{EditableRequirement, FindLink, RequirementsTxt};
|
||||
use uv_client::Connectivity;
|
||||
use uv_client::BaseClientBuilder;
|
||||
use uv_fs::Simplified;
|
||||
use uv_normalize::{ExtraName, PackageName};
|
||||
use uv_warnings::warn_user;
|
||||
|
|
@ -44,7 +44,7 @@ impl RequirementsSpecification {
|
|||
pub async fn from_source(
|
||||
source: &RequirementsSource,
|
||||
extras: &ExtrasSpecification<'_>,
|
||||
connectivity: Connectivity,
|
||||
client_builder: &BaseClientBuilder<'_>,
|
||||
) -> Result<Self> {
|
||||
Ok(match source {
|
||||
RequirementsSource::Package(name) => {
|
||||
|
|
@ -81,7 +81,7 @@ impl RequirementsSpecification {
|
|||
}
|
||||
RequirementsSource::RequirementsTxt(path) => {
|
||||
let requirements_txt =
|
||||
RequirementsTxt::parse(path, std::env::current_dir()?, connectivity).await?;
|
||||
RequirementsTxt::parse(path, std::env::current_dir()?, client_builder).await?;
|
||||
Self {
|
||||
project: None,
|
||||
requirements: requirements_txt
|
||||
|
|
@ -185,7 +185,7 @@ impl RequirementsSpecification {
|
|||
constraints: &[RequirementsSource],
|
||||
overrides: &[RequirementsSource],
|
||||
extras: &ExtrasSpecification<'_>,
|
||||
connectivity: Connectivity,
|
||||
client_builder: &BaseClientBuilder<'_>,
|
||||
) -> Result<Self> {
|
||||
let mut spec = Self::default();
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ impl RequirementsSpecification {
|
|||
// A `requirements.txt` can contain a `-c constraints.txt` directive within it, so reading
|
||||
// a requirements file can also add constraints.
|
||||
for source in requirements {
|
||||
let source = Self::from_source(source, extras, connectivity).await?;
|
||||
let source = Self::from_source(source, extras, client_builder).await?;
|
||||
spec.requirements.extend(source.requirements);
|
||||
spec.constraints.extend(source.constraints);
|
||||
spec.overrides.extend(source.overrides);
|
||||
|
|
@ -220,7 +220,7 @@ impl RequirementsSpecification {
|
|||
|
||||
// Read all constraints, treating _everything_ as a constraint.
|
||||
for source in constraints {
|
||||
let source = Self::from_source(source, extras, connectivity).await?;
|
||||
let source = Self::from_source(source, extras, client_builder).await?;
|
||||
for requirement in source.requirements {
|
||||
match requirement {
|
||||
RequirementsTxtRequirement::Pep508(requirement) => {
|
||||
|
|
@ -251,7 +251,7 @@ impl RequirementsSpecification {
|
|||
|
||||
// Read all overrides, treating both requirements _and_ constraints as overrides.
|
||||
for source in overrides {
|
||||
let source = Self::from_source(source, extras, connectivity).await?;
|
||||
let source = Self::from_source(source, extras, client_builder).await?;
|
||||
for requirement in source.requirements {
|
||||
match requirement {
|
||||
RequirementsTxtRequirement::Pep508(requirement) => {
|
||||
|
|
@ -286,14 +286,14 @@ impl RequirementsSpecification {
|
|||
/// Read the requirements from a set of sources.
|
||||
pub async fn from_simple_sources(
|
||||
requirements: &[RequirementsSource],
|
||||
connectivity: Connectivity,
|
||||
client_builder: &BaseClientBuilder<'_>,
|
||||
) -> Result<Self> {
|
||||
Self::from_sources(
|
||||
requirements,
|
||||
&[],
|
||||
&[],
|
||||
&ExtrasSpecification::None,
|
||||
connectivity,
|
||||
client_builder,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use anyhow::Result;
|
|||
use rustc_hash::FxHashSet;
|
||||
|
||||
use requirements_txt::RequirementsTxt;
|
||||
use uv_client::Connectivity;
|
||||
use uv_client::{BaseClientBuilder, Connectivity};
|
||||
use uv_normalize::PackageName;
|
||||
use uv_resolver::{Preference, PreferenceError};
|
||||
|
||||
|
|
@ -58,9 +58,12 @@ pub async fn read_lockfile(
|
|||
};
|
||||
|
||||
// Parse the requirements from the lockfile.
|
||||
let requirements_txt =
|
||||
RequirementsTxt::parse(output_file, std::env::current_dir()?, Connectivity::Offline)
|
||||
.await?;
|
||||
let requirements_txt = RequirementsTxt::parse(
|
||||
output_file,
|
||||
std::env::current_dir()?,
|
||||
&BaseClientBuilder::new().connectivity(Connectivity::Offline),
|
||||
)
|
||||
.await?;
|
||||
let preferences = requirements_txt
|
||||
.requirements
|
||||
.into_iter()
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ install-wheel-rs = { workspace = true, features = ["clap"], default-features = f
|
|||
pep508_rs = { workspace = true }
|
||||
platform-tags = { workspace = true }
|
||||
pypi-types = { workspace = true }
|
||||
requirements-txt = { workspace = true, features = ["reqwest"] }
|
||||
requirements-txt = { workspace = true, features = ["http"] }
|
||||
uv-auth = { workspace = true, features = ["clap"] }
|
||||
uv-cache = { workspace = true, features = ["clap"] }
|
||||
uv-client = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ use platform_tags::Tags;
|
|||
use requirements_txt::EditableRequirement;
|
||||
use uv_auth::{KeyringProvider, GLOBAL_AUTH_STORE};
|
||||
use uv_cache::Cache;
|
||||
use uv_client::{Connectivity, FlatIndex, FlatIndexClient, RegistryClientBuilder};
|
||||
use uv_client::{
|
||||
BaseClientBuilder, Connectivity, FlatIndex, FlatIndexClient, RegistryClientBuilder,
|
||||
};
|
||||
use uv_dispatch::BuildDispatch;
|
||||
use uv_fs::Simplified;
|
||||
use uv_installer::{Downloader, NoBinary};
|
||||
|
|
@ -88,6 +90,11 @@ pub(crate) async fn pip_compile(
|
|||
));
|
||||
}
|
||||
|
||||
let client_builder = BaseClientBuilder::new()
|
||||
.connectivity(connectivity)
|
||||
.native_tls(native_tls)
|
||||
.keyring_provider(keyring_provider);
|
||||
|
||||
// Read all requirements from the provided sources.
|
||||
let RequirementsSpecification {
|
||||
project,
|
||||
|
|
@ -105,7 +112,7 @@ pub(crate) async fn pip_compile(
|
|||
constraints,
|
||||
overrides,
|
||||
&extras,
|
||||
connectivity,
|
||||
&client_builder,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,10 @@ use pypi_types::Yanked;
|
|||
use requirements_txt::EditableRequirement;
|
||||
use uv_auth::{KeyringProvider, GLOBAL_AUTH_STORE};
|
||||
use uv_cache::Cache;
|
||||
use uv_client::{Connectivity, FlatIndex, FlatIndexClient, RegistryClient, RegistryClientBuilder};
|
||||
use uv_client::{
|
||||
BaseClientBuilder, Connectivity, FlatIndex, FlatIndexClient, RegistryClient,
|
||||
RegistryClientBuilder,
|
||||
};
|
||||
use uv_dispatch::BuildDispatch;
|
||||
use uv_fs::Simplified;
|
||||
use uv_installer::{
|
||||
|
|
@ -80,6 +83,10 @@ pub(crate) async fn pip_install(
|
|||
printer: Printer,
|
||||
) -> Result<ExitStatus> {
|
||||
let start = Instant::now();
|
||||
let client_builder = BaseClientBuilder::new()
|
||||
.connectivity(connectivity)
|
||||
.native_tls(native_tls)
|
||||
.keyring_provider(keyring_provider);
|
||||
|
||||
// Read all requirements from the provided sources.
|
||||
let RequirementsSpecification {
|
||||
|
|
@ -93,7 +100,14 @@ pub(crate) async fn pip_install(
|
|||
no_index,
|
||||
find_links,
|
||||
extras: _,
|
||||
} = read_requirements(requirements, constraints, overrides, extras, connectivity).await?;
|
||||
} = read_requirements(
|
||||
requirements,
|
||||
constraints,
|
||||
overrides,
|
||||
extras,
|
||||
&client_builder,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Detect the current Python interpreter.
|
||||
let venv = if let Some(python) = python.as_ref() {
|
||||
|
|
@ -355,7 +369,7 @@ async fn read_requirements(
|
|||
constraints: &[RequirementsSource],
|
||||
overrides: &[RequirementsSource],
|
||||
extras: &ExtrasSpecification<'_>,
|
||||
connectivity: Connectivity,
|
||||
client_builder: &BaseClientBuilder<'_>,
|
||||
) -> Result<RequirementsSpecification, Error> {
|
||||
// If the user requests `extras` but does not provide a pyproject toml source
|
||||
if !matches!(extras, ExtrasSpecification::None)
|
||||
|
|
@ -372,7 +386,7 @@ async fn read_requirements(
|
|||
constraints,
|
||||
overrides,
|
||||
extras,
|
||||
connectivity,
|
||||
client_builder,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,10 @@ use pypi_types::Yanked;
|
|||
use requirements_txt::EditableRequirement;
|
||||
use uv_auth::{KeyringProvider, GLOBAL_AUTH_STORE};
|
||||
use uv_cache::{ArchiveTarget, ArchiveTimestamp, Cache};
|
||||
use uv_client::{Connectivity, FlatIndex, FlatIndexClient, RegistryClient, RegistryClientBuilder};
|
||||
use uv_client::{
|
||||
BaseClientBuilder, Connectivity, FlatIndex, FlatIndexClient, RegistryClient,
|
||||
RegistryClientBuilder,
|
||||
};
|
||||
use uv_dispatch::BuildDispatch;
|
||||
use uv_fs::Simplified;
|
||||
use uv_installer::{
|
||||
|
|
@ -52,6 +55,10 @@ pub(crate) async fn pip_sync(
|
|||
printer: Printer,
|
||||
) -> Result<ExitStatus> {
|
||||
let start = std::time::Instant::now();
|
||||
let client_builder = BaseClientBuilder::new()
|
||||
.connectivity(connectivity)
|
||||
.native_tls(native_tls)
|
||||
.keyring_provider(keyring_provider);
|
||||
|
||||
// Read all requirements from the provided sources.
|
||||
let RequirementsSpecification {
|
||||
|
|
@ -65,7 +72,7 @@ pub(crate) async fn pip_sync(
|
|||
extra_index_urls,
|
||||
no_index,
|
||||
find_links,
|
||||
} = RequirementsSpecification::from_simple_sources(sources, connectivity).await?;
|
||||
} = RequirementsSpecification::from_simple_sources(sources, &client_builder).await?;
|
||||
|
||||
// Validate that the requirements are non-empty.
|
||||
let num_requirements = requirements.len() + editables.len();
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@ use tracing::debug;
|
|||
|
||||
use distribution_types::{InstalledMetadata, Name};
|
||||
use pep508_rs::{Requirement, RequirementsTxtRequirement, UnnamedRequirement};
|
||||
use uv_auth::KeyringProvider;
|
||||
use uv_cache::Cache;
|
||||
use uv_client::Connectivity;
|
||||
use uv_client::{BaseClientBuilder, Connectivity};
|
||||
use uv_fs::Simplified;
|
||||
use uv_interpreter::PythonEnvironment;
|
||||
|
||||
|
|
@ -17,6 +18,7 @@ use crate::printer::Printer;
|
|||
use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
||||
|
||||
/// Uninstall packages from the current environment.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) async fn pip_uninstall(
|
||||
sources: &[RequirementsSource],
|
||||
python: Option<String>,
|
||||
|
|
@ -24,12 +26,18 @@ pub(crate) async fn pip_uninstall(
|
|||
break_system_packages: bool,
|
||||
cache: Cache,
|
||||
connectivity: Connectivity,
|
||||
native_tls: bool,
|
||||
keyring_provider: KeyringProvider,
|
||||
printer: Printer,
|
||||
) -> Result<ExitStatus> {
|
||||
let start = std::time::Instant::now();
|
||||
let client_builder = BaseClientBuilder::new()
|
||||
.connectivity(connectivity)
|
||||
.native_tls(native_tls)
|
||||
.keyring_provider(keyring_provider);
|
||||
|
||||
// Read all requirements from the provided sources.
|
||||
let spec = RequirementsSpecification::from_simple_sources(sources, connectivity).await?;
|
||||
let spec = RequirementsSpecification::from_simple_sources(sources, &client_builder).await?;
|
||||
|
||||
// Detect the current Python interpreter.
|
||||
let venv = if let Some(python) = python.as_ref() {
|
||||
|
|
|
|||
|
|
@ -980,6 +980,13 @@ struct PipUninstallArgs {
|
|||
)]
|
||||
python: Option<String>,
|
||||
|
||||
/// Attempt to use `keyring` for authentication for remote requirements files.
|
||||
///
|
||||
/// Due to not having Python imports, only `--keyring-provider subprocess` argument is currently
|
||||
/// implemented `uv` will try to use `keyring` via CLI when this flag is used.
|
||||
#[clap(long, default_value_t, value_enum, env = "UV_KEYRING_PROVIDER")]
|
||||
keyring_provider: KeyringProvider,
|
||||
|
||||
/// Use the system Python to uninstall packages.
|
||||
///
|
||||
/// By default, `uv` uninstalls from the virtual environment in the current working directory or
|
||||
|
|
@ -1718,6 +1725,8 @@ async fn run() -> Result<ExitStatus> {
|
|||
} else {
|
||||
Connectivity::Online
|
||||
},
|
||||
cli.native_tls,
|
||||
args.keyring_provider,
|
||||
printer,
|
||||
)
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use pep508_rs::{Requirement, RequirementsTxtRequirement, UnnamedRequirement, Ver
|
|||
use pypi_types::Metadata10;
|
||||
use requirements_txt::{EditableRequirement, FindLink, RequirementsTxt};
|
||||
use uv_cache::Cache;
|
||||
use uv_client::{Connectivity, RegistryClient};
|
||||
use uv_client::{BaseClientBuilder, Connectivity};
|
||||
use uv_distribution::download_and_extract_archive;
|
||||
use uv_fs::Simplified;
|
||||
use uv_normalize::{ExtraName, PackageName};
|
||||
|
|
@ -138,7 +138,7 @@ impl RequirementsSpecification {
|
|||
pub(crate) async fn from_source(
|
||||
source: &RequirementsSource,
|
||||
extras: &ExtrasSpecification<'_>,
|
||||
connectivity: Connectivity,
|
||||
client_builder: &BaseClientBuilder<'_>,
|
||||
) -> Result<Self> {
|
||||
Ok(match source {
|
||||
RequirementsSource::Package(name) => {
|
||||
|
|
@ -175,7 +175,7 @@ impl RequirementsSpecification {
|
|||
}
|
||||
RequirementsSource::RequirementsTxt(path) => {
|
||||
let requirements_txt =
|
||||
RequirementsTxt::parse(path, std::env::current_dir()?, connectivity).await?;
|
||||
RequirementsTxt::parse(path, std::env::current_dir()?, client_builder).await?;
|
||||
Self {
|
||||
project: None,
|
||||
requirements: requirements_txt
|
||||
|
|
@ -280,7 +280,7 @@ impl RequirementsSpecification {
|
|||
constraints: &[RequirementsSource],
|
||||
overrides: &[RequirementsSource],
|
||||
extras: &ExtrasSpecification<'_>,
|
||||
connectivity: Connectivity,
|
||||
client_builder: &BaseClientBuilder<'_>,
|
||||
) -> Result<Self> {
|
||||
let mut spec = Self::default();
|
||||
|
||||
|
|
@ -288,7 +288,7 @@ impl RequirementsSpecification {
|
|||
// A `requirements.txt` can contain a `-c constraints.txt` directive within it, so reading
|
||||
// a requirements file can also add constraints.
|
||||
for source in requirements {
|
||||
let source = Self::from_source(source, extras, connectivity).await?;
|
||||
let source = Self::from_source(source, extras, client_builder).await?;
|
||||
spec.requirements.extend(source.requirements);
|
||||
spec.constraints.extend(source.constraints);
|
||||
spec.overrides.extend(source.overrides);
|
||||
|
|
@ -315,7 +315,7 @@ impl RequirementsSpecification {
|
|||
|
||||
// Read all constraints, treating _everything_ as a constraint.
|
||||
for source in constraints {
|
||||
let source = Self::from_source(source, extras, connectivity).await?;
|
||||
let source = Self::from_source(source, extras, &client_builder).await?;
|
||||
for requirement in source.requirements {
|
||||
match requirement {
|
||||
RequirementsTxtRequirement::Pep508(requirement) => {
|
||||
|
|
@ -346,7 +346,7 @@ impl RequirementsSpecification {
|
|||
|
||||
// Read all overrides, treating both requirements _and_ constraints as overrides.
|
||||
for source in overrides {
|
||||
let source = Self::from_source(source, extras, connectivity).await?;
|
||||
let source = Self::from_source(source, extras, &client_builder).await?;
|
||||
for requirement in source.requirements {
|
||||
match requirement {
|
||||
RequirementsTxtRequirement::Pep508(requirement) => {
|
||||
|
|
@ -381,14 +381,14 @@ impl RequirementsSpecification {
|
|||
/// Read the requirements from a set of sources.
|
||||
pub(crate) async fn from_simple_sources(
|
||||
requirements: &[RequirementsSource],
|
||||
connectivity: Connectivity,
|
||||
client_builder: &BaseClientBuilder<'_>,
|
||||
) -> Result<Self> {
|
||||
Self::from_sources(
|
||||
requirements,
|
||||
&[],
|
||||
&[],
|
||||
&ExtrasSpecification::None,
|
||||
connectivity,
|
||||
client_builder,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
@ -476,9 +476,12 @@ pub(crate) async fn read_lockfile(
|
|||
};
|
||||
|
||||
// Parse the requirements from the lockfile.
|
||||
let requirements_txt =
|
||||
RequirementsTxt::parse(output_file, std::env::current_dir()?, Connectivity::Offline)
|
||||
.await?;
|
||||
let requirements_txt = RequirementsTxt::parse(
|
||||
output_file,
|
||||
std::env::current_dir()?,
|
||||
&BaseClientBuilder::new().connectivity(Connectivity::Offline),
|
||||
)
|
||||
.await?;
|
||||
let preferences = requirements_txt
|
||||
.requirements
|
||||
.into_iter()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue