mirror of https://github.com/astral-sh/uv
Attach subcommand to User-Agent string (#16837)
This commit is contained in:
parent
5773b12fa9
commit
6b00d6522c
|
|
@ -89,6 +89,8 @@ pub struct BaseClientBuilder<'a> {
|
|||
cross_origin_credential_policy: CrossOriginCredentialsPolicy,
|
||||
/// Optional custom reqwest client to use instead of creating a new one.
|
||||
custom_client: Option<Client>,
|
||||
/// uv subcommand in which this client is being used
|
||||
subcommand: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
/// The policy for handling HTTP redirects.
|
||||
|
|
@ -143,6 +145,7 @@ impl Default for BaseClientBuilder<'_> {
|
|||
redirect_policy: RedirectPolicy::default(),
|
||||
cross_origin_credential_policy: CrossOriginCredentialsPolicy::Secure,
|
||||
custom_client: None,
|
||||
subcommand: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -276,6 +279,12 @@ impl<'a> BaseClientBuilder<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn subcommand(mut self, subcommand: Vec<String>) -> Self {
|
||||
self.subcommand = Some(subcommand);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_native_tls(&self) -> bool {
|
||||
self.native_tls
|
||||
}
|
||||
|
|
@ -358,7 +367,7 @@ impl<'a> BaseClientBuilder<'a> {
|
|||
let mut user_agent_string = format!("uv/{}", version());
|
||||
|
||||
// Add linehaul metadata.
|
||||
let linehaul = LineHaul::new(self.markers, self.platform);
|
||||
let linehaul = LineHaul::new(self.markers, self.platform, self.subcommand.clone());
|
||||
if let Ok(output) = serde_json::to_string(&linehaul) {
|
||||
let _ = write!(user_agent_string, " {output}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use uv_version::version;
|
|||
pub struct Installer {
|
||||
pub name: Option<String>,
|
||||
pub version: Option<String>,
|
||||
pub subcommand: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
|
|
@ -63,7 +64,11 @@ pub struct LineHaul {
|
|||
impl LineHaul {
|
||||
/// Initializes Linehaul information based on PEP 508 markers.
|
||||
#[instrument(name = "linehaul", skip_all)]
|
||||
pub fn new(markers: Option<&MarkerEnvironment>, platform: Option<&Platform>) -> Self {
|
||||
pub fn new(
|
||||
markers: Option<&MarkerEnvironment>,
|
||||
platform: Option<&Platform>,
|
||||
subcommand: Option<Vec<String>>,
|
||||
) -> Self {
|
||||
// https://github.com/pypa/pip/blob/24.0/src/pip/_internal/network/session.py#L87
|
||||
let looks_like_ci = [
|
||||
EnvVars::BUILD_BUILDID,
|
||||
|
|
@ -123,6 +128,7 @@ impl LineHaul {
|
|||
installer: Option::from(Installer {
|
||||
name: Some("uv".to_string()),
|
||||
version: Some(version().to_string()),
|
||||
subcommand,
|
||||
}),
|
||||
python: markers.map(|markers| markers.python_full_version().version.to_string()),
|
||||
implementation: Option::from(Implementation {
|
||||
|
|
|
|||
|
|
@ -57,7 +57,70 @@ async fn test_user_agent_has_version() -> Result<()> {
|
|||
assert_json_snapshot!(&linehaul.installer, @r#"
|
||||
{
|
||||
"name": "uv",
|
||||
"version": "[VERSION]"
|
||||
"version": "[VERSION]",
|
||||
"subcommand": null
|
||||
}
|
||||
"#);
|
||||
});
|
||||
|
||||
// Wait for the server task to complete, to be a good citizen.
|
||||
let _ = server_task.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_user_agent_has_subcommand() -> Result<()> {
|
||||
// Initialize dummy http server
|
||||
let (server_task, addr) = start_http_user_agent_server().await?;
|
||||
|
||||
// Initialize uv-client
|
||||
let cache = Cache::temp()?.init()?;
|
||||
let client = RegistryClientBuilder::new(
|
||||
BaseClientBuilder::default().subcommand(vec!["foo".to_owned(), "bar".to_owned()]),
|
||||
cache,
|
||||
)
|
||||
.build();
|
||||
|
||||
// Send request to our dummy server
|
||||
let url = DisplaySafeUrl::from_str(&format!("http://{addr}"))?;
|
||||
let res = client
|
||||
.cached_client()
|
||||
.uncached()
|
||||
.for_host(&url)
|
||||
.get(Url::from(url))
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
// Check the HTTP status
|
||||
assert!(res.status().is_success());
|
||||
|
||||
// Check User Agent
|
||||
let body = res.text().await?;
|
||||
|
||||
let (uv_version, uv_linehaul) = body
|
||||
.split_once(' ')
|
||||
.expect("Failed to split User-Agent header");
|
||||
|
||||
// Deserializing Linehaul
|
||||
let linehaul: LineHaul = serde_json::from_str(uv_linehaul)?;
|
||||
|
||||
// Assert linehaul user agent
|
||||
let filters = vec![(version(), "[VERSION]")];
|
||||
with_settings!({
|
||||
filters => filters
|
||||
}, {
|
||||
// Assert uv version
|
||||
assert_snapshot!(uv_version, @"uv/[VERSION]");
|
||||
// Assert linehaul json
|
||||
assert_json_snapshot!(&linehaul.installer, @r#"
|
||||
{
|
||||
"name": "uv",
|
||||
"version": "[VERSION]",
|
||||
"subcommand": [
|
||||
"foo",
|
||||
"bar"
|
||||
]
|
||||
}
|
||||
"#);
|
||||
});
|
||||
|
|
@ -152,11 +215,12 @@ async fn test_user_agent_has_linehaul() -> Result<()> {
|
|||
assert_json_snapshot!(&linehaul, {
|
||||
".distro" => "[distro]",
|
||||
".ci" => "[ci]"
|
||||
}, @r###"
|
||||
}, @r#"
|
||||
{
|
||||
"installer": {
|
||||
"name": "uv",
|
||||
"version": "[VERSION]"
|
||||
"version": "[VERSION]",
|
||||
"subcommand": null
|
||||
},
|
||||
"python": "3.12.2",
|
||||
"implementation": {
|
||||
|
|
@ -174,7 +238,7 @@ async fn test_user_agent_has_linehaul() -> Result<()> {
|
|||
"rustc_version": null,
|
||||
"ci": "[ci]"
|
||||
}
|
||||
"###);
|
||||
"#);
|
||||
});
|
||||
|
||||
// Assert distro
|
||||
|
|
|
|||
|
|
@ -630,7 +630,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
args.settings.torch_backend,
|
||||
args.settings.dependency_metadata,
|
||||
args.settings.keyring_provider,
|
||||
&client_builder,
|
||||
&client_builder.subcommand(vec!["pip".to_owned(), "compile".to_owned()]),
|
||||
args.settings.config_setting,
|
||||
args.settings.config_settings_package,
|
||||
args.settings.build_isolation.clone(),
|
||||
|
|
@ -707,7 +707,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
args.settings.torch_backend,
|
||||
args.settings.dependency_metadata,
|
||||
args.settings.keyring_provider,
|
||||
&client_builder,
|
||||
&client_builder.subcommand(vec!["pip".to_owned(), "sync".to_owned()]),
|
||||
args.settings.allow_empty_requirements,
|
||||
globals.installer_metadata,
|
||||
&args.settings.config_setting,
|
||||
|
|
@ -856,7 +856,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
args.settings.torch_backend,
|
||||
args.settings.dependency_metadata,
|
||||
args.settings.keyring_provider,
|
||||
&client_builder,
|
||||
&client_builder.subcommand(vec!["pip".to_owned(), "install".to_owned()]),
|
||||
args.settings.reinstall,
|
||||
args.settings.link_mode,
|
||||
args.settings.compile_bytecode,
|
||||
|
|
@ -919,7 +919,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
args.settings.prefix,
|
||||
cache,
|
||||
args.settings.keyring_provider,
|
||||
&client_builder,
|
||||
&client_builder.subcommand(vec!["pip".to_owned(), "uninstall".to_owned()]),
|
||||
args.dry_run,
|
||||
printer,
|
||||
globals.preview,
|
||||
|
|
@ -968,7 +968,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
args.settings.index_locations,
|
||||
args.settings.index_strategy,
|
||||
args.settings.keyring_provider,
|
||||
&client_builder,
|
||||
&client_builder.subcommand(vec!["pip".to_owned(), "list".to_owned()]),
|
||||
globals.concurrency,
|
||||
args.settings.strict,
|
||||
args.settings.exclude_newer,
|
||||
|
|
@ -1022,7 +1022,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
args.settings.index_locations,
|
||||
args.settings.index_strategy,
|
||||
args.settings.keyring_provider,
|
||||
client_builder,
|
||||
client_builder.subcommand(vec!["pip".to_owned(), "tree".to_owned()]),
|
||||
globals.concurrency,
|
||||
args.settings.strict,
|
||||
args.settings.exclude_newer,
|
||||
|
|
@ -1109,7 +1109,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
args.python,
|
||||
args.install_mirrors,
|
||||
&args.settings,
|
||||
&client_builder,
|
||||
&client_builder.subcommand(vec!["build".to_owned()]),
|
||||
cli.top_level.no_config,
|
||||
globals.python_preference,
|
||||
globals.python_downloads,
|
||||
|
|
@ -1176,7 +1176,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
args.settings.index_strategy,
|
||||
args.settings.dependency_metadata,
|
||||
args.settings.keyring_provider,
|
||||
&client_builder,
|
||||
&client_builder.subcommand(vec!["venv".to_owned()]),
|
||||
uv_virtualenv::Prompt::from_args(prompt),
|
||||
args.system_site_packages,
|
||||
args.seed,
|
||||
|
|
@ -1216,7 +1216,16 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
token,
|
||||
dry_run,
|
||||
}),
|
||||
}) => commands::self_update(target_version, token, dry_run, printer, client_builder).await,
|
||||
}) => {
|
||||
commands::self_update(
|
||||
target_version,
|
||||
token,
|
||||
dry_run,
|
||||
printer,
|
||||
client_builder.subcommand(vec!["self".to_owned(), "update".to_owned()]),
|
||||
)
|
||||
.await
|
||||
}
|
||||
Commands::Self_(SelfNamespace {
|
||||
command:
|
||||
SelfCommand::Version {
|
||||
|
|
@ -1323,6 +1332,13 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
.map(RequirementsSource::from_constraints_txt)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let client_builder = match invocation_source {
|
||||
ToolRunCommand::Uvx => client_builder.subcommand(vec!["uvx".to_owned()]),
|
||||
ToolRunCommand::ToolRun => {
|
||||
client_builder.subcommand(vec!["tool".to_owned(), "run".to_owned()])
|
||||
}
|
||||
};
|
||||
|
||||
Box::pin(commands::tool_run(
|
||||
args.command,
|
||||
args.from,
|
||||
|
|
@ -1432,7 +1448,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
args.force,
|
||||
args.options,
|
||||
args.settings,
|
||||
client_builder,
|
||||
client_builder.subcommand(vec!["tool".to_owned(), "install".to_owned()]),
|
||||
globals.python_preference,
|
||||
globals.python_downloads,
|
||||
globals.installer_metadata,
|
||||
|
|
@ -1481,7 +1497,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
args.install_mirrors,
|
||||
args.args,
|
||||
args.filesystem,
|
||||
client_builder,
|
||||
client_builder.subcommand(vec!["tool".to_owned(), "upgrade".to_owned()]),
|
||||
globals.python_preference,
|
||||
globals.python_downloads,
|
||||
globals.installer_metadata,
|
||||
|
|
@ -1538,7 +1554,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
args.python_downloads_json_url,
|
||||
globals.python_preference,
|
||||
globals.python_downloads,
|
||||
&client_builder,
|
||||
&client_builder.subcommand(vec!["python".to_owned(), "list".to_owned()]),
|
||||
&cache,
|
||||
printer,
|
||||
globals.preview,
|
||||
|
|
@ -1564,7 +1580,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
args.python_install_mirror,
|
||||
args.pypy_install_mirror,
|
||||
args.python_downloads_json_url,
|
||||
client_builder,
|
||||
client_builder.subcommand(vec!["python".to_owned(), "install".to_owned()]),
|
||||
args.default,
|
||||
globals.python_downloads,
|
||||
cli.top_level.no_config,
|
||||
|
|
@ -1593,7 +1609,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
args.python_install_mirror,
|
||||
args.pypy_install_mirror,
|
||||
args.python_downloads_json_url,
|
||||
client_builder,
|
||||
client_builder.subcommand(vec!["python".to_owned(), "upgrade".to_owned()]),
|
||||
args.default,
|
||||
globals.python_downloads,
|
||||
cli.top_level.no_config,
|
||||
|
|
@ -1631,7 +1647,8 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
commands::python_find_script(
|
||||
(&script).into(),
|
||||
args.show_version,
|
||||
&client_builder,
|
||||
// TODO(zsol): is this the right thing to do here?
|
||||
&client_builder.subcommand(vec!["python".to_owned(), "find".to_owned()]),
|
||||
globals.python_preference,
|
||||
globals.python_downloads,
|
||||
cli.top_level.no_config,
|
||||
|
|
@ -1650,7 +1667,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
args.system,
|
||||
globals.python_preference,
|
||||
args.python_downloads_json_url.as_deref(),
|
||||
&client_builder,
|
||||
&client_builder.subcommand(vec!["python".to_owned(), "find".to_owned()]),
|
||||
&cache,
|
||||
printer,
|
||||
globals.preview,
|
||||
|
|
@ -1677,7 +1694,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
args.global,
|
||||
args.rm,
|
||||
args.install_mirrors,
|
||||
client_builder,
|
||||
client_builder.subcommand(vec!["python".to_owned(), "pin".to_owned()]),
|
||||
&cache,
|
||||
printer,
|
||||
globals.preview,
|
||||
|
|
@ -1734,7 +1751,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
trusted_publishing,
|
||||
keyring_provider,
|
||||
&environment,
|
||||
&client_builder,
|
||||
&client_builder.subcommand(vec!["publish".to_owned()]),
|
||||
username,
|
||||
password,
|
||||
check_url,
|
||||
|
|
@ -1877,7 +1894,7 @@ async fn run_project(
|
|||
args.python,
|
||||
args.install_mirrors,
|
||||
args.no_workspace,
|
||||
&client_builder,
|
||||
&client_builder.subcommand(vec!["init".to_owned()]),
|
||||
globals.python_preference,
|
||||
globals.python_downloads,
|
||||
no_config,
|
||||
|
|
@ -1938,7 +1955,7 @@ async fn run_project(
|
|||
args.python_platform,
|
||||
args.install_mirrors,
|
||||
args.settings,
|
||||
client_builder,
|
||||
client_builder.subcommand(vec!["run".to_owned()]),
|
||||
globals.python_preference,
|
||||
globals.python_downloads,
|
||||
globals.installer_metadata,
|
||||
|
|
@ -1989,7 +2006,7 @@ async fn run_project(
|
|||
globals.python_preference,
|
||||
globals.python_downloads,
|
||||
args.settings,
|
||||
client_builder,
|
||||
client_builder.subcommand(vec!["sync".to_owned()]),
|
||||
script,
|
||||
globals.installer_metadata,
|
||||
globals.concurrency,
|
||||
|
|
@ -2035,7 +2052,7 @@ async fn run_project(
|
|||
args.python,
|
||||
args.install_mirrors,
|
||||
args.settings,
|
||||
client_builder,
|
||||
client_builder.subcommand(vec!["lock".to_owned()]),
|
||||
script,
|
||||
globals.python_preference,
|
||||
globals.python_downloads,
|
||||
|
|
@ -2162,7 +2179,7 @@ async fn run_project(
|
|||
args.workspace,
|
||||
args.install_mirrors,
|
||||
args.settings,
|
||||
client_builder,
|
||||
client_builder.subcommand(vec!["add".to_owned()]),
|
||||
script,
|
||||
globals.python_preference,
|
||||
globals.python_downloads,
|
||||
|
|
@ -2206,7 +2223,7 @@ async fn run_project(
|
|||
args.python,
|
||||
args.install_mirrors,
|
||||
args.settings,
|
||||
client_builder,
|
||||
client_builder.subcommand(vec!["remove".to_owned()]),
|
||||
script,
|
||||
globals.python_preference,
|
||||
globals.python_downloads,
|
||||
|
|
@ -2247,7 +2264,7 @@ async fn run_project(
|
|||
args.python,
|
||||
args.install_mirrors,
|
||||
args.settings,
|
||||
client_builder,
|
||||
client_builder.subcommand(vec!["version".to_owned()]),
|
||||
globals.python_preference,
|
||||
globals.python_downloads,
|
||||
globals.installer_metadata,
|
||||
|
|
@ -2292,7 +2309,7 @@ async fn run_project(
|
|||
args.python,
|
||||
args.install_mirrors,
|
||||
args.resolver,
|
||||
&client_builder,
|
||||
&client_builder.subcommand(vec!["tree".to_owned()]),
|
||||
script,
|
||||
globals.python_preference,
|
||||
globals.python_downloads,
|
||||
|
|
@ -2339,7 +2356,7 @@ async fn run_project(
|
|||
args.python,
|
||||
args.install_mirrors,
|
||||
args.settings,
|
||||
client_builder,
|
||||
client_builder.subcommand(vec!["export".to_owned()]),
|
||||
globals.python_preference,
|
||||
globals.python_downloads,
|
||||
globals.concurrency,
|
||||
|
|
@ -2366,7 +2383,7 @@ async fn run_project(
|
|||
args.diff,
|
||||
args.extra_args,
|
||||
args.version,
|
||||
client_builder,
|
||||
client_builder.subcommand(vec!["format".to_owned()]),
|
||||
cache,
|
||||
printer,
|
||||
globals.preview,
|
||||
|
|
|
|||
Loading…
Reference in New Issue