Skip to content

Commit

Permalink
Warn on unclosed script tags (#6704)
Browse files Browse the repository at this point in the history
Should this be user-facing by default? It seems annoying because then
it's unavoidable if you (for whatever reason) have an intentionally
unclosed tag.

Motivated by #6700.
  • Loading branch information
charliermarsh authored Aug 27, 2024
1 parent eb14056 commit a8f4e08
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 9 deletions.
27 changes: 18 additions & 9 deletions crates/uv-scripts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ use std::str::FromStr;
use std::sync::LazyLock;

use memchr::memmem::Finder;
use pep440_rs::VersionSpecifiers;
use serde::Deserialize;
use thiserror::Error;

use pep440_rs::VersionSpecifiers;
use pep508_rs::PackageName;
use pypi_types::VerbatimParsedUrl;
use uv_settings::{GlobalOptions, ResolverInstallerOptions};
Expand Down Expand Up @@ -41,13 +41,14 @@ impl Pep723Script {
};

// Extract the `script` tag.
let Some(ScriptTag {
let ScriptTag {
prelude,
metadata,
postlude,
}) = ScriptTag::parse(&contents)?
else {
return Ok(None);
} = match ScriptTag::parse(&contents) {
Ok(Some(tag)) => tag,
Ok(None) => return Ok(None),
Err(err) => return Err(err),
};

// Parse the metadata.
Expand Down Expand Up @@ -152,6 +153,8 @@ pub struct ToolUv {

#[derive(Debug, Error)]
pub enum Pep723Error {
#[error("An opening tag (`# /// script`) was found without a closing tag (`# ///`). Ensure that every line between the opening and closing tags (including empty lines) starts with a leading `#`.")]
UnclosedBlock,
#[error(transparent)]
Io(#[from] io::Error),
#[error(transparent)]
Expand Down Expand Up @@ -272,7 +275,7 @@ impl ScriptTag {
//
// The latter `///` is the closing pragma
let Some(index) = toml.iter().rev().position(|line| *line == "///") else {
return Ok(None);
return Err(Pep723Error::UnclosedBlock);
};
let index = toml.len() - index;

Expand Down Expand Up @@ -364,7 +367,7 @@ fn serialize_metadata(metadata: &str) -> String {

#[cfg(test)]
mod tests {
use crate::{serialize_metadata, ScriptTag};
use crate::{serialize_metadata, Pep723Error, ScriptTag};

#[test]
fn missing_space() {
Expand All @@ -374,7 +377,10 @@ mod tests {
# ///
"};

assert_eq!(ScriptTag::parse(contents.as_bytes()).unwrap(), None);
assert!(matches!(
ScriptTag::parse(contents.as_bytes()),
Err(Pep723Error::UnclosedBlock)
));
}

#[test]
Expand All @@ -388,7 +394,10 @@ mod tests {
# ]
"};

assert_eq!(ScriptTag::parse(contents.as_bytes()).unwrap(), None);
assert!(matches!(
ScriptTag::parse(contents.as_bytes()),
Err(Pep723Error::UnclosedBlock)
));
}

#[test]
Expand Down
24 changes: 24 additions & 0 deletions crates/uv/tests/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,30 @@ fn run_pep723_script() -> Result<()> {
╰─▶ Because there are no versions of add and you require add, we can conclude that your requirements are unsatisfiable.
"###);

// If the script contains an unclosed PEP 723 tag, we should error.
let test_script = context.temp_dir.child("main.py");
test_script.write_str(indoc! { r#"
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "iniconfig",
# ]
# ///
import iniconfig
"#
})?;

uv_snapshot!(context.filters(), context.run().arg("--no-project").arg("main.py"), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: An opening tag (`# /// script`) was found without a closing tag (`# ///`). Ensure that every line between the opening and closing tags (including empty lines) starts with a leading `#`.
"###);

Ok(())
}

Expand Down

0 comments on commit a8f4e08

Please sign in to comment.