Support remote `pylock.toml` files (#17119)

## Summary

Closes https://github.com/astral-sh/uv/issues/17112.
This commit is contained in:
Charlie Marsh 2025-12-16 19:16:23 -05:00 committed by GitHub
parent 4f6f56b070
commit e603761862
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 26 additions and 8 deletions

View File

@ -335,7 +335,7 @@ impl RequirementsSpecification {
}
}
RequirementsSource::PylockToml(path) => {
if !path.is_file() {
if !(path.starts_with("http://") || path.starts_with("https://") || path.exists()) {
return Err(anyhow::anyhow!("File not found: `{}`", path.user_display()));
}

View File

@ -500,10 +500,28 @@ pub(crate) async fn pip_install(
);
let (resolution, hasher) = if let Some(pylock) = pylock {
// Read the `pylock.toml` from disk, and deserialize it from TOML.
// Read the `pylock.toml` from disk or URL, and deserialize it from TOML.
let (install_path, content) =
if pylock.starts_with("http://") || pylock.starts_with("https://") {
// Fetch the `pylock.toml` over HTTP(S).
let url = uv_redacted::DisplaySafeUrl::parse(&pylock.to_string_lossy())?;
let client = client_builder.build();
let response = client
.for_host(&url)
.get(url::Url::from(url.clone()))
.send()
.await?;
response.error_for_status_ref()?;
let content = response.text().await?;
// Use the current working directory as the install path for remote lock files.
let install_path = std::env::current_dir()?;
(install_path, content)
} else {
let install_path = std::path::absolute(&pylock)?;
let install_path = install_path.parent().unwrap();
let install_path = install_path.parent().unwrap().to_path_buf();
let content = fs_err::tokio::read_to_string(&pylock).await?;
(install_path, content)
};
let lock = toml::from_str::<PylockToml>(&content).with_context(|| {
format!("Not a valid `pylock.toml` file: {}", pylock.user_display())
})?;
@ -537,7 +555,7 @@ pub(crate) async fn pip_install(
.collect::<Vec<_>>();
let resolution = lock.to_resolution(
install_path,
&install_path,
marker_env.markers(),
&extras,
&groups,

View File

@ -840,7 +840,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
.combine(Refresh::from(args.settings.upgrade.clone())),
);
commands::pip_install(
Box::pin(commands::pip_install(
&requirements,
&constraints,
&overrides,
@ -892,7 +892,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
args.dry_run,
printer,
globals.preview,
)
))
.await
}
Commands::Pip(PipNamespace {