Support empty dependencies in PEP 723 scripts (#5864)

## Summary

Closes https://github.com/astral-sh/uv/issues/5859.
This commit is contained in:
Charlie Marsh 2024-08-07 10:56:05 -04:00 committed by GitHub
parent 8998149ac1
commit 3ae75a21aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 57 additions and 23 deletions

View File

@ -14,7 +14,7 @@ static FINDER: LazyLock<Finder> = LazyLock::new(|| Finder::new(b"# /// script"))
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Pep723Metadata {
pub dependencies: Vec<pep508_rs::Requirement<VerbatimParsedUrl>>,
pub dependencies: Option<Vec<pep508_rs::Requirement<VerbatimParsedUrl>>>,
pub requires_python: Option<pep440_rs::VersionSpecifiers>,
}

View File

@ -92,6 +92,7 @@ pub(crate) async fn run(
let reporter = PythonDownloadReporter::single(printer.filter(show_resolution));
// Determine whether the command to execute is a PEP 723 script.
let temp_dir;
let script_interpreter = if let RunCommand::Python(target, _) = &command {
if let Some(metadata) = uv_scripts::read_pep723_metadata(&target).await? {
writeln!(
@ -129,28 +130,39 @@ pub(crate) async fn run(
.await?
.into_interpreter();
// Install the script requirements.
let requirements = metadata
.dependencies
.into_iter()
.map(Requirement::from)
.collect();
let spec = RequirementsSpecification::from_requirements(requirements);
let environment = CachedEnvironment::get_or_create(
spec,
interpreter,
&settings,
&state,
preview,
connectivity,
concurrency,
native_tls,
cache,
printer.filter(show_resolution),
)
.await?;
// Install the script requirements, if necessary. Otherwise, use an isolated environment.
if let Some(dependencies) = metadata.dependencies {
let requirements = dependencies.into_iter().map(Requirement::from).collect();
let spec = RequirementsSpecification::from_requirements(requirements);
let environment = CachedEnvironment::get_or_create(
spec,
interpreter,
&settings,
&state,
preview,
connectivity,
concurrency,
native_tls,
cache,
printer.filter(show_resolution),
)
.await?;
Some(environment.into_interpreter())
Some(environment.into_interpreter())
} else {
// Create a virtual environment
temp_dir = cache.environment()?;
let environment = uv_virtualenv::create_venv(
temp_dir.path(),
interpreter,
uv_virtualenv::Prompt::None,
false,
false,
false,
)?;
Some(environment.into_interpreter())
}
} else {
None
}

View File

@ -199,7 +199,7 @@ fn run_args() -> Result<()> {
/// Run a PEP 723-compatible script. The script should take precedence over the workspace
/// dependencies.
#[test]
fn run_script() -> Result<()> {
fn run_pep723_script() -> Result<()> {
let context = TestContext::new("3.12");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
@ -298,6 +298,28 @@ fn run_script() -> Result<()> {
Audited 4 packages in [TIME]
"###);
// If the script contains a PEP 723 tag, it can omit the dependencies field.
let test_script = context.temp_dir.child("main.py");
test_script.write_str(indoc! { r#"
# /// script
# requires-python = ">=3.11"
# ///
print("Hello, world!")
"#
})?;
// Running the script should install the requirements.
uv_snapshot!(context.filters(), context.run().arg("--preview").arg("main.py"), @r###"
success: true
exit_code: 0
----- stdout -----
Hello, world!
----- stderr -----
Reading inline script metadata from: main.py
"###);
Ok(())
}