Allow URL dependencies in tool run --from (#5002)

## Summary

Converting to a lock requires that we generate hashes; but generating
hashes isn't required here. So let's just use a different representation
for the cache key.

Closes https://github.com/astral-sh/uv/issues/4990.
This commit is contained in:
Charlie Marsh 2024-07-11 18:31:07 -07:00 committed by GitHub
parent 9643fb99d1
commit 6949796110
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 45 additions and 5 deletions

View file

@ -1,6 +1,8 @@
use itertools::Itertools;
use tracing::debug;
use cache_key::digest;
use distribution_types::Resolution;
use pypi_types::Requirement;
use uv_cache::{Cache, CacheBucket};
use uv_client::Connectivity;
@ -8,7 +10,6 @@ use uv_configuration::{Concurrency, PreviewMode};
use uv_fs::{LockedFile, Simplified};
use uv_python::{Interpreter, PythonEnvironment};
use uv_requirements::RequirementsSpecification;
use uv_resolver::Lock;
use crate::commands::project::{resolve_environment, sync_environment};
use crate::commands::SharedState;
@ -59,7 +60,7 @@ impl CachedEnvironment {
};
// Resolve the requirements with the interpreter.
let resolution = resolve_environment(
let graph = resolve_environment(
&interpreter,
spec,
settings.as_ref().into(),
@ -72,13 +73,17 @@ impl CachedEnvironment {
printer,
)
.await?;
let resolution = Resolution::from(graph);
// Hash the resolution by hashing the generated lockfile.
// TODO(charlie): If the resolution contains any mutable metadata (like a path or URL
// dependency), skip this step.
// TODO(charlie): Consider implementing `CacheKey` for `Resolution`.
let resolution_hash = digest(
&Lock::from_resolution_graph(&resolution)?
.to_toml()?
&resolution
.distributions()
.map(std::string::ToString::to_string)
.join("\n")
.as_bytes(),
);
@ -126,7 +131,7 @@ impl CachedEnvironment {
// struct that lets us "continue" from `resolve_environment`.
let venv = sync_environment(
venv,
&resolution.into(),
&resolution,
settings.as_ref().into(),
state,
preview,

View file

@ -461,3 +461,38 @@ fn tool_run_cache() {
+ platformdirs==4.2.0
"###);
}
#[test]
fn tool_run_url() {
let context = TestContext::new("3.12").with_filtered_counts();
let tool_dir = context.temp_dir.child("tools");
let bin_dir = context.temp_dir.child("bin");
uv_snapshot!(context.filters(), context.tool_run()
.arg("--from")
.arg("flask @ https://files.pythonhosted.org/packages/61/80/ffe1da13ad9300f87c93af113edd0638c75138c42a0994becfacac078c06/flask-3.0.3-py3-none-any.whl")
.arg("flask")
.arg("--version")
.env("UV_TOOL_DIR", tool_dir.as_os_str())
.env("XDG_BIN_HOME", bin_dir.as_os_str()), @r###"
success: true
exit_code: 0
----- stdout -----
Python 3.12.[X]
Flask 3.0.3
Werkzeug 3.0.1
----- stderr -----
warning: `uv tool run` is experimental and may change without warning.
Resolved [N] packages in [TIME]
Prepared [N] packages in [TIME]
Installed [N] packages in [TIME]
+ blinker==1.7.0
+ click==8.1.7
+ flask==3.0.3 (from https://files.pythonhosted.org/packages/61/80/ffe1da13ad9300f87c93af113edd0638c75138c42a0994becfacac078c06/flask-3.0.3-py3-none-any.whl)
+ itsdangerous==2.1.2
+ jinja2==3.1.3
+ markupsafe==2.1.5
+ werkzeug==3.0.1
"###);
}