mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 19:08:04 +00:00
Initialize PEP 723 script in uv lock --script
(#11717)
## Summary Like `uv add --script`, `uv lock --script` will now initialize a PEP 723 script tag if it doesn't already exist. --------- Co-authored-by: Zanie Blue <contact@zanie.dev>
This commit is contained in:
parent
c18c8f478e
commit
f3ebd04a9a
4 changed files with 92 additions and 26 deletions
|
@ -10,7 +10,7 @@ use rustc_hash::{FxBuildHasher, FxHashMap};
|
|||
use tracing::debug;
|
||||
|
||||
use uv_cache::Cache;
|
||||
use uv_client::{Connectivity, FlatIndexClient, RegistryClientBuilder};
|
||||
use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder};
|
||||
use uv_configuration::{
|
||||
Concurrency, Constraints, DevGroupsSpecification, DryRun, ExtrasSpecification, PreviewMode,
|
||||
Reinstall, TrustedHost, Upgrade,
|
||||
|
@ -41,10 +41,11 @@ use uv_workspace::{DiscoveryOptions, Workspace, WorkspaceMember};
|
|||
use crate::commands::pip::loggers::{DefaultResolveLogger, ResolveLogger, SummaryResolveLogger};
|
||||
use crate::commands::project::lock_target::LockTarget;
|
||||
use crate::commands::project::{
|
||||
ProjectError, ProjectInterpreter, ScriptInterpreter, UniversalState,
|
||||
init_script_python_requirement, ProjectError, ProjectInterpreter, ScriptInterpreter,
|
||||
UniversalState,
|
||||
};
|
||||
use crate::commands::reporters::ResolverReporter;
|
||||
use crate::commands::{diagnostics, pip, ExitStatus};
|
||||
use crate::commands::reporters::{PythonDownloadReporter, ResolverReporter};
|
||||
use crate::commands::{diagnostics, pip, ExitStatus, ScriptPath};
|
||||
use crate::printer::Printer;
|
||||
use crate::settings::{ResolverSettings, ResolverSettingsRef};
|
||||
|
||||
|
@ -83,7 +84,7 @@ pub(crate) async fn lock(
|
|||
python: Option<String>,
|
||||
install_mirrors: PythonInstallMirrors,
|
||||
settings: ResolverSettings,
|
||||
script: Option<Pep723Script>,
|
||||
script: Option<ScriptPath>,
|
||||
python_preference: PythonPreference,
|
||||
python_downloads: PythonDownloads,
|
||||
connectivity: Connectivity,
|
||||
|
@ -95,6 +96,33 @@ pub(crate) async fn lock(
|
|||
printer: Printer,
|
||||
preview: PreviewMode,
|
||||
) -> anyhow::Result<ExitStatus> {
|
||||
// If necessary, initialize the PEP 723 script.
|
||||
let script = match script {
|
||||
Some(ScriptPath::Path(path)) => {
|
||||
let client_builder = BaseClientBuilder::new()
|
||||
.connectivity(connectivity)
|
||||
.native_tls(native_tls)
|
||||
.allow_insecure_host(allow_insecure_host.to_vec());
|
||||
let reporter = PythonDownloadReporter::single(printer);
|
||||
let requires_python = init_script_python_requirement(
|
||||
python.as_deref(),
|
||||
&install_mirrors,
|
||||
project_dir,
|
||||
false,
|
||||
python_preference,
|
||||
python_downloads,
|
||||
no_config,
|
||||
&client_builder,
|
||||
cache,
|
||||
&reporter,
|
||||
)
|
||||
.await?;
|
||||
Some(Pep723Script::init(&path, requires_python.specifiers()).await?)
|
||||
}
|
||||
Some(ScriptPath::Script(script)) => Some(script),
|
||||
None => None,
|
||||
};
|
||||
|
||||
// Find the project requirements.
|
||||
let workspace;
|
||||
let target = if let Some(script) = script.as_ref() {
|
||||
|
|
|
@ -193,22 +193,25 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
) => Pep723Metadata::parse(contents)?.map(Pep723Item::Stdin),
|
||||
_ => None,
|
||||
},
|
||||
// For `uv add --script` and `uv lock --script`, we'll create a PEP 723 tag if it
|
||||
// doesn't already exist.
|
||||
ProjectCommand::Add(uv_cli::AddArgs {
|
||||
script: Some(script),
|
||||
..
|
||||
})
|
||||
| ProjectCommand::Lock(uv_cli::LockArgs {
|
||||
script: Some(script),
|
||||
..
|
||||
}) => match Pep723Script::read(&script).await {
|
||||
Ok(Some(script)) => Some(Pep723Item::Script(script)),
|
||||
Ok(None) => None,
|
||||
Err(err) => return Err(err.into()),
|
||||
},
|
||||
// For the remaining commands, the PEP 723 tag must exist already.
|
||||
ProjectCommand::Remove(uv_cli::RemoveArgs {
|
||||
script: Some(script),
|
||||
..
|
||||
})
|
||||
| ProjectCommand::Lock(uv_cli::LockArgs {
|
||||
script: Some(script),
|
||||
..
|
||||
})
|
||||
| ProjectCommand::Sync(uv_cli::SyncArgs {
|
||||
script: Some(script),
|
||||
..
|
||||
|
@ -223,8 +226,6 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
}) => match Pep723Script::read(&script).await {
|
||||
Ok(Some(script)) => Some(Pep723Item::Script(script)),
|
||||
Ok(None) => {
|
||||
// TODO(charlie): `uv lock --script` should initialize the tag, if it doesn't
|
||||
// exist.
|
||||
bail!(
|
||||
"`{}` does not contain a PEP 723 metadata tag; run `{}` to initialize the script",
|
||||
script.user_display().cyan(),
|
||||
|
@ -1582,12 +1583,18 @@ async fn run_project(
|
|||
.combine(Refresh::from(args.settings.upgrade.clone())),
|
||||
);
|
||||
|
||||
// Unwrap the script.
|
||||
let script = script.map(|script| match script {
|
||||
Pep723Item::Script(script) => script,
|
||||
Pep723Item::Stdin(..) => unreachable!("`uv lock` does not support stdin"),
|
||||
Pep723Item::Remote(..) => unreachable!("`uv lock` does not support remote files"),
|
||||
});
|
||||
// If the script already exists, use it; otherwise, propagate the file path and we'll
|
||||
// initialize it later on.
|
||||
let script = script
|
||||
.map(|script| match script {
|
||||
Pep723Item::Script(script) => script,
|
||||
Pep723Item::Stdin(..) => unreachable!("`uv add` does not support stdin"),
|
||||
Pep723Item::Remote(..) => {
|
||||
unreachable!("`uv add` does not support remote files")
|
||||
}
|
||||
})
|
||||
.map(ScriptPath::Script)
|
||||
.or(args.script.map(ScriptPath::Path));
|
||||
|
||||
Box::pin(commands::lock(
|
||||
project_dir,
|
||||
|
|
|
@ -152,6 +152,19 @@ impl TestContext {
|
|||
self
|
||||
}
|
||||
|
||||
/// Add extra standard filtering for Windows-compatible missing file errors.
|
||||
pub fn with_filtered_missing_file_error(mut self) -> Self {
|
||||
self.filters.push((
|
||||
regex::escape("The system cannot find the file specified. (os error 2)"),
|
||||
"[OS ERROR 2]".to_string(),
|
||||
));
|
||||
self.filters.push((
|
||||
regex::escape("No such file or directory (os error 2)"),
|
||||
"[OS ERROR 2]".to_string(),
|
||||
));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add extra standard filtering for executable suffixes on the current platform e.g.
|
||||
/// drops `.exe` on Windows.
|
||||
#[must_use]
|
||||
|
|
|
@ -23619,34 +23619,52 @@ fn lock_script_path() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// `uv lock --script` should add a PEP 723 tag, if it doesn't exist already.
|
||||
#[test]
|
||||
fn lock_script_error() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
fn lock_script_initialize() -> Result<()> {
|
||||
let context = TestContext::new("3.12").with_filtered_missing_file_error();
|
||||
|
||||
uv_snapshot!(context.filters(), context.lock().arg("--script").arg("script.py"), @r###"
|
||||
uv_snapshot!(context.filters(), context.lock().arg("--script").arg("script.py"), @r"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: Failed to read `script.py` (not found); run `uv init --script script.py` to create a PEP 723 script
|
||||
"###);
|
||||
error: failed to read from file `script.py`: [OS ERROR 2]
|
||||
");
|
||||
|
||||
let script = context.temp_dir.child("script.py");
|
||||
script.write_str(indoc! { r"
|
||||
import anyio
|
||||
print('Hello, world!')
|
||||
"
|
||||
})?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.lock().arg("--script").arg("script.py"), @r###"
|
||||
success: false
|
||||
exit_code: 2
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: `script.py` does not contain a PEP 723 metadata tag; run `uv init --script script.py` to initialize the script
|
||||
Resolved in [TIME]
|
||||
"###);
|
||||
|
||||
let lock = context.read("script.py.lock");
|
||||
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
lock, @r###"
|
||||
version = 1
|
||||
revision = 1
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[options]
|
||||
exclude-newer = "2024-03-25T00:00:00Z"
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue