Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 0b7b597

Browse files
committedDec 25, 2024·
Respect PEP 723 script lockfiles in uv run
1 parent 9ca5fd2 commit 0b7b597

File tree

10 files changed

+658
-266
lines changed

10 files changed

+658
-266
lines changed
 

‎crates/uv-configuration/src/dev.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ impl From<GroupsSpecification> for DevGroupsSpecification {
316316

317317
/// The manifest of `dependency-groups` to include, taking into account the user-provided
318318
/// [`DevGroupsSpecification`] and the project-specific default groups.
319-
#[derive(Debug, Clone)]
319+
#[derive(Debug, Default, Clone)]
320320
pub struct DevGroupsManifest {
321321
/// The specification for the development dependencies.
322322
pub(crate) spec: DevGroupsSpecification,
@@ -347,7 +347,7 @@ impl DevGroupsManifest {
347347
}
348348

349349
/// Returns `true` if the group was enabled by default.
350-
pub fn default(&self, group: &GroupName) -> bool {
350+
pub fn is_default(&self, group: &GroupName) -> bool {
351351
if self.spec.contains(group) {
352352
// If the group was explicitly requested, then it wasn't enabled by default.
353353
false

‎crates/uv-scripts/src/lib.rs

+20
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ impl Pep723Item {
5454
Self::Remote(_) => None,
5555
}
5656
}
57+
58+
/// Return the PEP 723 script, if any.
59+
pub fn as_script(&self) -> Option<&Pep723Script> {
60+
match self {
61+
Self::Script(script) => Some(script),
62+
_ => None,
63+
}
64+
}
5765
}
5866

5967
/// A PEP 723 script, including its [`Pep723Metadata`].
@@ -193,6 +201,18 @@ impl Pep723Script {
193201

194202
Ok(())
195203
}
204+
205+
/// Return the [`Sources`] defined in the PEP 723 metadata.
206+
pub fn sources(&self) -> &BTreeMap<PackageName, Sources> {
207+
static EMPTY: BTreeMap<PackageName, Sources> = BTreeMap::new();
208+
209+
self.metadata
210+
.tool
211+
.as_ref()
212+
.and_then(|tool| tool.uv.as_ref())
213+
.and_then(|uv| uv.sources.as_ref())
214+
.unwrap_or(&EMPTY)
215+
}
196216
}
197217

198218
/// PEP 723 metadata as parsed from a `script` comment block.

‎crates/uv/src/commands/project/add.rs

+1
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,7 @@ pub(crate) async fn add(
593593
Target::Project(project, environment) => (project, environment),
594594
// If `--script`, exit early. There's no reason to lock and sync.
595595
Target::Script(script, _) => {
596+
// TODO(charlie): Lock the script, if a lockfile already exists.
596597
writeln!(
597598
printer.stderr(),
598599
"Updated `{}`",

‎crates/uv/src/commands/project/environment.rs

+119-19
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use tracing::debug;
22

33
use crate::commands::pip::loggers::{InstallLogger, ResolveLogger};
4+
use crate::commands::project::install_target::InstallTarget;
45
use crate::commands::project::{
56
resolve_environment, sync_environment, EnvironmentSpecification, ProjectError,
67
};
@@ -9,10 +10,13 @@ use crate::settings::ResolverInstallerSettings;
910
use uv_cache::{Cache, CacheBucket};
1011
use uv_cache_key::{cache_digest, hash_digest};
1112
use uv_client::Connectivity;
12-
use uv_configuration::{Concurrency, PreviewMode, TrustedHost};
13+
use uv_configuration::{
14+
Concurrency, DevGroupsManifest, ExtrasSpecification, InstallOptions, PreviewMode, TrustedHost,
15+
};
1316
use uv_dispatch::SharedState;
1417
use uv_distribution_types::{Name, Resolution};
1518
use uv_python::{Interpreter, PythonEnvironment};
19+
use uv_resolver::Installable;
1620

1721
/// A [`PythonEnvironment`] stored in the cache.
1822
#[derive(Debug)]
@@ -25,9 +29,8 @@ impl From<CachedEnvironment> for PythonEnvironment {
2529
}
2630

2731
impl CachedEnvironment {
28-
/// Get or create an [`CachedEnvironment`] based on a given set of requirements and a base
29-
/// interpreter.
30-
pub(crate) async fn get_or_create(
32+
/// Get or create an [`CachedEnvironment`] based on a given set of requirements.
33+
pub(crate) async fn from_spec(
3134
spec: EnvironmentSpecification<'_>,
3235
interpreter: Interpreter,
3336
settings: &ResolverInstallerSettings,
@@ -43,21 +46,7 @@ impl CachedEnvironment {
4346
printer: Printer,
4447
preview: PreviewMode,
4548
) -> Result<Self, ProjectError> {
46-
// When caching, always use the base interpreter, rather than that of the virtual
47-
// environment.
48-
let interpreter = if let Some(interpreter) = interpreter.to_base_interpreter(cache)? {
49-
debug!(
50-
"Caching via base interpreter: `{}`",
51-
interpreter.sys_executable().display()
52-
);
53-
interpreter
54-
} else {
55-
debug!(
56-
"Caching via interpreter: `{}`",
57-
interpreter.sys_executable().display()
58-
);
59-
interpreter
60-
};
49+
let interpreter = Self::base_interpreter(interpreter, cache)?;
6150

6251
// Resolve the requirements with the interpreter.
6352
let resolution = Resolution::from(
@@ -78,6 +67,93 @@ impl CachedEnvironment {
7867
.await?,
7968
);
8069

70+
Self::from_resolution(
71+
resolution,
72+
interpreter,
73+
settings,
74+
state,
75+
install,
76+
installer_metadata,
77+
connectivity,
78+
concurrency,
79+
native_tls,
80+
allow_insecure_host,
81+
cache,
82+
printer,
83+
preview,
84+
)
85+
.await
86+
}
87+
88+
/// Get or create an [`CachedEnvironment`] based on a given [`InstallTarget`].
89+
pub(crate) async fn from_lock(
90+
target: InstallTarget<'_>,
91+
extras: &ExtrasSpecification,
92+
dev: &DevGroupsManifest,
93+
install_options: InstallOptions,
94+
settings: &ResolverInstallerSettings,
95+
interpreter: Interpreter,
96+
state: &SharedState,
97+
install: Box<dyn InstallLogger>,
98+
installer_metadata: bool,
99+
connectivity: Connectivity,
100+
concurrency: Concurrency,
101+
native_tls: bool,
102+
allow_insecure_host: &[TrustedHost],
103+
cache: &Cache,
104+
printer: Printer,
105+
preview: PreviewMode,
106+
) -> Result<Self, ProjectError> {
107+
let interpreter = Self::base_interpreter(interpreter, cache)?;
108+
109+
// Determine the tags, markers, and interpreter to use for resolution.
110+
let tags = interpreter.tags()?;
111+
let marker_env = interpreter.resolver_marker_environment();
112+
113+
// Read the lockfile.
114+
let resolution = target.to_resolution(
115+
&marker_env,
116+
tags,
117+
extras,
118+
dev,
119+
&settings.build_options,
120+
&install_options,
121+
)?;
122+
123+
Self::from_resolution(
124+
resolution,
125+
interpreter,
126+
settings,
127+
state,
128+
install,
129+
installer_metadata,
130+
connectivity,
131+
concurrency,
132+
native_tls,
133+
allow_insecure_host,
134+
cache,
135+
printer,
136+
preview,
137+
)
138+
.await
139+
}
140+
141+
/// Get or create an [`CachedEnvironment`] based on a given [`Resolution`].
142+
pub(crate) async fn from_resolution(
143+
resolution: Resolution,
144+
interpreter: Interpreter,
145+
settings: &ResolverInstallerSettings,
146+
state: &SharedState,
147+
install: Box<dyn InstallLogger>,
148+
installer_metadata: bool,
149+
connectivity: Connectivity,
150+
concurrency: Concurrency,
151+
native_tls: bool,
152+
allow_insecure_host: &[TrustedHost],
153+
cache: &Cache,
154+
printer: Printer,
155+
preview: PreviewMode,
156+
) -> Result<Self, ProjectError> {
81157
// Hash the resolution by hashing the generated lockfile.
82158
// TODO(charlie): If the resolution contains any mutable metadata (like a path or URL
83159
// dependency), skip this step.
@@ -144,4 +220,28 @@ impl CachedEnvironment {
144220
pub(crate) fn into_interpreter(self) -> Interpreter {
145221
self.0.into_interpreter()
146222
}
223+
224+
/// Return the [`Interpreter`] to use for the cached environment, based on a given
225+
/// [`Interpreter`].
226+
///
227+
/// When caching, always use the base interpreter, rather than that of the virtual
228+
/// environment.
229+
fn base_interpreter(
230+
interpreter: Interpreter,
231+
cache: &Cache,
232+
) -> Result<Interpreter, uv_python::Error> {
233+
if let Some(interpreter) = interpreter.to_base_interpreter(cache)? {
234+
debug!(
235+
"Caching via base interpreter: `{}`",
236+
interpreter.sys_executable().display()
237+
);
238+
Ok(interpreter)
239+
} else {
240+
debug!(
241+
"Caching via interpreter: `{}`",
242+
interpreter.sys_executable().display()
243+
);
244+
Ok(interpreter)
245+
}
246+
}
147247
}

0 commit comments

Comments
 (0)
Please sign in to comment.