mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Add support for parsing unnamed URL requirements (#2567)
## Summary First piece of https://github.com/astral-sh/uv/issues/313. In order to support unnamed requirements, we need to be able to parse them in `requirements-txt`, which in turn means that we need to introduce a new type that's distinct from `pep508::Requirement`, given that these _aren't_ PEP 508-compatible requirements. Part of: https://github.com/astral-sh/uv/issues/313.
This commit is contained in:
parent
0af6a3d41d
commit
ee211b35bc
30 changed files with 2122 additions and 1305 deletions
|
@ -122,23 +122,105 @@ create_exception!(
|
|||
"A PEP 508 parser error with span information"
|
||||
);
|
||||
|
||||
/// A PEP 508 dependency specification
|
||||
/// A requirement specifier in a `requirements.txt` file.
|
||||
#[derive(Hash, Debug, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum RequirementsTxtRequirement {
|
||||
/// A PEP 508-compliant dependency specifier.
|
||||
Pep508(Requirement),
|
||||
/// A PEP 508-like, direct URL dependency specifier.
|
||||
Unnamed(UnnamedRequirement),
|
||||
}
|
||||
|
||||
impl Display for RequirementsTxtRequirement {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Pep508(requirement) => write!(f, "{requirement}"),
|
||||
Self::Unnamed(requirement) => write!(f, "{requirement}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A PEP 508-like, direct URL dependency specifier without a package name.
|
||||
///
|
||||
/// In a `requirements.txt` file, the name of the package is optional for direct URL
|
||||
/// dependencies. This isn't compliant with PEP 508, but is common in `requirements.txt`, which
|
||||
/// is implementation-defined.
|
||||
#[derive(Hash, Debug, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "pyo3", pyclass(module = "pep508"))]
|
||||
pub struct UnnamedRequirement {
|
||||
/// The direct URL that defines the version specifier.
|
||||
pub url: VerbatimUrl,
|
||||
/// The list of extras such as `security`, `tests` in
|
||||
/// `requests [security,tests] >= 2.8.1, == 2.8.* ; python_version > "3.8"`.
|
||||
pub extras: Vec<ExtraName>,
|
||||
/// The markers such as `python_version > "3.8"` in
|
||||
/// `requests [security,tests] >= 2.8.1, == 2.8.* ; python_version > "3.8"`.
|
||||
/// Those are a nested and/or tree.
|
||||
pub marker: Option<MarkerTree>,
|
||||
}
|
||||
|
||||
impl Display for UnnamedRequirement {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.url)?;
|
||||
if !self.extras.is_empty() {
|
||||
write!(
|
||||
f,
|
||||
"[{}]",
|
||||
self.extras
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<_>>()
|
||||
.join(",")
|
||||
)?;
|
||||
}
|
||||
if let Some(marker) = &self.marker {
|
||||
write!(f, " ; {}", marker)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://github.com/serde-rs/serde/issues/908#issuecomment-298027413>
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> Deserialize<'de> for UnnamedRequirement {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
FromStr::from_str(&s).map_err(de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://github.com/serde-rs/serde/issues/1316#issue-332908452>
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for UnnamedRequirement {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.collect_str(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// A PEP 508 dependency specifier.
|
||||
#[derive(Hash, Debug, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "pyo3", pyclass(module = "pep508"))]
|
||||
pub struct Requirement {
|
||||
/// The distribution name such as `numpy` in
|
||||
/// `requests [security,tests] >= 2.8.1, == 2.8.* ; python_version > "3.8"`
|
||||
/// `requests [security,tests] >= 2.8.1, == 2.8.* ; python_version > "3.8"`.
|
||||
pub name: PackageName,
|
||||
/// The list of extras such as `security`, `tests` in
|
||||
/// `requests [security,tests] >= 2.8.1, == 2.8.* ; python_version > "3.8"`
|
||||
/// `requests [security,tests] >= 2.8.1, == 2.8.* ; python_version > "3.8"`.
|
||||
pub extras: Vec<ExtraName>,
|
||||
/// The version specifier such as `>= 2.8.1`, `== 2.8.*` in
|
||||
/// `requests [security,tests] >= 2.8.1, == 2.8.* ; python_version > "3.8"`
|
||||
/// `requests [security,tests] >= 2.8.1, == 2.8.* ; python_version > "3.8"`.
|
||||
/// or a url
|
||||
pub version_or_url: Option<VersionOrUrl>,
|
||||
/// The markers such as `python_version > "3.8"` in
|
||||
/// `requests [security,tests] >= 2.8.1, == 2.8.* ; python_version > "3.8"`.
|
||||
/// Those are a nested and/or tree
|
||||
/// Those are a nested and/or tree.
|
||||
pub marker: Option<MarkerTree>,
|
||||
}
|
||||
|
||||
|
@ -377,7 +459,7 @@ impl Requirement {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns whether the markers apply for the given environment
|
||||
/// Returns whether the markers apply for the given environment.
|
||||
pub fn evaluate_markers_and_report(
|
||||
&self,
|
||||
env: &MarkerEnvironment,
|
||||
|
@ -394,16 +476,69 @@ impl Requirement {
|
|||
impl FromStr for Requirement {
|
||||
type Err = Pep508Error;
|
||||
|
||||
/// Parse a [Dependency Specifier](https://packaging.python.org/en/latest/specifications/dependency-specifiers/)
|
||||
/// Parse a [Dependency Specifier](https://packaging.python.org/en/latest/specifications/dependency-specifiers/).
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
parse(&mut Cursor::new(input), None)
|
||||
parse_pep508_requirement(&mut Cursor::new(input), None)
|
||||
}
|
||||
}
|
||||
|
||||
impl Requirement {
|
||||
/// Parse a [Dependency Specifier](https://packaging.python.org/en/latest/specifications/dependency-specifiers/)
|
||||
/// Parse a [Dependency Specifier](https://packaging.python.org/en/latest/specifications/dependency-specifiers/).
|
||||
pub fn parse(input: &str, working_dir: impl AsRef<Path>) -> Result<Self, Pep508Error> {
|
||||
parse(&mut Cursor::new(input), Some(working_dir.as_ref()))
|
||||
parse_pep508_requirement(&mut Cursor::new(input), Some(working_dir.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for UnnamedRequirement {
|
||||
type Err = Pep508Error;
|
||||
|
||||
/// Parse a PEP 508-like direct URL requirement without a package name.
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
parse_unnamed_requirement(&mut Cursor::new(input), None)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnnamedRequirement {
|
||||
/// Parse a PEP 508-like direct URL requirement without a package name.
|
||||
pub fn parse(input: &str, working_dir: impl AsRef<Path>) -> Result<Self, Pep508Error> {
|
||||
parse_unnamed_requirement(&mut Cursor::new(input), Some(working_dir.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for RequirementsTxtRequirement {
|
||||
type Err = Pep508Error;
|
||||
|
||||
/// Parse a requirement as seen in a `requirements.txt` file.
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
match Requirement::from_str(input) {
|
||||
Ok(requirement) => Ok(Self::Pep508(requirement)),
|
||||
Err(err) => match err.message {
|
||||
Pep508ErrorSource::UnsupportedRequirement(_) => {
|
||||
Ok(Self::Unnamed(UnnamedRequirement::from_str(input)?))
|
||||
}
|
||||
_ => Err(err),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RequirementsTxtRequirement {
|
||||
/// Parse a requirement as seen in a `requirements.txt` file.
|
||||
pub fn parse(input: &str, working_dir: impl AsRef<Path>) -> Result<Self, Pep508Error> {
|
||||
// Attempt to parse as a PEP 508-compliant requirement.
|
||||
match Requirement::parse(input, &working_dir) {
|
||||
Ok(requirement) => Ok(Self::Pep508(requirement)),
|
||||
Err(err) => match err.message {
|
||||
Pep508ErrorSource::UnsupportedRequirement(_) => {
|
||||
// If that fails, attempt to parse as a direct URL requirement.
|
||||
Ok(Self::Unnamed(UnnamedRequirement::parse(
|
||||
input,
|
||||
&working_dir,
|
||||
)?))
|
||||
}
|
||||
_ => Err(err),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -575,7 +710,7 @@ fn parse_name(cursor: &mut Cursor) -> Result<PackageName, Pep508Error> {
|
|||
// Check if the user added a filesystem path without a package name. pip supports this
|
||||
// in `requirements.txt`, but it doesn't adhere to the PEP 508 grammar.
|
||||
let mut clone = cursor.clone().at(start);
|
||||
return if looks_like_file_path(&mut clone) {
|
||||
return if looks_like_unnamed_requirement(&mut clone) {
|
||||
Err(Pep508Error {
|
||||
message: Pep508ErrorSource::UnsupportedRequirement("URL requirement must be preceded by a package name. Add the name of the package before the URL (e.g., `package_name @ /path/to/file`).".to_string()),
|
||||
start,
|
||||
|
@ -627,6 +762,37 @@ fn parse_name(cursor: &mut Cursor) -> Result<PackageName, Pep508Error> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parse a potential URL from the [`Cursor`], advancing the [`Cursor`] to the end of the URL.
|
||||
///
|
||||
/// Returns `true` if the URL appears to be a viable unnamed requirement, and `false` otherwise.
|
||||
fn looks_like_unnamed_requirement(cursor: &mut Cursor) -> bool {
|
||||
// Read the entire path.
|
||||
let (start, len) = cursor.take_while(|char| !char.is_whitespace());
|
||||
let url = cursor.slice(start, len);
|
||||
|
||||
// Expand any environment variables in the path.
|
||||
let expanded = expand_env_vars(url);
|
||||
|
||||
// Analyze the path.
|
||||
let mut chars = expanded.chars();
|
||||
|
||||
let Some(first_char) = chars.next() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Ex) `/bin/ls`
|
||||
if first_char == '\\' || first_char == '/' || first_char == '.' {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ex) `https://` or `C:`
|
||||
if split_scheme(&expanded).is_some() {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// parses extras in the `[extra1,extra2] format`
|
||||
fn parse_extras(cursor: &mut Cursor) -> Result<Vec<ExtraName>, Pep508Error> {
|
||||
let Some(bracket_pos) = cursor.eat_char('[') else {
|
||||
|
@ -767,35 +933,6 @@ fn parse_url(cursor: &mut Cursor, working_dir: Option<&Path>) -> Result<Verbatim
|
|||
Ok(url)
|
||||
}
|
||||
|
||||
/// Parse a filesystem path from the [`Cursor`], advancing the [`Cursor`] to the end of the path.
|
||||
///
|
||||
/// Returns `false` if the path is not a clear and unambiguous filesystem path.
|
||||
fn looks_like_file_path(cursor: &mut Cursor) -> bool {
|
||||
let Some((_, first_char)) = cursor.next() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Ex) `/bin/ls`
|
||||
if first_char == '\\' || first_char == '/' || first_char == '.' {
|
||||
// Read until the end of the path.
|
||||
cursor.take_while(|char| !char.is_whitespace());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ex) `C:`
|
||||
if first_char.is_alphabetic() {
|
||||
if let Some((_, second_char)) = cursor.next() {
|
||||
if second_char == ':' {
|
||||
// Read until the end of the path.
|
||||
cursor.take_while(|char| !char.is_whitespace());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Create a `VerbatimUrl` to represent the requirement.
|
||||
fn preprocess_url(
|
||||
url: &str,
|
||||
|
@ -882,6 +1019,172 @@ fn preprocess_url(
|
|||
}
|
||||
}
|
||||
|
||||
/// Like [`parse_url`], but allows for extras to be present at the end of the URL, to comply
|
||||
/// with the non-PEP 508 extensions.
|
||||
///
|
||||
/// For example:
|
||||
/// - `https://download.pytorch.org/whl/torch_stable.html[dev]`
|
||||
/// - `../editable[dev]`
|
||||
fn parse_unnamed_url(
|
||||
cursor: &mut Cursor,
|
||||
working_dir: Option<&Path>,
|
||||
) -> Result<(VerbatimUrl, Vec<ExtraName>), Pep508Error> {
|
||||
// wsp*
|
||||
cursor.eat_whitespace();
|
||||
// <URI_reference>
|
||||
let (start, len) = cursor.take_while(|char| !char.is_whitespace());
|
||||
let url = cursor.slice(start, len);
|
||||
if url.is_empty() {
|
||||
return Err(Pep508Error {
|
||||
message: Pep508ErrorSource::String("Expected URL".to_string()),
|
||||
start,
|
||||
len,
|
||||
input: cursor.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let url = preprocess_unnamed_url(url, working_dir, cursor, start, len)?;
|
||||
|
||||
Ok(url)
|
||||
}
|
||||
|
||||
/// Create a `VerbatimUrl` to represent the requirement, and extracts any extras at the end of the
|
||||
/// URL, to comply with the non-PEP 508 extensions.
|
||||
fn preprocess_unnamed_url(
|
||||
url: &str,
|
||||
#[cfg_attr(not(feature = "non-pep508-extensions"), allow(unused))] working_dir: Option<&Path>,
|
||||
cursor: &Cursor,
|
||||
start: usize,
|
||||
len: usize,
|
||||
) -> Result<(VerbatimUrl, Vec<ExtraName>), Pep508Error> {
|
||||
// Split extras _before_ expanding the URL. We assume that the extras are not environment
|
||||
// variables. If we parsed the extras after expanding the URL, then the verbatim representation
|
||||
// of the URL itself would be ambiguous, since it would consist of the environment variable,
|
||||
// which would expand to _more_ than the URL.
|
||||
let (url, extras) = if let Some((url, extras)) = split_extras(url) {
|
||||
(url, Some(extras))
|
||||
} else {
|
||||
(url, None)
|
||||
};
|
||||
|
||||
// Parse the extras, if provided.
|
||||
let extras = if let Some(extras) = extras {
|
||||
parse_extras(&mut Cursor::new(extras)).map_err(|err| Pep508Error {
|
||||
message: err.message,
|
||||
start: start + url.len() + err.start,
|
||||
len: err.len,
|
||||
input: cursor.to_string(),
|
||||
})?
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
// Expand environment variables in the URL.
|
||||
let expanded = expand_env_vars(url);
|
||||
|
||||
if let Some((scheme, path)) = split_scheme(&expanded) {
|
||||
match Scheme::parse(scheme) {
|
||||
// Ex) `file:///home/ferris/project/scripts/...` or `file:../editable/`.
|
||||
Some(Scheme::File) => {
|
||||
let path = path.strip_prefix("//").unwrap_or(path);
|
||||
|
||||
// Transform, e.g., `/C:/Users/ferris/wheel-0.42.0.tar.gz` to `C:\Users\ferris\wheel-0.42.0.tar.gz`.
|
||||
let path = normalize_url_path(path);
|
||||
|
||||
#[cfg(feature = "non-pep508-extensions")]
|
||||
if let Some(working_dir) = working_dir {
|
||||
let url = VerbatimUrl::parse_path(path.as_ref(), working_dir)
|
||||
.with_given(url.to_string());
|
||||
return Ok((url, extras));
|
||||
}
|
||||
|
||||
let url = VerbatimUrl::parse_absolute_path(path.as_ref())
|
||||
.map_err(|err| Pep508Error {
|
||||
message: Pep508ErrorSource::UrlError(err),
|
||||
start,
|
||||
len,
|
||||
input: cursor.to_string(),
|
||||
})?
|
||||
.with_given(url.to_string());
|
||||
Ok((url, extras))
|
||||
}
|
||||
// Ex) `https://download.pytorch.org/whl/torch_stable.html`
|
||||
Some(_) => {
|
||||
// Ex) `https://download.pytorch.org/whl/torch_stable.html`
|
||||
let url = VerbatimUrl::parse_url(expanded.as_ref())
|
||||
.map_err(|err| Pep508Error {
|
||||
message: Pep508ErrorSource::UrlError(VerbatimUrlError::Url(err)),
|
||||
start,
|
||||
len,
|
||||
input: cursor.to_string(),
|
||||
})?
|
||||
.with_given(url.to_string());
|
||||
Ok((url, extras))
|
||||
}
|
||||
|
||||
// Ex) `C:\Users\ferris\wheel-0.42.0.tar.gz`
|
||||
_ => {
|
||||
#[cfg(feature = "non-pep508-extensions")]
|
||||
if let Some(working_dir) = working_dir {
|
||||
let url = VerbatimUrl::parse_path(expanded.as_ref(), working_dir)
|
||||
.with_given(url.to_string());
|
||||
return Ok((url, extras));
|
||||
}
|
||||
|
||||
let url = VerbatimUrl::parse_absolute_path(expanded.as_ref())
|
||||
.map_err(|err| Pep508Error {
|
||||
message: Pep508ErrorSource::UrlError(err),
|
||||
start,
|
||||
len,
|
||||
input: cursor.to_string(),
|
||||
})?
|
||||
.with_given(url.to_string());
|
||||
Ok((url, extras))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Ex) `../editable/`
|
||||
#[cfg(feature = "non-pep508-extensions")]
|
||||
if let Some(working_dir) = working_dir {
|
||||
let url =
|
||||
VerbatimUrl::parse_path(expanded.as_ref(), working_dir).with_given(url.to_string());
|
||||
return Ok((url, extras));
|
||||
}
|
||||
|
||||
let url = VerbatimUrl::parse_absolute_path(expanded.as_ref())
|
||||
.map_err(|err| Pep508Error {
|
||||
message: Pep508ErrorSource::UrlError(err),
|
||||
start,
|
||||
len,
|
||||
input: cursor.to_string(),
|
||||
})?
|
||||
.with_given(url.to_string());
|
||||
Ok((url, extras))
|
||||
}
|
||||
}
|
||||
|
||||
/// Identify the extras in a relative URL (e.g., `../editable[dev]`).
|
||||
///
|
||||
/// Pip uses `m = re.match(r'^(.+)(\[[^]]+])$', path)`. Our strategy is:
|
||||
/// - If the string ends with a closing bracket (`]`)...
|
||||
/// - Iterate backwards until you find the open bracket (`[`)...
|
||||
/// - But abort if you find another closing bracket (`]`) first.
|
||||
pub fn split_extras(given: &str) -> Option<(&str, &str)> {
|
||||
let mut chars = given.char_indices().rev();
|
||||
|
||||
// If the string ends with a closing bracket (`]`)...
|
||||
if !matches!(chars.next(), Some((_, ']'))) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Iterate backwards until you find the open bracket (`[`)...
|
||||
let (index, _) = chars
|
||||
.take_while(|(_, c)| *c != ']')
|
||||
.find(|(_, c)| *c == '[')?;
|
||||
|
||||
Some(given.split_at(index))
|
||||
}
|
||||
|
||||
/// PEP 440 wrapper
|
||||
fn parse_specifier(
|
||||
cursor: &mut Cursor,
|
||||
|
@ -973,8 +1276,11 @@ fn parse_version_specifier_parentheses(
|
|||
Ok(requirement_kind)
|
||||
}
|
||||
|
||||
/// Parse a [dependency specifier](https://packaging.python.org/en/latest/specifications/dependency-specifiers)
|
||||
fn parse(cursor: &mut Cursor, working_dir: Option<&Path>) -> Result<Requirement, Pep508Error> {
|
||||
/// Parse a PEP 508-compliant [dependency specifier](https://packaging.python.org/en/latest/specifications/dependency-specifiers).
|
||||
fn parse_pep508_requirement(
|
||||
cursor: &mut Cursor,
|
||||
working_dir: Option<&Path>,
|
||||
) -> Result<Requirement, Pep508Error> {
|
||||
let start = cursor.pos();
|
||||
|
||||
// Technically, the grammar is:
|
||||
|
@ -1088,6 +1394,64 @@ fn parse(cursor: &mut Cursor, working_dir: Option<&Path>) -> Result<Requirement,
|
|||
})
|
||||
}
|
||||
|
||||
/// Parse a PEP 508-like direct URL specifier without a package name.
|
||||
///
|
||||
/// Unlike pip, we allow extras on URLs and paths.
|
||||
fn parse_unnamed_requirement(
|
||||
cursor: &mut Cursor,
|
||||
working_dir: Option<&Path>,
|
||||
) -> Result<UnnamedRequirement, Pep508Error> {
|
||||
cursor.eat_whitespace();
|
||||
|
||||
// Parse the URL itself, along with any extras.
|
||||
let (url, extras) = parse_unnamed_url(cursor, working_dir)?;
|
||||
let requirement_end = cursor.pos;
|
||||
|
||||
// wsp*
|
||||
cursor.eat_whitespace();
|
||||
// quoted_marker?
|
||||
let marker = if cursor.peek_char() == Some(';') {
|
||||
// Skip past the semicolon
|
||||
cursor.next();
|
||||
Some(marker::parse_markers_impl(cursor)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// wsp*
|
||||
cursor.eat_whitespace();
|
||||
if let Some((pos, char)) = cursor.next() {
|
||||
if let Some(given) = url.given() {
|
||||
if given.ends_with(';') && marker.is_none() {
|
||||
return Err(Pep508Error {
|
||||
message: Pep508ErrorSource::String(
|
||||
"Missing space before ';', the end of the URL is ambiguous".to_string(),
|
||||
),
|
||||
start: requirement_end - ';'.len_utf8(),
|
||||
len: ';'.len_utf8(),
|
||||
input: cursor.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
let message = if marker.is_none() {
|
||||
format!(r#"Expected end of input or ';', found '{char}'"#)
|
||||
} else {
|
||||
format!(r#"Expected end of input, found '{char}'"#)
|
||||
};
|
||||
return Err(Pep508Error {
|
||||
message: Pep508ErrorSource::String(message),
|
||||
start: pos,
|
||||
len: char.len_utf8(),
|
||||
input: cursor.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(UnnamedRequirement {
|
||||
url,
|
||||
extras,
|
||||
marker,
|
||||
})
|
||||
}
|
||||
|
||||
/// A library for [dependency specifiers](https://packaging.python.org/en/latest/specifications/dependency-specifiers/)
|
||||
/// as originally specified in [PEP 508](https://peps.python.org/pep-0508/)
|
||||
///
|
||||
|
@ -1128,12 +1492,16 @@ mod tests {
|
|||
parse_markers_impl, MarkerExpression, MarkerOperator, MarkerTree, MarkerValue,
|
||||
MarkerValueString, MarkerValueVersion,
|
||||
};
|
||||
use crate::{Cursor, Pep508Error, Requirement, VerbatimUrl, VersionOrUrl};
|
||||
use crate::{Cursor, Pep508Error, Requirement, UnnamedRequirement, VerbatimUrl, VersionOrUrl};
|
||||
|
||||
fn parse_err(input: &str) -> String {
|
||||
fn parse_pepe508_err(input: &str) -> String {
|
||||
Requirement::from_str(input).unwrap_err().to_string()
|
||||
}
|
||||
|
||||
fn parse_unnamed_err(input: &str) -> String {
|
||||
UnnamedRequirement::from_str(input).unwrap_err().to_string()
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn test_preprocess_url_windows() {
|
||||
|
@ -1155,7 +1523,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_empty() {
|
||||
assert_snapshot!(
|
||||
parse_err(""),
|
||||
parse_pepe508_err(""),
|
||||
@r"
|
||||
Empty field is not allowed for PEP508
|
||||
|
||||
|
@ -1166,7 +1534,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_start() {
|
||||
assert_snapshot!(
|
||||
parse_err("_name"),
|
||||
parse_pepe508_err("_name"),
|
||||
@"
|
||||
Expected package name starting with an alphanumeric character, found '_'
|
||||
_name
|
||||
|
@ -1177,7 +1545,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_end() {
|
||||
assert_snapshot!(
|
||||
parse_err("name_"),
|
||||
parse_pepe508_err("name_"),
|
||||
@"
|
||||
Package name must end with an alphanumeric character, not '_'
|
||||
name_
|
||||
|
@ -1245,10 +1613,43 @@ mod tests {
|
|||
assert_eq!(numpy.name.as_ref(), "numpy");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn direct_url_no_extras() {
|
||||
let numpy = UnnamedRequirement::from_str("https://files.pythonhosted.org/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl").unwrap();
|
||||
assert_eq!(numpy.url.to_string(), "https://files.pythonhosted.org/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl");
|
||||
assert_eq!(numpy.extras, vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn direct_url_extras() {
|
||||
let numpy =
|
||||
UnnamedRequirement::from_str("/path/to/numpy-1.26.4-cp312-cp312-win32.whl[dev]")
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
numpy.url.to_string(),
|
||||
"file:///path/to/numpy-1.26.4-cp312-cp312-win32.whl"
|
||||
);
|
||||
assert_eq!(numpy.extras, vec![ExtraName::from_str("dev").unwrap()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn direct_url_extras() {
|
||||
let numpy =
|
||||
UnnamedRequirement::from_str("C:\\path\\to\\numpy-1.26.4-cp312-cp312-win32.whl[dev]")
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
numpy.url.to_string(),
|
||||
"file:///C:/path/to/numpy-1.26.4-cp312-cp312-win32.whl"
|
||||
);
|
||||
assert_eq!(numpy.extras, vec![ExtraName::from_str("dev").unwrap()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_extras_eof1() {
|
||||
assert_snapshot!(
|
||||
parse_err("black["),
|
||||
parse_pepe508_err("black["),
|
||||
@"
|
||||
Missing closing bracket (expected ']', found end of dependency specification)
|
||||
black[
|
||||
|
@ -1259,7 +1660,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_extras_eof2() {
|
||||
assert_snapshot!(
|
||||
parse_err("black[d"),
|
||||
parse_pepe508_err("black[d"),
|
||||
@"
|
||||
Missing closing bracket (expected ']', found end of dependency specification)
|
||||
black[d
|
||||
|
@ -1270,7 +1671,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_extras_eof3() {
|
||||
assert_snapshot!(
|
||||
parse_err("black[d,"),
|
||||
parse_pepe508_err("black[d,"),
|
||||
@"
|
||||
Missing closing bracket (expected ']', found end of dependency specification)
|
||||
black[d,
|
||||
|
@ -1281,7 +1682,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_extras_illegal_start1() {
|
||||
assert_snapshot!(
|
||||
parse_err("black[ö]"),
|
||||
parse_pepe508_err("black[ö]"),
|
||||
@"
|
||||
Expected an alphanumeric character starting the extra name, found 'ö'
|
||||
black[ö]
|
||||
|
@ -1292,7 +1693,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_extras_illegal_start2() {
|
||||
assert_snapshot!(
|
||||
parse_err("black[_d]"),
|
||||
parse_pepe508_err("black[_d]"),
|
||||
@"
|
||||
Expected an alphanumeric character starting the extra name, found '_'
|
||||
black[_d]
|
||||
|
@ -1303,7 +1704,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_extras_illegal_start3() {
|
||||
assert_snapshot!(
|
||||
parse_err("black[,]"),
|
||||
parse_pepe508_err("black[,]"),
|
||||
@"
|
||||
Expected either alphanumerical character (starting the extra name) or ']' (ending the extras section), found ','
|
||||
black[,]
|
||||
|
@ -1314,7 +1715,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_extras_illegal_character() {
|
||||
assert_snapshot!(
|
||||
parse_err("black[jüpyter]"),
|
||||
parse_pepe508_err("black[jüpyter]"),
|
||||
@"
|
||||
Invalid character in extras name, expected an alphanumeric character, '-', '_', '.', ',' or ']', found 'ü'
|
||||
black[jüpyter]
|
||||
|
@ -1355,7 +1756,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_extra_with_trailing_comma() {
|
||||
assert_snapshot!(
|
||||
parse_err("black[d,]"),
|
||||
parse_pepe508_err("black[d,]"),
|
||||
@"
|
||||
Expected an alphanumeric character starting the extra name, found ']'
|
||||
black[d,]
|
||||
|
@ -1366,7 +1767,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_parenthesized_pep440() {
|
||||
assert_snapshot!(
|
||||
parse_err("numpy ( ><1.19 )"),
|
||||
parse_pepe508_err("numpy ( ><1.19 )"),
|
||||
@"
|
||||
no such comparison operator \"><\", must be one of ~= == != <= >= < > ===
|
||||
numpy ( ><1.19 )
|
||||
|
@ -1377,7 +1778,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_parenthesized_parenthesis() {
|
||||
assert_snapshot!(
|
||||
parse_err("numpy ( >=1.19"),
|
||||
parse_pepe508_err("numpy ( >=1.19"),
|
||||
@"
|
||||
Missing closing parenthesis (expected ')', found end of dependency specification)
|
||||
numpy ( >=1.19
|
||||
|
@ -1388,7 +1789,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_whats_that() {
|
||||
assert_snapshot!(
|
||||
parse_err("numpy % 1.16"),
|
||||
parse_pepe508_err("numpy % 1.16"),
|
||||
@"
|
||||
Expected one of `@`, `(`, `<`, `=`, `>`, `~`, `!`, `;`, found `%`
|
||||
numpy % 1.16
|
||||
|
@ -1454,7 +1855,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_marker_incomplete1() {
|
||||
assert_snapshot!(
|
||||
parse_err(r"numpy; sys_platform"),
|
||||
parse_pepe508_err(r"numpy; sys_platform"),
|
||||
@"
|
||||
Expected a valid marker operator (such as '>=' or 'not in'), found ''
|
||||
numpy; sys_platform
|
||||
|
@ -1465,7 +1866,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_marker_incomplete2() {
|
||||
assert_snapshot!(
|
||||
parse_err(r"numpy; sys_platform =="),
|
||||
parse_pepe508_err(r"numpy; sys_platform =="),
|
||||
@r"
|
||||
Expected marker value, found end of dependency specification
|
||||
numpy; sys_platform ==
|
||||
|
@ -1476,7 +1877,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_marker_incomplete3() {
|
||||
assert_snapshot!(
|
||||
parse_err(r#"numpy; sys_platform == "win32" or"#),
|
||||
parse_pepe508_err(r#"numpy; sys_platform == "win32" or"#),
|
||||
@r#"
|
||||
Expected marker value, found end of dependency specification
|
||||
numpy; sys_platform == "win32" or
|
||||
|
@ -1487,7 +1888,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_marker_incomplete4() {
|
||||
assert_snapshot!(
|
||||
parse_err(r#"numpy; sys_platform == "win32" or (os_name == "linux""#),
|
||||
parse_pepe508_err(r#"numpy; sys_platform == "win32" or (os_name == "linux""#),
|
||||
@r#"
|
||||
Expected ')', found end of dependency specification
|
||||
numpy; sys_platform == "win32" or (os_name == "linux"
|
||||
|
@ -1498,7 +1899,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_marker_incomplete5() {
|
||||
assert_snapshot!(
|
||||
parse_err(r#"numpy; sys_platform == "win32" or (os_name == "linux" and"#),
|
||||
parse_pepe508_err(r#"numpy; sys_platform == "win32" or (os_name == "linux" and"#),
|
||||
@r#"
|
||||
Expected marker value, found end of dependency specification
|
||||
numpy; sys_platform == "win32" or (os_name == "linux" and
|
||||
|
@ -1509,7 +1910,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_pep440() {
|
||||
assert_snapshot!(
|
||||
parse_err(r"numpy >=1.1.*"),
|
||||
parse_pepe508_err(r"numpy >=1.1.*"),
|
||||
@r"
|
||||
Operator >= cannot be used with a wildcard version specifier
|
||||
numpy >=1.1.*
|
||||
|
@ -1520,7 +1921,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_no_name() {
|
||||
assert_snapshot!(
|
||||
parse_err(r"==0.0"),
|
||||
parse_pepe508_err(r"==0.0"),
|
||||
@r"
|
||||
Expected package name starting with an alphanumeric character, found '='
|
||||
==0.0
|
||||
|
@ -1530,9 +1931,9 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn error_bare_url() {
|
||||
fn error_unnamedunnamed_url() {
|
||||
assert_snapshot!(
|
||||
parse_err(r"git+https://github.com/pallets/flask.git"),
|
||||
parse_pepe508_err(r"git+https://github.com/pallets/flask.git"),
|
||||
@"
|
||||
URL requirement must be preceded by a package name. Add the name of the package before the URL (e.g., `package_name @ https://...`).
|
||||
git+https://github.com/pallets/flask.git
|
||||
|
@ -1541,9 +1942,9 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn error_bare_file_path() {
|
||||
fn error_unnamed_file_path() {
|
||||
assert_snapshot!(
|
||||
parse_err(r"/path/to/flask.tar.gz"),
|
||||
parse_pepe508_err(r"/path/to/flask.tar.gz"),
|
||||
@r###"
|
||||
URL requirement must be preceded by a package name. Add the name of the package before the URL (e.g., `package_name @ /path/to/file`).
|
||||
/path/to/flask.tar.gz
|
||||
|
@ -1555,7 +1956,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_no_comma_between_extras() {
|
||||
assert_snapshot!(
|
||||
parse_err(r"name[bar baz]"),
|
||||
parse_pepe508_err(r"name[bar baz]"),
|
||||
@"
|
||||
Expected either ',' (separating extras) or ']' (ending the extras section), found 'b'
|
||||
name[bar baz]
|
||||
|
@ -1566,7 +1967,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_extra_comma_after_extras() {
|
||||
assert_snapshot!(
|
||||
parse_err(r"name[bar, baz,]"),
|
||||
parse_pepe508_err(r"name[bar, baz,]"),
|
||||
@"
|
||||
Expected an alphanumeric character starting the extra name, found ']'
|
||||
name[bar, baz,]
|
||||
|
@ -1577,7 +1978,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_extras_not_closed() {
|
||||
assert_snapshot!(
|
||||
parse_err(r"name[bar, baz >= 1.0"),
|
||||
parse_pepe508_err(r"name[bar, baz >= 1.0"),
|
||||
@"
|
||||
Expected either ',' (separating extras) or ']' (ending the extras section), found '>'
|
||||
name[bar, baz >= 1.0
|
||||
|
@ -1588,7 +1989,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_no_space_after_url() {
|
||||
assert_snapshot!(
|
||||
parse_err(r"name @ https://example.com/; extra == 'example'"),
|
||||
parse_pepe508_err(r"name @ https://example.com/; extra == 'example'"),
|
||||
@"
|
||||
Missing space before ';', the end of the URL is ambiguous
|
||||
name @ https://example.com/; extra == 'example'
|
||||
|
@ -1599,7 +2000,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_name_at_nothing() {
|
||||
assert_snapshot!(
|
||||
parse_err(r"name @"),
|
||||
parse_pepe508_err(r"name @"),
|
||||
@"
|
||||
Expected URL
|
||||
name @
|
||||
|
@ -1610,7 +2011,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_error_invalid_marker_key() {
|
||||
assert_snapshot!(
|
||||
parse_err(r"name; invalid_name"),
|
||||
parse_pepe508_err(r"name; invalid_name"),
|
||||
@"
|
||||
Expected a valid marker name, found 'invalid_name'
|
||||
name; invalid_name
|
||||
|
@ -1621,7 +2022,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_markers_invalid_order() {
|
||||
assert_snapshot!(
|
||||
parse_err("name; '3.7' <= invalid_name"),
|
||||
parse_pepe508_err("name; '3.7' <= invalid_name"),
|
||||
@"
|
||||
Expected a valid marker name, found 'invalid_name'
|
||||
name; '3.7' <= invalid_name
|
||||
|
@ -1632,7 +2033,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_markers_notin() {
|
||||
assert_snapshot!(
|
||||
parse_err("name; '3.7' notin python_version"),
|
||||
parse_pepe508_err("name; '3.7' notin python_version"),
|
||||
@"
|
||||
Expected a valid marker operator (such as '>=' or 'not in'), found 'notin'
|
||||
name; '3.7' notin python_version
|
||||
|
@ -1643,7 +2044,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_markers_inpython_version() {
|
||||
assert_snapshot!(
|
||||
parse_err("name; '3.6'inpython_version"),
|
||||
parse_pepe508_err("name; '3.6'inpython_version"),
|
||||
@"
|
||||
Expected a valid marker operator (such as '>=' or 'not in'), found 'inpython_version'
|
||||
name; '3.6'inpython_version
|
||||
|
@ -1654,7 +2055,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_markers_not_python_version() {
|
||||
assert_snapshot!(
|
||||
parse_err("name; '3.7' not python_version"),
|
||||
parse_pepe508_err("name; '3.7' not python_version"),
|
||||
@"
|
||||
Expected 'i', found 'p'
|
||||
name; '3.7' not python_version
|
||||
|
@ -1665,7 +2066,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_markers_invalid_operator() {
|
||||
assert_snapshot!(
|
||||
parse_err("name; '3.7' ~ python_version"),
|
||||
parse_pepe508_err("name; '3.7' ~ python_version"),
|
||||
@"
|
||||
Expected a valid marker operator (such as '>=' or 'not in'), found '~'
|
||||
name; '3.7' ~ python_version
|
||||
|
@ -1676,7 +2077,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_invalid_prerelease() {
|
||||
assert_snapshot!(
|
||||
parse_err("name==1.0.org1"),
|
||||
parse_pepe508_err("name==1.0.org1"),
|
||||
@"
|
||||
after parsing 1.0, found \".org1\" after it, which is not part of a valid version
|
||||
name==1.0.org1
|
||||
|
@ -1687,7 +2088,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_no_version_value() {
|
||||
assert_snapshot!(
|
||||
parse_err("name=="),
|
||||
parse_pepe508_err("name=="),
|
||||
@"
|
||||
Unexpected end of version specifier, expected version
|
||||
name==
|
||||
|
@ -1698,7 +2099,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_no_version_operator() {
|
||||
assert_snapshot!(
|
||||
parse_err("name 1.0"),
|
||||
parse_pepe508_err("name 1.0"),
|
||||
@"
|
||||
Expected one of `@`, `(`, `<`, `=`, `>`, `~`, `!`, `;`, found `1`
|
||||
name 1.0
|
||||
|
@ -1709,7 +2110,7 @@ mod tests {
|
|||
#[test]
|
||||
fn error_random_char() {
|
||||
assert_snapshot!(
|
||||
parse_err("name >= 1.0 #"),
|
||||
parse_pepe508_err("name >= 1.0 #"),
|
||||
@"
|
||||
Trailing `#` is not allowed
|
||||
name >= 1.0 #
|
||||
|
@ -1717,6 +2118,18 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_invalid_extra_unnamed_url() {
|
||||
assert_snapshot!(
|
||||
parse_unnamed_err("/foo-3.0.0-py3-none-any.whl[d,]"),
|
||||
@r###"
|
||||
Expected an alphanumeric character starting the extra name, found ']'
|
||||
/foo-3.0.0-py3-none-any.whl[d,]
|
||||
^
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
/// Check that the relative path support feature toggle works.
|
||||
#[test]
|
||||
fn non_pep508_paths() {
|
||||
|
|
|
@ -46,7 +46,8 @@ impl VerbatimUrl {
|
|||
let (path, fragment) = split_fragment(&path);
|
||||
|
||||
// Convert to a URL.
|
||||
let mut url = Url::from_file_path(path).expect("path is absolute");
|
||||
let mut url = Url::from_file_path(path.clone())
|
||||
.unwrap_or_else(|_| panic!("path is absolute: {}", path.display()));
|
||||
|
||||
// Set the fragment, if it exists.
|
||||
if let Some(fragment) = fragment {
|
||||
|
@ -81,7 +82,13 @@ impl VerbatimUrl {
|
|||
let (path, fragment) = split_fragment(&path);
|
||||
|
||||
// Convert to a URL.
|
||||
let mut url = Url::from_file_path(path).expect("path is absolute");
|
||||
let mut url = Url::from_file_path(path.clone()).unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"path is absolute: {}, {}",
|
||||
path.display(),
|
||||
working_dir.as_ref().display()
|
||||
)
|
||||
});
|
||||
|
||||
// Set the fragment, if it exists.
|
||||
if let Some(fragment) = fragment {
|
||||
|
@ -109,7 +116,8 @@ impl VerbatimUrl {
|
|||
let (path, fragment) = split_fragment(&path);
|
||||
|
||||
// Convert to a URL.
|
||||
let mut url = Url::from_file_path(path).expect("path is absolute");
|
||||
let mut url = Url::from_file_path(path.clone())
|
||||
.unwrap_or_else(|_| panic!("path is absolute: {}", path.display()));
|
||||
|
||||
// Set the fragment, if it exists.
|
||||
if let Some(fragment) = fragment {
|
||||
|
|
|
@ -46,8 +46,8 @@ use unscanny::{Pattern, Scanner};
|
|||
use url::Url;
|
||||
|
||||
use pep508_rs::{
|
||||
expand_env_vars, split_scheme, Extras, Pep508Error, Pep508ErrorSource, Requirement, Scheme,
|
||||
VerbatimUrl,
|
||||
expand_env_vars, split_scheme, Extras, Pep508Error, Pep508ErrorSource, Requirement,
|
||||
RequirementsTxtRequirement, Scheme, VerbatimUrl,
|
||||
};
|
||||
use uv_client::Connectivity;
|
||||
use uv_fs::{normalize_url_path, Simplified};
|
||||
|
@ -287,7 +287,7 @@ impl Display for EditableRequirement {
|
|||
#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Serialize)]
|
||||
pub struct RequirementEntry {
|
||||
/// The actual PEP 508 requirement
|
||||
pub requirement: Requirement,
|
||||
pub requirement: RequirementsTxtRequirement,
|
||||
/// Hashes of the downloadable packages
|
||||
pub hashes: Vec<String>,
|
||||
/// Editable installation, see e.g. <https://stackoverflow.com/q/35064426/3549270>
|
||||
|
@ -470,12 +470,19 @@ impl RequirementsTxt {
|
|||
// Treat any nested requirements or constraints as constraints. This differs
|
||||
// from `pip`, which seems to treat `-r` requirements in constraints files as
|
||||
// _requirements_, but we don't want to support that.
|
||||
data.constraints.extend(
|
||||
sub_constraints
|
||||
.requirements
|
||||
.into_iter()
|
||||
.map(|requirement_entry| requirement_entry.requirement),
|
||||
);
|
||||
for entry in sub_constraints.requirements {
|
||||
match entry.requirement {
|
||||
RequirementsTxtRequirement::Pep508(requirement) => {
|
||||
data.constraints.push(requirement);
|
||||
}
|
||||
RequirementsTxtRequirement::Unnamed(_) => {
|
||||
return Err(RequirementsTxtParserError::UnnamedConstraint {
|
||||
start,
|
||||
end,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
data.constraints.extend(sub_constraints.constraints);
|
||||
}
|
||||
RequirementsTxtStatement::RequirementEntry(requirement_entry) => {
|
||||
|
@ -610,7 +617,7 @@ fn parse_entry(
|
|||
}
|
||||
})?;
|
||||
RequirementsTxtStatement::FindLinks(path_or_url)
|
||||
} else if s.at(char::is_ascii_alphanumeric) {
|
||||
} else if s.at(char::is_ascii_alphanumeric) || s.at(|char| matches!(char, '.' | '/' | '$')) {
|
||||
let (requirement, hashes) = parse_requirement_and_hashes(s, content, working_dir)?;
|
||||
RequirementsTxtStatement::RequirementEntry(RequirementEntry {
|
||||
requirement,
|
||||
|
@ -675,7 +682,7 @@ fn parse_requirement_and_hashes(
|
|||
s: &mut Scanner,
|
||||
content: &str,
|
||||
working_dir: &Path,
|
||||
) -> Result<(Requirement, Vec<String>), RequirementsTxtParserError> {
|
||||
) -> Result<(RequirementsTxtRequirement, Vec<String>), RequirementsTxtParserError> {
|
||||
// PEP 508 requirement
|
||||
let start = s.cursor();
|
||||
// Termination: s.eat() eventually becomes None
|
||||
|
@ -731,41 +738,26 @@ fn parse_requirement_and_hashes(
|
|||
}
|
||||
}
|
||||
|
||||
// If the requirement looks like an editable requirement (with a missing `-e`), raise an
|
||||
// error.
|
||||
//
|
||||
// Slashes are not allowed in package names, so these would be rejected in the next step anyway.
|
||||
if requirement.contains('/') || requirement.contains('\\') {
|
||||
let path = Path::new(requirement);
|
||||
let path = if path.is_absolute() {
|
||||
Cow::Borrowed(path)
|
||||
} else {
|
||||
Cow::Owned(working_dir.join(path))
|
||||
};
|
||||
if path.is_dir() {
|
||||
return Err(RequirementsTxtParserError::MissingEditablePrefix(
|
||||
requirement.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let requirement =
|
||||
Requirement::parse(requirement, working_dir).map_err(|err| match err.message {
|
||||
Pep508ErrorSource::String(_) | Pep508ErrorSource::UrlError(_) => {
|
||||
RequirementsTxtParserError::Pep508 {
|
||||
source: err,
|
||||
start,
|
||||
end,
|
||||
RequirementsTxtRequirement::parse(requirement, working_dir).map_err(|err| {
|
||||
match err.message {
|
||||
Pep508ErrorSource::String(_) | Pep508ErrorSource::UrlError(_) => {
|
||||
RequirementsTxtParserError::Pep508 {
|
||||
source: err,
|
||||
start,
|
||||
end,
|
||||
}
|
||||
}
|
||||
}
|
||||
Pep508ErrorSource::UnsupportedRequirement(_) => {
|
||||
RequirementsTxtParserError::UnsupportedRequirement {
|
||||
source: err,
|
||||
start,
|
||||
end,
|
||||
Pep508ErrorSource::UnsupportedRequirement(_) => {
|
||||
RequirementsTxtParserError::UnsupportedRequirement {
|
||||
source: err,
|
||||
start,
|
||||
end,
|
||||
}
|
||||
}
|
||||
}
|
||||
})?;
|
||||
|
||||
let hashes = if has_hashes {
|
||||
let hashes = parse_hashes(content, s)?;
|
||||
eat_trailing_line(content, s)?;
|
||||
|
@ -863,7 +855,10 @@ pub enum RequirementsTxtParserError {
|
|||
InvalidEditablePath(String),
|
||||
UnsupportedUrl(String),
|
||||
MissingRequirementPrefix(String),
|
||||
MissingEditablePrefix(String),
|
||||
UnnamedConstraint {
|
||||
start: usize,
|
||||
end: usize,
|
||||
},
|
||||
Parser {
|
||||
message: String,
|
||||
line: usize,
|
||||
|
@ -911,7 +906,10 @@ impl RequirementsTxtParserError {
|
|||
},
|
||||
Self::UnsupportedUrl(url) => Self::UnsupportedUrl(url),
|
||||
Self::MissingRequirementPrefix(given) => Self::MissingRequirementPrefix(given),
|
||||
Self::MissingEditablePrefix(given) => Self::MissingEditablePrefix(given),
|
||||
Self::UnnamedConstraint { start, end } => Self::UnnamedConstraint {
|
||||
start: start + offset,
|
||||
end: end + offset,
|
||||
},
|
||||
Self::Parser {
|
||||
message,
|
||||
line,
|
||||
|
@ -959,11 +957,8 @@ impl Display for RequirementsTxtParserError {
|
|||
Self::MissingRequirementPrefix(given) => {
|
||||
write!(f, "Requirement `{given}` looks like a requirements file but was passed as a package name. Did you mean `-r {given}`?")
|
||||
}
|
||||
Self::MissingEditablePrefix(given) => {
|
||||
write!(
|
||||
f,
|
||||
"Requirement `{given}` looks like a directory but was passed as a package name. Did you mean `-e {given}`?"
|
||||
)
|
||||
Self::UnnamedConstraint { .. } => {
|
||||
write!(f, "Unnamed requirements are not allowed as constraints")
|
||||
}
|
||||
Self::Parser {
|
||||
message,
|
||||
|
@ -1004,7 +999,7 @@ impl std::error::Error for RequirementsTxtParserError {
|
|||
Self::InvalidEditablePath(_) => None,
|
||||
Self::UnsupportedUrl(_) => None,
|
||||
Self::MissingRequirementPrefix(_) => None,
|
||||
Self::MissingEditablePrefix(_) => None,
|
||||
Self::UnnamedConstraint { .. } => None,
|
||||
Self::UnsupportedRequirement { source, .. } => Some(source),
|
||||
Self::Pep508 { source, .. } => Some(source),
|
||||
Self::Subfile { source, .. } => Some(source.as_ref()),
|
||||
|
@ -1048,10 +1043,10 @@ impl Display for RequirementsTxtFileError {
|
|||
self.file.user_display(),
|
||||
)
|
||||
}
|
||||
RequirementsTxtParserError::MissingEditablePrefix(given) => {
|
||||
RequirementsTxtParserError::UnnamedConstraint { .. } => {
|
||||
write!(
|
||||
f,
|
||||
"Requirement `{given}` in `{}` looks like a directory but was passed as a package name. Did you mean `-e {given}`?",
|
||||
"Unnamed requirements are not allowed as constraints in `{}`",
|
||||
self.file.user_display(),
|
||||
)
|
||||
}
|
||||
|
@ -1177,14 +1172,14 @@ mod test {
|
|||
use tempfile::tempdir;
|
||||
use test_case::test_case;
|
||||
use unscanny::Scanner;
|
||||
use uv_client::Connectivity;
|
||||
|
||||
use uv_client::Connectivity;
|
||||
use uv_fs::Simplified;
|
||||
|
||||
use crate::{calculate_row_column, EditableRequirement, RequirementsTxt};
|
||||
|
||||
fn workspace_test_data_dir() -> PathBuf {
|
||||
PathBuf::from("./test-data")
|
||||
PathBuf::from("./test-data").canonicalize().unwrap()
|
||||
}
|
||||
|
||||
#[test_case(Path::new("basic.txt"))]
|
||||
|
@ -1254,6 +1249,53 @@ mod test {
|
|||
insta::assert_debug_snapshot!(snapshot, actual);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test_case(Path::new("bare-url.txt"))]
|
||||
#[tokio::test]
|
||||
async fn parse_unnamed_unix(path: &Path) {
|
||||
let working_dir = workspace_test_data_dir().join("requirements-txt");
|
||||
let requirements_txt = working_dir.join(path);
|
||||
|
||||
let actual = RequirementsTxt::parse(requirements_txt, &working_dir, Connectivity::Offline)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let snapshot = format!("parse-unix-{}", path.to_string_lossy());
|
||||
let pattern = regex::escape(&working_dir.simplified_display().to_string());
|
||||
let filters = vec![(pattern.as_str(), "[WORKSPACE_DIR]")];
|
||||
insta::with_settings!({
|
||||
filters => filters
|
||||
}, {
|
||||
insta::assert_debug_snapshot!(snapshot, actual);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test_case(Path::new("bare-url.txt"))]
|
||||
#[tokio::test]
|
||||
async fn parse_unnamed_windows(path: &Path) {
|
||||
let working_dir = workspace_test_data_dir().join("requirements-txt");
|
||||
let requirements_txt = working_dir.join(path);
|
||||
|
||||
let actual = RequirementsTxt::parse(requirements_txt, &working_dir, Connectivity::Offline)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let snapshot = format!("parse-windows-{}", path.to_string_lossy());
|
||||
let pattern = regex::escape(
|
||||
&working_dir
|
||||
.simplified_display()
|
||||
.to_string()
|
||||
.replace('\\', "/"),
|
||||
);
|
||||
let filters = vec![(pattern.as_str(), "[WORKSPACE_DIR]")];
|
||||
insta::with_settings!({
|
||||
filters => filters
|
||||
}, {
|
||||
insta::assert_debug_snapshot!(snapshot, actual);
|
||||
});
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn invalid_include_missing_file() -> Result<()> {
|
||||
let temp_dir = assert_fs::TempDir::new()?;
|
||||
|
@ -1566,14 +1608,16 @@ mod test {
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"flask",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"flask",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -5,140 +5,152 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.24.2",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.24.2",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"pandas",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.0.0",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"pandas",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.0.0",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"python-dateutil",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.8.2",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"python-dateutil",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.8.2",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"pytz",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2023.3",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"pytz",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2023.3",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"six",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.16.0",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"six",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.16.0",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"tzdata",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2023.3",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"tzdata",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2023.3",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -5,25 +5,27 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"django-debug-toolbar",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: LessThan,
|
||||
version: "2.2",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"django-debug-toolbar",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: LessThan,
|
||||
version: "2.2",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -5,48 +5,52 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"django",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.1.15",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"django",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.1.15",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"pytz",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2023.3",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"pytz",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2023.3",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -5,53 +5,57 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"pandas",
|
||||
),
|
||||
extras: [
|
||||
ExtraName(
|
||||
"tabulate",
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"pandas",
|
||||
),
|
||||
],
|
||||
version_or_url: Some(
|
||||
Url(
|
||||
VerbatimUrl {
|
||||
url: Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"github.com",
|
||||
extras: [
|
||||
ExtraName(
|
||||
"tabulate",
|
||||
),
|
||||
],
|
||||
version_or_url: Some(
|
||||
Url(
|
||||
VerbatimUrl {
|
||||
url: Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"github.com",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/pandas-dev/pandas",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
given: Some(
|
||||
"https://github.com/pandas-dev/pandas",
|
||||
),
|
||||
port: None,
|
||||
path: "/pandas-dev/pandas",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
given: Some(
|
||||
"https://github.com/pandas-dev/pandas",
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -5,91 +5,99 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"inflection",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "0.5.1",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"inflection",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "0.5.1",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"upsidedown",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "0.4",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"upsidedown",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "0.4",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"pandas",
|
||||
),
|
||||
extras: [
|
||||
ExtraName(
|
||||
"tabulate",
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"pandas",
|
||||
),
|
||||
],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: GreaterThanEqual,
|
||||
version: "1",
|
||||
},
|
||||
VersionSpecifier {
|
||||
operator: LessThan,
|
||||
version: "2",
|
||||
},
|
||||
],
|
||||
extras: [
|
||||
ExtraName(
|
||||
"tabulate",
|
||||
),
|
||||
],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: GreaterThanEqual,
|
||||
version: "1",
|
||||
},
|
||||
VersionSpecifier {
|
||||
operator: LessThan,
|
||||
version: "2",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -5,37 +5,41 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"tomli",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"tomli",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.24.2",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.24.2",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -5,14 +5,16 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"tomli",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"tomli",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -5,219 +5,227 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"werkzeug",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"werkzeug",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.2.3",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.2.3",
|
||||
},
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4.0",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4.0",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
hashes: [
|
||||
"sha256:2e1ccc9417d4da358b9de6f174e3ac094391ea1d4fbef2d667865d819dfd0afe",
|
||||
],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"urllib3",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"urllib3",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.26.15",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.26.15",
|
||||
},
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
hashes: [
|
||||
"sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305",
|
||||
],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"ansicon",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"ansicon",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.89.0",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.89.0",
|
||||
},
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvString(
|
||||
PlatformSystem,
|
||||
),
|
||||
operator: Equal,
|
||||
r_value: QuotedString(
|
||||
"Windows",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvString(
|
||||
PlatformSystem,
|
||||
),
|
||||
operator: Equal,
|
||||
r_value: QuotedString(
|
||||
"Windows",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
hashes: [
|
||||
"sha256:e4d039def5768a47e4afec8e89e83ec3ae5a26bf00ad851f914d1240b444d2b1",
|
||||
],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"requests-oauthlib",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"requests-oauthlib",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.3.1",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.3.1",
|
||||
},
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4.0",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4.0",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
hashes: [
|
||||
"sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5",
|
||||
"sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a",
|
||||
|
@ -225,52 +233,54 @@ RequirementsTxt {
|
|||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"psycopg2",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"psycopg2",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.9.5",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.9.5",
|
||||
},
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4.0",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4.0",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
hashes: [
|
||||
"sha256:093e3894d2d3c592ab0945d9eba9d139c139664dcf83a1c440b8a7aa9bb21955",
|
||||
"sha256:190d51e8c1b25a47484e52a79638a8182451d6f6dff99f26ad9bd81e5359a0fa",
|
||||
|
|
|
@ -5,48 +5,52 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"tqdm",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "4.65.0",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"tqdm",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "4.65.0",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"tomli-w",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.0.0",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"tomli-w",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.0.0",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -5,53 +5,57 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"pandas",
|
||||
),
|
||||
extras: [
|
||||
ExtraName(
|
||||
"tabulate",
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"pandas",
|
||||
),
|
||||
],
|
||||
version_or_url: Some(
|
||||
Url(
|
||||
VerbatimUrl {
|
||||
url: Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"github.com",
|
||||
extras: [
|
||||
ExtraName(
|
||||
"tabulate",
|
||||
),
|
||||
],
|
||||
version_or_url: Some(
|
||||
Url(
|
||||
VerbatimUrl {
|
||||
url: Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"github.com",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/pandas-dev/pandas",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
given: Some(
|
||||
"https://github.com/pandas-dev/pandas",
|
||||
),
|
||||
port: None,
|
||||
path: "/pandas-dev/pandas",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
given: Some(
|
||||
"https://github.com/pandas-dev/pandas",
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -5,140 +5,152 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.24.2",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.24.2",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"pandas",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.0.0",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"pandas",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.0.0",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"python-dateutil",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.8.2",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"python-dateutil",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.8.2",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"pytz",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2023.3",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"pytz",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2023.3",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"six",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.16.0",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"six",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.16.0",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"tzdata",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2023.3",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"tzdata",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2023.3",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -5,25 +5,27 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"django-debug-toolbar",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: LessThan,
|
||||
version: "2.2",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"django-debug-toolbar",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: LessThan,
|
||||
version: "2.2",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -5,48 +5,52 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"django",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.1.15",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"django",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.1.15",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"pytz",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2023.3",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"pytz",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2023.3",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -5,91 +5,99 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"inflection",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "0.5.1",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"inflection",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "0.5.1",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"upsidedown",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "0.4",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"upsidedown",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "0.4",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"pandas",
|
||||
),
|
||||
extras: [
|
||||
ExtraName(
|
||||
"tabulate",
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"pandas",
|
||||
),
|
||||
],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: GreaterThanEqual,
|
||||
version: "1",
|
||||
},
|
||||
VersionSpecifier {
|
||||
operator: LessThan,
|
||||
version: "2",
|
||||
},
|
||||
],
|
||||
extras: [
|
||||
ExtraName(
|
||||
"tabulate",
|
||||
),
|
||||
],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: GreaterThanEqual,
|
||||
version: "1",
|
||||
},
|
||||
VersionSpecifier {
|
||||
operator: LessThan,
|
||||
version: "2",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -5,37 +5,41 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"tomli",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"tomli",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.24.2",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.24.2",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -5,14 +5,16 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"tomli",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"tomli",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -5,219 +5,227 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"werkzeug",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"werkzeug",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.2.3",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.2.3",
|
||||
},
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4.0",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4.0",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
hashes: [
|
||||
"sha256:2e1ccc9417d4da358b9de6f174e3ac094391ea1d4fbef2d667865d819dfd0afe",
|
||||
],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"urllib3",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"urllib3",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.26.15",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.26.15",
|
||||
},
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
hashes: [
|
||||
"sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305",
|
||||
],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"ansicon",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"ansicon",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.89.0",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.89.0",
|
||||
},
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvString(
|
||||
PlatformSystem,
|
||||
),
|
||||
operator: Equal,
|
||||
r_value: QuotedString(
|
||||
"Windows",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvString(
|
||||
PlatformSystem,
|
||||
),
|
||||
operator: Equal,
|
||||
r_value: QuotedString(
|
||||
"Windows",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
hashes: [
|
||||
"sha256:e4d039def5768a47e4afec8e89e83ec3ae5a26bf00ad851f914d1240b444d2b1",
|
||||
],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"requests-oauthlib",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"requests-oauthlib",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.3.1",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.3.1",
|
||||
},
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4.0",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4.0",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
hashes: [
|
||||
"sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5",
|
||||
"sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a",
|
||||
|
@ -225,52 +233,54 @@ RequirementsTxt {
|
|||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"psycopg2",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"psycopg2",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.9.5",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "2.9.5",
|
||||
},
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4.0",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: Some(
|
||||
And(
|
||||
[
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: GreaterEqual,
|
||||
r_value: QuotedString(
|
||||
"3.8",
|
||||
),
|
||||
},
|
||||
),
|
||||
Expression(
|
||||
MarkerExpression {
|
||||
l_value: MarkerEnvVersion(
|
||||
PythonVersion,
|
||||
),
|
||||
operator: LessThan,
|
||||
r_value: QuotedString(
|
||||
"4.0",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
hashes: [
|
||||
"sha256:093e3894d2d3c592ab0945d9eba9d139c139664dcf83a1c440b8a7aa9bb21955",
|
||||
"sha256:190d51e8c1b25a47484e52a79638a8182451d6f6dff99f26ad9bd81e5359a0fa",
|
||||
|
|
|
@ -5,48 +5,52 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"tqdm",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "4.65.0",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"tqdm",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "4.65.0",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"tomli-w",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.0.0",
|
||||
},
|
||||
],
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"tomli-w",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: Some(
|
||||
VersionSpecifier(
|
||||
VersionSpecifiers(
|
||||
[
|
||||
VersionSpecifier {
|
||||
operator: Equal,
|
||||
version: "1.0.0",
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
---
|
||||
source: crates/requirements-txt/src/lib.rs
|
||||
expression: actual
|
||||
---
|
||||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Unnamed(
|
||||
UnnamedRequirement {
|
||||
url: VerbatimUrl {
|
||||
url: Url {
|
||||
scheme: "file",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: None,
|
||||
port: None,
|
||||
path: "[WORKSPACE_DIR]/scripts/editable-installs/black_editable",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
given: Some(
|
||||
"./scripts/editable-installs/black_editable",
|
||||
),
|
||||
},
|
||||
extras: [],
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Unnamed(
|
||||
UnnamedRequirement {
|
||||
url: VerbatimUrl {
|
||||
url: Url {
|
||||
scheme: "file",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: None,
|
||||
port: None,
|
||||
path: "[WORKSPACE_DIR]/scripts/editable-installs/black_editable",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
given: Some(
|
||||
"./scripts/editable-installs/black_editable",
|
||||
),
|
||||
},
|
||||
extras: [
|
||||
ExtraName(
|
||||
"dev",
|
||||
),
|
||||
],
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Unnamed(
|
||||
UnnamedRequirement {
|
||||
url: VerbatimUrl {
|
||||
url: Url {
|
||||
scheme: "file",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: None,
|
||||
port: None,
|
||||
path: "/scripts/editable-installs/black_editable",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
given: Some(
|
||||
"file:///scripts/editable-installs/black_editable",
|
||||
),
|
||||
},
|
||||
extras: [],
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
],
|
||||
constraints: [],
|
||||
editables: [],
|
||||
index_url: None,
|
||||
extra_index_urls: [],
|
||||
find_links: [],
|
||||
no_index: false,
|
||||
}
|
|
@ -5,53 +5,57 @@ expression: actual
|
|||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"numpy",
|
||||
),
|
||||
extras: [],
|
||||
version_or_url: None,
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Requirement {
|
||||
name: PackageName(
|
||||
"pandas",
|
||||
),
|
||||
extras: [
|
||||
ExtraName(
|
||||
"tabulate",
|
||||
requirement: Pep508(
|
||||
Requirement {
|
||||
name: PackageName(
|
||||
"pandas",
|
||||
),
|
||||
],
|
||||
version_or_url: Some(
|
||||
Url(
|
||||
VerbatimUrl {
|
||||
url: Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"github.com",
|
||||
extras: [
|
||||
ExtraName(
|
||||
"tabulate",
|
||||
),
|
||||
],
|
||||
version_or_url: Some(
|
||||
Url(
|
||||
VerbatimUrl {
|
||||
url: Url {
|
||||
scheme: "https",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: Some(
|
||||
Domain(
|
||||
"github.com",
|
||||
),
|
||||
),
|
||||
port: None,
|
||||
path: "/pandas-dev/pandas",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
given: Some(
|
||||
"https://github.com/pandas-dev/pandas",
|
||||
),
|
||||
port: None,
|
||||
path: "/pandas-dev/pandas",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
given: Some(
|
||||
"https://github.com/pandas-dev/pandas",
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
marker: None,
|
||||
},
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
---
|
||||
source: crates/requirements-txt/src/lib.rs
|
||||
expression: actual
|
||||
---
|
||||
RequirementsTxt {
|
||||
requirements: [
|
||||
RequirementEntry {
|
||||
requirement: Unnamed(
|
||||
UnnamedRequirement {
|
||||
url: VerbatimUrl {
|
||||
url: Url {
|
||||
scheme: "file",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: None,
|
||||
port: None,
|
||||
path: "/[WORKSPACE_DIR]/scripts/editable-installs/black_editable",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
given: Some(
|
||||
"./scripts/editable-installs/black_editable",
|
||||
),
|
||||
},
|
||||
extras: [],
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Unnamed(
|
||||
UnnamedRequirement {
|
||||
url: VerbatimUrl {
|
||||
url: Url {
|
||||
scheme: "file",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: None,
|
||||
port: None,
|
||||
path: "/[WORKSPACE_DIR]/scripts/editable-installs/black_editable",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
given: Some(
|
||||
"./scripts/editable-installs/black_editable",
|
||||
),
|
||||
},
|
||||
extras: [
|
||||
ExtraName(
|
||||
"dev",
|
||||
),
|
||||
],
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
RequirementEntry {
|
||||
requirement: Unnamed(
|
||||
UnnamedRequirement {
|
||||
url: VerbatimUrl {
|
||||
url: Url {
|
||||
scheme: "file",
|
||||
cannot_be_a_base: false,
|
||||
username: "",
|
||||
password: None,
|
||||
host: None,
|
||||
port: None,
|
||||
path: "/[WORKSPACE_DIR]/scripts/editable-installs/black_editable",
|
||||
query: None,
|
||||
fragment: None,
|
||||
},
|
||||
given: Some(
|
||||
"file:///scripts/editable-installs/black_editable",
|
||||
),
|
||||
},
|
||||
extras: [],
|
||||
marker: None,
|
||||
},
|
||||
),
|
||||
hashes: [],
|
||||
editable: false,
|
||||
},
|
||||
],
|
||||
constraints: [],
|
||||
editables: [],
|
||||
index_url: None,
|
||||
extra_index_urls: [],
|
||||
find_links: [],
|
||||
no_index: false,
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
./scripts/editable-installs/black_editable
|
||||
./scripts/editable-installs/black_editable[dev]
|
||||
file:///scripts/editable-installs/black_editable
|
1
crates/uv-resolver/src/bare.rs
Normal file
1
crates/uv-resolver/src/bare.rs
Normal file
|
@ -0,0 +1 @@
|
|||
|
|
@ -3,7 +3,7 @@ pub use error::ResolveError;
|
|||
pub use finder::{DistFinder, Reporter as FinderReporter};
|
||||
pub use manifest::Manifest;
|
||||
pub use options::{Options, OptionsBuilder};
|
||||
pub use preferences::Preference;
|
||||
pub use preferences::{Preference, PreferenceError};
|
||||
pub use prerelease_mode::PreReleaseMode;
|
||||
pub use python_requirement::PythonRequirement;
|
||||
pub use resolution::{AnnotationStyle, Diagnostic, DisplayResolutionGraph, ResolutionGraph};
|
||||
|
@ -15,6 +15,7 @@ pub use resolver::{
|
|||
};
|
||||
pub use version_map::VersionMap;
|
||||
|
||||
mod bare;
|
||||
mod candidate_selector;
|
||||
mod constraints;
|
||||
mod dependency_mode;
|
||||
|
|
|
@ -3,11 +3,21 @@ use std::str::FromStr;
|
|||
use rustc_hash::FxHashMap;
|
||||
|
||||
use pep440_rs::{Operator, Version};
|
||||
use pep508_rs::{MarkerEnvironment, Requirement, VersionOrUrl};
|
||||
use pep508_rs::{
|
||||
MarkerEnvironment, Requirement, RequirementsTxtRequirement, UnnamedRequirement, VersionOrUrl,
|
||||
};
|
||||
use pypi_types::{HashError, Hashes};
|
||||
use requirements_txt::RequirementEntry;
|
||||
use uv_normalize::PackageName;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum PreferenceError {
|
||||
#[error("direct URL requirements without package names are not supported: {0}")]
|
||||
Bare(UnnamedRequirement),
|
||||
#[error(transparent)]
|
||||
Hash(#[from] HashError),
|
||||
}
|
||||
|
||||
/// A pinned requirement, as extracted from a `requirements.txt` file.
|
||||
#[derive(Debug)]
|
||||
pub struct Preference {
|
||||
|
@ -17,9 +27,14 @@ pub struct Preference {
|
|||
|
||||
impl Preference {
|
||||
/// Create a [`Preference`] from a [`RequirementEntry`].
|
||||
pub fn from_entry(entry: RequirementEntry) -> Result<Self, HashError> {
|
||||
pub fn from_entry(entry: RequirementEntry) -> Result<Self, PreferenceError> {
|
||||
Ok(Self {
|
||||
requirement: entry.requirement,
|
||||
requirement: match entry.requirement {
|
||||
RequirementsTxtRequirement::Pep508(requirement) => requirement,
|
||||
RequirementsTxtRequirement::Unnamed(requirement) => {
|
||||
return Err(PreferenceError::Bare(requirement))
|
||||
}
|
||||
},
|
||||
hashes: entry
|
||||
.hashes
|
||||
.iter()
|
||||
|
|
|
@ -3,20 +3,19 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use console::Term;
|
||||
use indexmap::IndexMap;
|
||||
use rustc_hash::FxHashSet;
|
||||
use tracing::{instrument, Level};
|
||||
|
||||
use distribution_types::{FlatIndexLocation, IndexUrl};
|
||||
use pep508_rs::Requirement;
|
||||
use pypi_types::HashError;
|
||||
use pep508_rs::{Requirement, RequirementsTxtRequirement};
|
||||
use requirements_txt::{EditableRequirement, FindLink, RequirementsTxt};
|
||||
use uv_client::Connectivity;
|
||||
use uv_fs::Simplified;
|
||||
use uv_normalize::{ExtraName, PackageName};
|
||||
use uv_resolver::Preference;
|
||||
use uv_resolver::{Preference, PreferenceError};
|
||||
use uv_warnings::warn_user;
|
||||
|
||||
use crate::commands::Upgrade;
|
||||
|
@ -65,20 +64,6 @@ impl RequirementsSource {
|
|||
}
|
||||
}
|
||||
|
||||
// If the user provided a path to a local directory without `-e` (as in
|
||||
// `uv pip install ../flask`), prompt them to correct it.
|
||||
if (name.contains('/') || name.contains('\\')) && Path::new(&name).is_dir() {
|
||||
let term = Term::stderr();
|
||||
if term.is_term() {
|
||||
let prompt =
|
||||
format!("`{name}` looks like a local directory but was passed as a package name. Did you mean `-e {name}`?");
|
||||
let confirmation = confirm::confirm(&prompt, &term, true).unwrap();
|
||||
if confirmation {
|
||||
return Self::RequirementsTxt(name.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Self::Package(name)
|
||||
}
|
||||
}
|
||||
|
@ -187,8 +172,13 @@ impl RequirementsSpecification {
|
|||
requirements: requirements_txt
|
||||
.requirements
|
||||
.into_iter()
|
||||
.map(|entry| entry.requirement)
|
||||
.collect(),
|
||||
.map(|entry| match entry.requirement {
|
||||
RequirementsTxtRequirement::Pep508(requirement) => Ok(requirement),
|
||||
RequirementsTxtRequirement::Unnamed(requirement) => Err(anyhow!(
|
||||
"Unnamed URL requirements are not yet supported: {requirement}"
|
||||
)),
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?,
|
||||
constraints: requirements_txt.constraints,
|
||||
editables: requirements_txt.editables,
|
||||
overrides: vec![],
|
||||
|
@ -465,7 +455,7 @@ pub(crate) async fn read_lockfile(
|
|||
.into_iter()
|
||||
.filter(|entry| !entry.editable)
|
||||
.map(Preference::from_entry)
|
||||
.collect::<Result<Vec<_>, HashError>>()?;
|
||||
.collect::<Result<Vec<_>, PreferenceError>>()?;
|
||||
|
||||
// Apply the upgrade strategy to the requirements.
|
||||
Ok(match upgrade {
|
||||
|
|
|
@ -2474,6 +2474,55 @@ fn respect_http_env_var() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// A requirement defined as a single unnamed environment variable should be parsed as such.
|
||||
#[test]
|
||||
fn respect_unnamed_env_var() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let requirements_in = context.temp_dir.child("requirements.in");
|
||||
requirements_in.write_str("${URL}")?;
|
||||
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in")
|
||||
.env("URL", "https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl"), @r###"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: Unnamed URL requirements are not yet supported: https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A requirement defined as a single unnamed environment variable should error if the environment
|
||||
/// variable is not set.
|
||||
#[test]
|
||||
fn error_missing_unnamed_env_var() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let requirements_in = context.temp_dir.child("requirements.in");
|
||||
requirements_in.write_str("${URL}")?;
|
||||
|
||||
uv_snapshot!(context.compile()
|
||||
.arg("requirements.in"), @r###"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: Couldn't parse requirement in `requirements.in` at position 0
|
||||
Caused by: Expected package name starting with an alphanumeric character, found '$'
|
||||
${URL}
|
||||
^
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resolve a dependency from a file path, passing in the entire path as an environment variable.
|
||||
#[test]
|
||||
fn respect_file_env_var() -> Result<()> {
|
||||
|
@ -3409,10 +3458,7 @@ fn missing_package_name() -> Result<()> {
|
|||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: Unsupported requirement in requirements.in at position 0
|
||||
Caused by: URL requirement must be preceded by a package name. Add the name of the package before the URL (e.g., `package_name @ https://...`).
|
||||
https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
error: Unnamed URL requirements are not yet supported: https://files.pythonhosted.org/packages/36/42/015c23096649b908c809c69388a805a571a3bea44362fe87e33fc3afa01f/flask-3.0.0-py3-none-any.whl
|
||||
"###
|
||||
);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue