Add preview warnings to native-keyring usage (#15555)

The refactor here was all done by Claude Code.
This commit is contained in:
Zanie Blue 2025-08-29 14:18:15 -05:00
parent 460ea6e9eb
commit 4ad5ae5e6f
14 changed files with 91 additions and 11 deletions

6
Cargo.lock generated
View file

@ -5373,6 +5373,7 @@ dependencies = [
"uv-pep440",
"uv-pep508",
"uv-platform-tags",
"uv-preview",
"uv-pypi-types",
"uv-redacted",
"uv-small-str",
@ -5409,8 +5410,10 @@ dependencies = [
"uv-pep440",
"uv-pep508",
"uv-platform-tags",
"uv-preview",
"uv-redacted",
"uv-static",
"uv-warnings",
]
[[package]]
@ -6001,6 +6004,7 @@ dependencies = [
"uv-extract",
"uv-fs",
"uv-metadata",
"uv-preview",
"uv-pypi-types",
"uv-redacted",
"uv-static",
@ -6139,6 +6143,7 @@ dependencies = [
"uv-git",
"uv-normalize",
"uv-pep508",
"uv-preview",
"uv-pypi-types",
"uv-redacted",
"uv-requirements-txt",
@ -6175,6 +6180,7 @@ dependencies = [
"uv-fs",
"uv-normalize",
"uv-pep508",
"uv-preview",
"uv-pypi-types",
"uv-redacted",
"uv-warnings",

View file

@ -22,6 +22,7 @@ uv-normalize = { workspace = true }
uv-pep440 = { workspace = true }
uv-pep508 = { workspace = true }
uv-platform-tags = { workspace = true }
uv-preview = { workspace = true }
uv-pypi-types = { workspace = true }
uv-small-str = { workspace = true }
uv-redacted = { workspace = true }

View file

@ -33,6 +33,7 @@ use uv_configuration::{KeyringProviderType, TrustedHost};
use uv_fs::Simplified;
use uv_pep508::MarkerEnvironment;
use uv_platform_tags::Platform;
use uv_preview::Preview;
use uv_redacted::DisplaySafeUrl;
use uv_static::EnvVars;
use uv_version::version;
@ -68,6 +69,7 @@ pub enum AuthIntegration {
#[derive(Debug, Clone)]
pub struct BaseClientBuilder<'a> {
keyring: KeyringProviderType,
preview: Preview,
allow_insecure_host: Vec<TrustedHost>,
native_tls: bool,
built_in_root_certs: bool,
@ -125,6 +127,7 @@ impl Default for BaseClientBuilder<'_> {
fn default() -> Self {
Self {
keyring: KeyringProviderType::default(),
preview: Preview::default(),
allow_insecure_host: vec![],
native_tls: false,
built_in_root_certs: false,
@ -486,13 +489,13 @@ impl<'a> BaseClientBuilder<'a> {
AuthIntegration::Default => {
let auth_middleware = AuthMiddleware::new()
.with_indexes(self.indexes.clone())
.with_keyring(self.keyring.to_provider());
.with_keyring(self.keyring.to_provider(&self.preview));
client = client.with(auth_middleware);
}
AuthIntegration::OnlyAuthenticated => {
let auth_middleware = AuthMiddleware::new()
.with_indexes(self.indexes.clone())
.with_keyring(self.keyring.to_provider())
.with_keyring(self.keyring.to_provider(&self.preview))
.with_only_authenticated(true);
client = client.with(auth_middleware);

View file

@ -25,8 +25,10 @@ uv-normalize = { workspace = true }
uv-pep440 = { workspace = true }
uv-pep508 = { workspace = true, features = ["schemars"] }
uv-platform-tags = { workspace = true }
uv-preview = { workspace = true }
uv-redacted = { workspace = true }
uv-static = { workspace = true }
uv-warnings = { workspace = true }
clap = { workspace = true, features = ["derive"], optional = true }
either = { workspace = true }
fs-err = { workspace = true }

View file

@ -1,6 +1,8 @@
use std::str::FromStr;
use uv_auth::{self, KeyringProvider};
use uv_preview::{Preview, PreviewFeatures};
use uv_redacted::DisplaySafeUrl;
use uv_warnings::warn_user_once;
/// Keyring provider type to use for credential lookup.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
@ -23,10 +25,18 @@ pub enum KeyringProviderType {
// See <https://pip.pypa.io/en/stable/topics/authentication/#keyring-support> for details.
impl KeyringProviderType {
pub fn to_provider(&self) -> Option<KeyringProvider> {
pub fn to_provider(&self, preview: &Preview) -> Option<KeyringProvider> {
match self {
Self::Disabled => None,
Self::Native => Some(KeyringProvider::native()),
Self::Native => {
if !preview.is_enabled(PreviewFeatures::NATIVE_KEYRING) {
warn_user_once!(
"The native keyring provider is experimental and may change without warning. Pass `--preview-features {}` to disable this warning.",
PreviewFeatures::NATIVE_KEYRING
);
}
Some(KeyringProvider::native())
}
Self::Subprocess => Some(KeyringProvider::subprocess()),
}
}

View file

@ -22,6 +22,7 @@ uv-distribution-types = { workspace = true }
uv-extract = { workspace = true }
uv-fs = { workspace = true }
uv-metadata = { workspace = true }
uv-preview = { workspace = true }
uv-pypi-types = { workspace = true }
uv-redacted = { workspace = true }
uv-static = { workspace = true }

View file

@ -22,6 +22,7 @@ uv-distribution-types = { workspace = true }
uv-fs = { workspace = true }
uv-normalize = { workspace = true }
uv-pep508 = { workspace = true }
uv-preview = { workspace = true }
uv-pypi-types = { workspace = true }
uv-redacted = { workspace = true }
uv-warnings = { workspace = true }

View file

@ -27,6 +27,7 @@ uv-fs = { workspace = true }
uv-git = { workspace = true }
uv-normalize = { workspace = true }
uv-pep508 = { workspace = true }
uv-preview = { workspace = true }
uv-pypi-types = { workspace = true }
uv-redacted = { workspace = true }
uv-requirements-txt = { workspace = true, features = ["http"] }

View file

@ -4,6 +4,7 @@ use std::fmt::Write;
use console::Term;
use uv_auth::Credentials;
use uv_configuration::{KeyringProviderType, Service};
use uv_preview::Preview;
use crate::{commands::ExitStatus, printer::Printer};
@ -15,6 +16,7 @@ pub(crate) async fn login(
token: Option<String>,
keyring_provider: Option<KeyringProviderType>,
printer: Printer,
preview: Preview,
) -> Result<ExitStatus> {
let url = service.url();
let display_url = username
@ -44,7 +46,7 @@ pub(crate) async fn login(
);
};
let provider = match keyring_provider {
KeyringProviderType::Native => keyring_provider.to_provider().unwrap(),
KeyringProviderType::Native => keyring_provider.to_provider(&preview).unwrap(),
KeyringProviderType::Disabled | KeyringProviderType::Subprocess => {
bail!(
"Cannot login with `keyring-provider = {keyring_provider}`, use `keyring-provider = {}` instead",

View file

@ -1,6 +1,7 @@
use anyhow::{Context, Result, bail};
use std::{borrow::Cow, fmt::Write};
use uv_configuration::{KeyringProviderType, Service};
use uv_preview::Preview;
use crate::{commands::ExitStatus, printer::Printer};
@ -12,6 +13,7 @@ pub(crate) async fn logout(
username: Option<String>,
keyring_provider: Option<KeyringProviderType>,
printer: Printer,
preview: Preview,
) -> Result<ExitStatus> {
let url = service.url();
let display_url = username
@ -28,7 +30,7 @@ pub(crate) async fn logout(
// Be helpful about incompatible `keyring-provider` settings
let provider = match keyring_provider {
KeyringProviderType::Native => keyring_provider.to_provider().unwrap(),
KeyringProviderType::Native => keyring_provider.to_provider(&preview).unwrap(),
KeyringProviderType::Disabled | KeyringProviderType::Subprocess => {
bail!(
"Cannot logout with `keyring-provider = {keyring_provider}`, use `keyring-provider = {}` instead",

View file

@ -3,6 +3,7 @@ use std::fmt::Write;
use anyhow::{Context, Result, bail};
use uv_configuration::{KeyringProviderType, Service};
use uv_preview::Preview;
use crate::{Printer, commands::ExitStatus};
@ -12,12 +13,13 @@ pub(crate) async fn token(
username: Option<String>,
keyring_provider: Option<KeyringProviderType>,
printer: Printer,
preview: Preview,
) -> Result<ExitStatus> {
// Determine the keyring provider to use
let Some(keyring_provider) = &keyring_provider else {
bail!("Retrieving credentials requires setting a `keyring-provider`");
};
let Some(provider) = keyring_provider.to_provider() else {
let Some(provider) = keyring_provider.to_provider(&preview) else {
bail!("Cannot retrieve credentials with `keyring-provider = {keyring_provider}`");
};

View file

@ -12,6 +12,7 @@ use uv_cache::Cache;
use uv_client::{AuthIntegration, BaseClient, BaseClientBuilder, RegistryClientBuilder};
use uv_configuration::{KeyringProviderType, TrustedPublishing};
use uv_distribution_types::{IndexCapabilities, IndexLocations, IndexUrl};
use uv_preview::Preview;
use uv_publish::{
CheckUrlClient, TrustedPublishResult, check_trusted_publishing, files_for_publishing, upload,
};
@ -34,6 +35,7 @@ pub(crate) async fn publish(
index_locations: IndexLocations,
cache: &Cache,
printer: Printer,
preview: Preview,
) -> Result<ExitStatus> {
if client_builder.is_offline() {
bail!("Unable to publish files in offline mode");
@ -83,6 +85,7 @@ pub(crate) async fn publish(
check_url.as_ref(),
Prompt::Enabled,
printer,
preview,
)
.await?;
@ -197,6 +200,7 @@ async fn gather_credentials(
check_url: Option<&IndexUrl>,
prompt: Prompt,
printer: Printer,
preview: Preview,
) -> Result<(DisplaySafeUrl, Credentials)> {
// Support reading username and password from the URL, for symmetry with the index API.
if let Some(url_password) = publish_url.password() {
@ -280,11 +284,11 @@ async fn gather_credentials(
// If applicable, fetch the password from the keyring eagerly to avoid user confusion about
// missing keyring entries later.
if let Some(keyring_provider) = keyring_provider.to_provider() {
if let Some(provider) = keyring_provider.to_provider(&preview) {
if password.is_none() {
if let Some(username) = &username {
debug!("Fetching password from keyring");
if let Some(keyring_password) = keyring_provider
if let Some(keyring_password) = provider
.fetch(DisplaySafeUrl::ref_cast(&publish_url), Some(username))
.await
.as_ref()
@ -350,6 +354,7 @@ mod tests {
None,
Prompt::Disabled,
Printer::Quiet,
Preview::default(),
)
.await
}

View file

@ -453,6 +453,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
args.token,
args.keyring_provider,
printer,
globals.preview,
)
.await
}
@ -463,7 +464,14 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
let args = settings::AuthLogoutSettings::resolve(args, filesystem);
show_settings!(args);
commands::auth_logout(args.service, args.username, args.keyring_provider, printer).await
commands::auth_logout(
args.service,
args.username,
args.keyring_provider,
printer,
globals.preview,
)
.await
}
Commands::Auth(AuthNamespace {
command: AuthCommand::Token(args),
@ -472,7 +480,14 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
let args = settings::AuthTokenSettings::resolve(args, filesystem);
show_settings!(args);
commands::auth_token(args.service, args.username, args.keyring_provider, printer).await
commands::auth_token(
args.service,
args.username,
args.keyring_provider,
printer,
globals.preview,
)
.await
}
Commands::Help(args) => commands::help(
args.command.unwrap_or_default().as_slice(),
@ -1668,6 +1683,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
index_locations,
&cache,
printer,
globals.preview,
)
.await
}

View file

@ -38,6 +38,7 @@ fn add_package_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
× No solution found when resolving dependencies:
Because anyio was not found in the package registry and your project depends on anyio, we can conclude that your project's requirements are unsatisfiable.
@ -58,6 +59,7 @@ fn add_package_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Logged in to public@https://pypi-proxy.fly.dev/basic-auth/simple
"
);
@ -70,6 +72,7 @@ fn add_package_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Resolved 4 packages in [TIME]
Prepared 3 packages in [TIME]
Installed 3 packages in [TIME]
@ -89,6 +92,7 @@ fn add_package_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Logged out of public@https://pypi-proxy.fly.dev/basic-auth/simple
"
);
@ -100,6 +104,7 @@ fn add_package_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
× No solution found when resolving dependencies:
Because iniconfig was not found in the package registry and your project depends on iniconfig, we can conclude that your project's requirements are unsatisfiable.
@ -159,6 +164,7 @@ fn token_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Failed to fetch credentials for https://pypi-proxy.fly.dev/basic-auth/simple
");
@ -174,6 +180,7 @@ fn token_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Failed to fetch credentials for public@https://pypi-proxy.fly.dev/basic-auth/simple
");
@ -191,6 +198,7 @@ fn token_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Logged in to public@https://pypi-proxy.fly.dev/basic-auth/simple
"
);
@ -208,6 +216,7 @@ fn token_native_keyring() -> Result<()> {
heron
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
");
// Without the username
@ -221,6 +230,7 @@ fn token_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Failed to fetch credentials for https://pypi-proxy.fly.dev/basic-auth/simple
");
@ -237,6 +247,7 @@ fn token_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Failed to fetch credentials for private@https://pypi-proxy.fly.dev/basic-auth/simple
");
@ -252,6 +263,7 @@ fn token_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Logged in to https://pypi-proxy.fly.dev/basic-auth/simple
"
);
@ -267,6 +279,7 @@ fn token_native_keyring() -> Result<()> {
heron
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
");
Ok(())
@ -412,6 +425,7 @@ fn login_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: No password provided; did you mean to provide `--password` or `--token`?
");
@ -442,6 +456,7 @@ fn login_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Logged in to public@https://pypi-proxy.fly.dev/basic-auth/simple
"
);
@ -473,6 +488,7 @@ fn login_token_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Logged in to https://pypi-proxy.fly.dev/basic-auth/simple
"
);
@ -515,6 +531,7 @@ fn logout_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Logged out of https://pypi-proxy.fly.dev/basic-auth/simple
");
@ -528,6 +545,7 @@ fn logout_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Unable to remove credentials for https://pypi-proxy.fly.dev/basic-auth/simple
Caused by: No matching entry found in secure storage
");
@ -544,6 +562,7 @@ fn logout_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Unable to remove credentials for public@https://pypi-proxy.fly.dev/basic-auth/simple
Caused by: No matching entry found in secure storage
");
@ -562,6 +581,7 @@ fn logout_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Logged in to public@https://pypi-proxy.fly.dev/basic-auth/simple
"
);
@ -577,6 +597,7 @@ fn logout_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
error: Unable to remove credentials for https://pypi-proxy.fly.dev/basic-auth/simple
Caused by: No matching entry found in secure storage
");
@ -593,6 +614,7 @@ fn logout_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Logged out of public@https://pypi-proxy.fly.dev/basic-auth/simple
");
@ -621,6 +643,7 @@ fn logout_token_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Logged in to https://pypi-proxy.fly.dev/basic-auth/simple
"
);
@ -635,6 +658,7 @@ fn logout_token_native_keyring() -> Result<()> {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Logged out of https://pypi-proxy.fly.dev/basic-auth/simple
");
@ -659,6 +683,7 @@ fn login_url_parsing() {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Logged in to test@https://example.com/
");
@ -676,6 +701,7 @@ fn login_url_parsing() {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Logged in to test@http://example.com/
");
@ -692,6 +718,7 @@ fn login_url_parsing() {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Logged in to test@https://example.com/
");
@ -709,6 +736,7 @@ fn login_url_parsing() {
----- stdout -----
----- stderr -----
warning: The native keyring provider is experimental and may change without warning. Pass `--preview-features native-keyring` to disable this warning.
Logged in to test@https://example.com/simple
");