mirror of https://github.com/astral-sh/uv
Rewrite
This commit is contained in:
parent
902d7694ab
commit
61cd8402b5
|
|
@ -574,11 +574,6 @@ const fn is_terminal(c: char) -> bool {
|
|||
matches!(c, '\n' | '\r' | '#')
|
||||
}
|
||||
|
||||
/// Returns `true` if the character should terminate an option value (like `--extra-index-url`).
|
||||
const fn is_option_terminal(c: char) -> bool {
|
||||
matches!(c, '\n' | '\r' | '#' | ';')
|
||||
}
|
||||
|
||||
/// Parse a single entry, that is a requirement, an inclusion or a comment line.
|
||||
///
|
||||
/// Consumes all preceding trivia (whitespace and comments). If it returns `None`, we've reached
|
||||
|
|
@ -599,11 +594,12 @@ fn parse_entry(
|
|||
|
||||
let start = s.cursor();
|
||||
Ok(Some(if s.eat_if("-r") || s.eat_if("--requirement") {
|
||||
let filename = parse_value("--requirement", content, s, |c: char| !is_option_terminal(c))?;
|
||||
let filename = parse_value("--requirement", content, s, |c: char| !is_terminal(c))?;
|
||||
let filename = unquote(filename)
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_else(|| filename.to_string());
|
||||
let filename = strip_markers(&filename).to_string();
|
||||
let end = s.cursor();
|
||||
RequirementsTxtStatement::Requirements {
|
||||
filename,
|
||||
|
|
@ -611,11 +607,12 @@ fn parse_entry(
|
|||
end,
|
||||
}
|
||||
} else if s.eat_if("-c") || s.eat_if("--constraint") {
|
||||
let filename = parse_value("--constraint", content, s, |c: char| !is_option_terminal(c))?;
|
||||
let filename = parse_value("--constraint", content, s, |c: char| !is_terminal(c))?;
|
||||
let filename = unquote(filename)
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_else(|| filename.to_string());
|
||||
let filename = strip_markers(&filename).to_string();
|
||||
let end = s.cursor();
|
||||
RequirementsTxtStatement::Constraint {
|
||||
filename,
|
||||
|
|
@ -658,13 +655,14 @@ fn parse_entry(
|
|||
hashes,
|
||||
})
|
||||
} else if s.eat_if("-i") || s.eat_if("--index-url") {
|
||||
let given = parse_value("--index-url", content, s, |c: char| !is_option_terminal(c))?;
|
||||
let given = parse_value("--index-url", content, s, |c: char| !is_terminal(c))?;
|
||||
let given = unquote(given)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(Cow::Owned)
|
||||
.unwrap_or(Cow::Borrowed(given));
|
||||
let expanded = expand_env_vars(given.as_ref());
|
||||
let stripped = strip_markers(given.as_ref());
|
||||
let expanded = expand_env_vars(stripped);
|
||||
let url = if let Some(path) = std::path::absolute(expanded.as_ref())
|
||||
.ok()
|
||||
.filter(|path| path.exists())
|
||||
|
|
@ -687,17 +685,16 @@ fn parse_entry(
|
|||
}
|
||||
})?
|
||||
};
|
||||
// Consume any trailing content (like markers) until end of line.
|
||||
s.eat_until(['\r', '\n']);
|
||||
RequirementsTxtStatement::IndexUrl(url.with_given(given))
|
||||
} else if s.eat_if("--extra-index-url") {
|
||||
let given = parse_value("--extra-index-url", content, s, |c: char| !is_option_terminal(c))?;
|
||||
let given = parse_value("--extra-index-url", content, s, |c: char| !is_terminal(c))?;
|
||||
let given = unquote(given)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(Cow::Owned)
|
||||
.unwrap_or(Cow::Borrowed(given));
|
||||
let expanded = expand_env_vars(given.as_ref());
|
||||
let stripped = strip_markers(given.as_ref());
|
||||
let expanded = expand_env_vars(stripped);
|
||||
let url = if let Some(path) = std::path::absolute(expanded.as_ref())
|
||||
.ok()
|
||||
.filter(|path| path.exists())
|
||||
|
|
@ -720,19 +717,18 @@ fn parse_entry(
|
|||
}
|
||||
})?
|
||||
};
|
||||
// Consume any trailing content (like markers) until end of line.
|
||||
s.eat_until(['\r', '\n']);
|
||||
RequirementsTxtStatement::ExtraIndexUrl(url.with_given(given))
|
||||
} else if s.eat_if("--no-index") {
|
||||
RequirementsTxtStatement::NoIndex
|
||||
} else if s.eat_if("--find-links") || s.eat_if("-f") {
|
||||
let given = parse_value("--find-links", content, s, |c: char| !is_option_terminal(c))?;
|
||||
let given = parse_value("--find-links", content, s, |c: char| !is_terminal(c))?;
|
||||
let given = unquote(given)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(Cow::Owned)
|
||||
.unwrap_or(Cow::Borrowed(given));
|
||||
let expanded = expand_env_vars(given.as_ref());
|
||||
let given = strip_markers(given.as_ref());
|
||||
let expanded = expand_env_vars(given);
|
||||
let url = if let Some(path) = std::path::absolute(expanded.as_ref())
|
||||
.ok()
|
||||
.filter(|path| path.exists())
|
||||
|
|
@ -755,44 +751,40 @@ fn parse_entry(
|
|||
}
|
||||
})?
|
||||
};
|
||||
// Consume any trailing content (like markers) until end of line.
|
||||
s.eat_until(['\r', '\n']);
|
||||
RequirementsTxtStatement::FindLinks(url.with_given(given))
|
||||
} else if s.eat_if("--no-binary") {
|
||||
let given = parse_value("--no-binary", content, s, |c: char| !is_option_terminal(c))?;
|
||||
let given = parse_value("--no-binary", content, s, |c: char| !is_terminal(c))?;
|
||||
let given = unquote(given)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(Cow::Owned)
|
||||
.unwrap_or(Cow::Borrowed(given));
|
||||
let specifier = PackageNameSpecifier::from_str(given.as_ref()).map_err(|err| {
|
||||
let stripped = strip_markers(given.as_ref());
|
||||
let specifier = PackageNameSpecifier::from_str(stripped).map_err(|err| {
|
||||
RequirementsTxtParserError::NoBinary {
|
||||
source: err,
|
||||
specifier: given.to_string(),
|
||||
specifier: stripped.to_string(),
|
||||
start,
|
||||
end: s.cursor(),
|
||||
}
|
||||
})?;
|
||||
// Consume any trailing content (like markers) until end of line.
|
||||
s.eat_until(['\r', '\n']);
|
||||
RequirementsTxtStatement::NoBinary(NoBinary::from_pip_arg(specifier))
|
||||
} else if s.eat_if("--only-binary") {
|
||||
let given = parse_value("--only-binary", content, s, |c: char| !is_option_terminal(c))?;
|
||||
let given = parse_value("--only-binary", content, s, |c: char| !is_terminal(c))?;
|
||||
let given = unquote(given)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(Cow::Owned)
|
||||
.unwrap_or(Cow::Borrowed(given));
|
||||
let specifier = PackageNameSpecifier::from_str(given.as_ref()).map_err(|err| {
|
||||
let stripped = strip_markers(given.as_ref());
|
||||
let specifier = PackageNameSpecifier::from_str(stripped).map_err(|err| {
|
||||
RequirementsTxtParserError::NoBinary {
|
||||
source: err,
|
||||
specifier: given.to_string(),
|
||||
specifier: stripped.to_string(),
|
||||
start,
|
||||
end: s.cursor(),
|
||||
}
|
||||
})?;
|
||||
// Consume any trailing content (like markers) until end of line.
|
||||
s.eat_until(['\r', '\n']);
|
||||
RequirementsTxtStatement::OnlyBinary(NoBuild::from_pip_arg(specifier))
|
||||
} else if s.at(char::is_ascii_alphanumeric) || s.at(|char| matches!(char, '.' | '/' | '$')) {
|
||||
let source = if requirements_txt == Path::new("-") {
|
||||
|
|
@ -828,6 +820,45 @@ fn parse_entry(
|
|||
}))
|
||||
}
|
||||
|
||||
/// Remove any trailing markers from an option value.
|
||||
///
|
||||
/// For example, given `--index-url https://pypi,org/simple ; python_version < "3.8"`,
|
||||
/// remove the trailing marker and return `https://pypi,org/simple`.
|
||||
fn strip_markers(content: &str) -> &str {
|
||||
let mut escaped = false;
|
||||
for (i, c) in content.char_indices() {
|
||||
if escaped {
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Respect escape characters.
|
||||
if c == '\\' {
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we see a semicolon, check if it's followed by whitespace (marker syntax).
|
||||
// If so, stop parsing and omit the semicolon.
|
||||
if c == ';' {
|
||||
let rest = &content[i + 1..];
|
||||
if rest.chars().next().is_some_and(char::is_whitespace) {
|
||||
return &content[..i];
|
||||
}
|
||||
}
|
||||
|
||||
// If we see whitespace followed by a semicolon, stop parsing at the whitespace.
|
||||
if c.is_whitespace() {
|
||||
let rest = &content[i..];
|
||||
if rest.trim_start().starts_with(';') {
|
||||
return &content[..i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
content
|
||||
}
|
||||
|
||||
/// Eat whitespace and ignore newlines escaped with a backslash
|
||||
fn eat_wrappable_whitespace<'a>(s: &mut Scanner<'a>) -> &'a str {
|
||||
let start = s.cursor();
|
||||
|
|
|
|||
|
|
@ -44,7 +44,47 @@ RequirementsTxt {
|
|||
fragment: None,
|
||||
},
|
||||
given: Some(
|
||||
"https://download.pytorch.org/whl/nightly/cpu",
|
||||
"https://download.pytorch.org/whl/nightly/cpu ; sys_platform == darwin",
|
||||
),
|
||||
},
|
||||
VerbatimUrl {
|
||||
url: DisplaySafeUrl {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"download.pytorch.org",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/whl/nightly/cpu",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
given: Some(
|
||||
"https://download.pytorch.org/whl/nightly/cpu; sys_platform == darwin",
|
||||
),
|
||||
},
|
||||
VerbatimUrl {
|
||||
url: DisplaySafeUrl {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"download.pytorch.org",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/whl/cu129",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
given: Some(
|
||||
"https://download.pytorch.org/whl/cu129; sys_platform != 'darwin'",
|
||||
),
|
||||
},
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
# Test that command-line options stop at markers (semicolons)
|
||||
--extra-index-url=https://download.pytorch.org/whl/nightly/cpu ; sys_platform == 'darwin'
|
||||
--extra-index-url=https://download.pytorch.org/whl/nightly/cpu; sys_platform == 'darwin'
|
||||
--extra-index-url="https://download.pytorch.org/whl/cu129; sys_platform != 'darwin'"
|
||||
numpy
|
||||
|
|
|
|||
Loading…
Reference in New Issue