Add lazy base client construction

This commit is contained in:
Zanie Blue 2024-03-13 19:03:02 -05:00
parent d91f64ea9a
commit 91fc60f938
6 changed files with 71 additions and 37 deletions

1
Cargo.lock generated
View File

@ -4360,6 +4360,7 @@ dependencies = [
"hyper 0.14.28",
"insta",
"install-wheel-rs",
"itertools 0.12.1",
"pep440_rs",
"pep508_rs",
"platform-tags",

View File

@ -51,7 +51,7 @@ use pep508_rs::{
};
#[cfg(feature = "http")]
use uv_client::BaseClient;
use uv_client::BaseClientBuilder;
use uv_client::{BaseClientBuilder, LazyBaseClientBuilder};
use uv_fs::{normalize_url_path, Simplified};
use uv_normalize::ExtraName;
use uv_warnings::warn_user;
@ -334,7 +334,7 @@ impl RequirementsTxt {
pub async fn parse(
requirements_txt: impl AsRef<Path>,
working_dir: impl AsRef<Path>,
client_builder: BaseClientBuilder,
client_builder: &mut LazyBaseClientBuilder,
) -> Result<Self, RequirementsTxtFileError> {
let requirements_txt = requirements_txt.as_ref();
let working_dir = working_dir.as_ref();
@ -355,17 +355,17 @@ impl RequirementsTxt {
#[cfg(feature = "http")]
{
// 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()),
)),
});
}
// 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.clone().build();
let client = client_builder.build();
read_url_to_string(&requirements_txt, client).await
}
} else {
@ -406,7 +406,7 @@ impl RequirementsTxt {
content: &str,
working_dir: &Path,
requirements_dir: &Path,
client_builder: BaseClientBuilder,
client_builder: &mut LazyBaseClientBuilder,
) -> Result<Self, RequirementsTxtParserError> {
let mut s = Scanner::new(content);
@ -425,14 +425,13 @@ impl RequirementsTxt {
} else {
requirements_dir.join(filename.as_ref())
};
let sub_requirements =
Self::parse(&sub_file, working_dir, client_builder.clone())
.await
.map_err(|err| RequirementsTxtParserError::Subfile {
source: Box::new(err),
start,
end,
})?;
let sub_requirements = Self::parse(&sub_file, working_dir, client_builder)
.await
.map_err(|err| RequirementsTxtParserError::Subfile {
source: Box::new(err),
start,
end,
})?;
// Disallow conflicting `--index-url` in nested `requirements` files.
if sub_requirements.index_url.is_some()
@ -464,14 +463,13 @@ impl RequirementsTxt {
} else {
requirements_dir.join(filename.as_ref())
};
let sub_constraints =
Self::parse(&sub_file, working_dir, client_builder.clone())
.await
.map_err(|err| RequirementsTxtParserError::Subfile {
source: Box::new(err),
start,
end,
})?;
let sub_constraints = Self::parse(&sub_file, working_dir, client_builder)
.await
.map_err(|err| RequirementsTxtParserError::Subfile {
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.
@ -835,7 +833,7 @@ fn parse_value<'a, T>(
#[cfg(feature = "http")]
async fn read_url_to_string(
path: impl AsRef<Path>,
client: BaseClient,
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 =

View File

@ -28,6 +28,7 @@ fs-err = { workspace = true, features = ["tokio"] }
futures = { workspace = true }
html-escape = { workspace = true }
http = { workspace = true }
itertools = { workspace = true }
reqwest = { workspace = true }
reqwest-middleware = { workspace = true }
reqwest-retry = { workspace = true }

View File

@ -1,10 +1,13 @@
use itertools::Either;
use reqwest::{Client, ClientBuilder};
use reqwest_middleware::ClientWithMiddleware;
use reqwest_retry::policies::ExponentialBackoff;
use reqwest_retry::RetryTransientMiddleware;
use std::env;
use std::fmt::Debug;
use std::ops::Deref;
use std::path::Path;
use std::sync::Mutex;
use tracing::debug;
use uv_auth::{AuthMiddleware, KeyringProvider};
use uv_fs::Simplified;
@ -78,6 +81,10 @@ impl BaseClientBuilder {
matches!(self.connectivity, Connectivity::Offline)
}
pub fn to_lazy_builder(self) -> LazyBaseClientBuilder {
LazyBaseClientBuilder::new(self)
}
pub fn build(self) -> BaseClient {
// Create user agent.
let user_agent_string = format!("uv/{}", version());
@ -185,3 +192,27 @@ impl BaseClient {
self.connectivity
}
}
pub struct LazyBaseClientBuilder {
builder: Option<BaseClientBuilder>,
client: Option<BaseClient>,
}
impl LazyBaseClientBuilder {
fn new(builder: BaseClientBuilder) -> Self {
Self {
builder: Some(builder),
client: None,
}
}
pub fn build(&mut self) -> &BaseClient {
if let Some(ref client) = self.client {
client
} else {
let builder = self.builder.take().unwrap();
let client = self.client.insert(builder.build());
client
}
}
}

View File

@ -1,4 +1,4 @@
pub use base_client::{BaseClient, BaseClientBuilder};
pub use base_client::{BaseClient, BaseClientBuilder, LazyBaseClientBuilder};
pub use cached_client::{CacheControl, CachedClient, CachedClientError, DataWithCachePolicy};
pub use error::{BetterReqwestError, Error, ErrorKind};
pub use flat_index::{FlatDistributions, FlatIndex, FlatIndexClient, FlatIndexError};

View File

@ -12,7 +12,7 @@ use tracing::{instrument, Level};
use distribution_types::{FlatIndexLocation, IndexUrl};
use pep508_rs::Requirement;
use requirements_txt::{EditableRequirement, FindLink, RequirementsTxt};
use uv_client::{BaseClientBuilder, Connectivity};
use uv_client::{BaseClient, BaseClientBuilder, Connectivity, LazyBaseClientBuilder};
use uv_fs::Simplified;
use uv_normalize::{ExtraName, PackageName};
use uv_warnings::warn_user;
@ -142,7 +142,7 @@ impl RequirementsSpecification {
pub(crate) async fn from_source(
source: &RequirementsSource,
extras: &ExtrasSpecification<'_>,
client_builder: BaseClientBuilder,
client_builder: &mut LazyBaseClientBuilder,
) -> Result<Self> {
Ok(match source {
RequirementsSource::Package(name) => {
@ -284,12 +284,13 @@ impl RequirementsSpecification {
client_builder: BaseClientBuilder,
) -> Result<Self> {
let mut spec = Self::default();
let mut client_builder = client_builder.to_lazy_builder();
// Read all requirements, and keep track of all requirements _and_ constraints.
// 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, client_builder.clone()).await?;
let source = Self::from_source(source, extras, &mut client_builder).await?;
spec.requirements.extend(source.requirements);
spec.constraints.extend(source.constraints);
spec.overrides.extend(source.overrides);
@ -316,7 +317,7 @@ impl RequirementsSpecification {
// Read all constraints, treating _everything_ as a constraint.
for source in constraints {
let source = Self::from_source(source, extras, client_builder.clone()).await?;
let source = Self::from_source(source, extras, &mut client_builder).await?;
spec.constraints.extend(source.requirements);
spec.constraints.extend(source.constraints);
spec.constraints.extend(source.overrides);
@ -336,7 +337,7 @@ impl RequirementsSpecification {
// Read all overrides, treating both requirements _and_ constraints as overrides.
for source in overrides {
let source = Self::from_source(source, extras, client_builder.clone()).await?;
let source = Self::from_source(source, extras, &mut client_builder).await?;
spec.overrides.extend(source.requirements);
spec.overrides.extend(source.constraints);
spec.overrides.extend(source.overrides);
@ -458,7 +459,9 @@ pub(crate) async fn read_lockfile(
let requirements_txt = RequirementsTxt::parse(
output_file,
std::env::current_dir()?,
BaseClientBuilder::new().connectivity(Connectivity::Offline),
&mut BaseClientBuilder::new()
.connectivity(Connectivity::Offline)
.to_lazy_builder(),
)
.await?;
let requirements = requirements_txt