From c8477991a96da2a2ee5aa4c8e5d1dc3ffd0f6886 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Fri, 6 Oct 2023 20:16:44 -0400 Subject: [PATCH] Use local versions of PEP 440 and PEP 508 crates (#32) This PR modifies the PEP 440 and PEP 508 crates to pass CI, primarily by fixing all lint violations. We're also now using these crates in the workspace via `path`. (Previously, we were still fetching them from Cargo.) --- Cargo.lock | 154 ++++- Cargo.toml | 3 +- crates/pep440-rs/Cargo.toml | 12 +- crates/pep440-rs/pyproject.toml | 15 - crates/pep440-rs/src/version.rs | 107 ++-- crates/pep440-rs/src/version_specifier.rs | 80 ++- .../pep508-rs/.github/workflows/release.yml | 177 ------ crates/pep508-rs/.github/workflows/test.yml | 38 -- crates/pep508-rs/.gitignore | 6 - crates/pep508-rs/Cargo.toml | 40 +- crates/pep508-rs/poetry.lock | 580 ------------------ crates/pep508-rs/pyproject.toml | 32 - crates/pep508-rs/src/lib.rs | 89 ++- crates/pep508-rs/src/marker.rs | 142 ++--- crates/pep508-rs/src/modern.rs | 362 ----------- crates/pep508-rs/test/test_pep508.py | 97 --- crates/puffin-cli/Cargo.toml | 4 +- crates/puffin-interpreter/Cargo.toml | 4 +- crates/puffin-package/Cargo.toml | 4 +- crates/puffin-platform/Cargo.toml | 2 +- crates/puffin-resolver/Cargo.toml | 4 +- crates/puffin-resolver/src/lib.rs | 3 +- 22 files changed, 359 insertions(+), 1596 deletions(-) delete mode 100644 crates/pep440-rs/pyproject.toml delete mode 100644 crates/pep508-rs/.github/workflows/release.yml delete mode 100644 crates/pep508-rs/.github/workflows/test.yml delete mode 100644 crates/pep508-rs/.gitignore delete mode 100644 crates/pep508-rs/poetry.lock delete mode 100644 crates/pep508-rs/pyproject.toml delete mode 100644 crates/pep508-rs/src/modern.rs delete mode 100644 crates/pep508-rs/test/test_pep508.py diff --git a/Cargo.lock b/Cargo.lock index 1de1d858c..9574b7ead 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,6 +127,12 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + [[package]] name = "async-compression" version = "0.4.3" @@ -149,7 +155,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -392,7 +398,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -773,7 +779,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -1077,6 +1083,18 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "indoc" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" + +[[package]] +name = "indoc" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" + [[package]] name = "insta" version = "1.33.0" @@ -1292,7 +1310,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -1422,7 +1440,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -1483,25 +1501,30 @@ dependencies = [ [[package]] name = "pep440_rs" version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "887f66cc62717ea72caac4f1eb4e6f392224da3ffff3f40ec13ab427802746d6" dependencies = [ - "lazy_static", + "indoc 2.0.4", + "once_cell", + "pyo3", "regex", "serde", + "tracing", "unicode-width", ] [[package]] name = "pep508_rs" version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4516b53d9ea6112ebb38b4af08d5707d30b994fb7f98ff133c5dcf7ed8fa854" dependencies = [ + "indoc 2.0.4", + "log", "once_cell", "pep440_rs", + "pyo3", + "pyo3-log", "regex", "serde", + "serde_json", + "testing_logger", "thiserror", "tracing", "unicode-width", @@ -1734,6 +1757,77 @@ dependencies = [ "tracing", ] +[[package]] +name = "pyo3" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e681a6cfdc4adcc93b4d3cf993749a4552018ee0a9b65fc0ccfad74352c72a38" +dependencies = [ + "cfg-if", + "indoc 1.0.9", + "libc", + "memoffset", + "parking_lot", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076c73d0bc438f7a4ef6fdd0c3bb4732149136abd952b110ac93e4edb13a6ba5" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e53cee42e77ebe256066ba8aa77eff722b3bb91f3419177cf4cd0f304d3284d9" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-log" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47b0777feb17f61eea78667d61103758b243a871edc09a7786500a50467b605" +dependencies = [ + "arc-swap", + "log", + "pyo3", +] + +[[package]] +name = "pyo3-macros" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfeb4c99597e136528c6dd7d5e3de5434d1ceaf487436a3f03b2d56b6fc9efd1" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "947dc12175c254889edc0c02e399476c2f652b4b9ebd123aa655c224de259536" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quick-xml" version = "0.29.0" @@ -2069,7 +2163,7 @@ checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -2112,7 +2206,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -2257,6 +2351,17 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.38" @@ -2317,6 +2422,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "testing_logger" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d92b727cb45d33ae956f7f46b966b25f1bc712092aeef9dba5ac798fc89f720" +dependencies = [ + "log", +] + [[package]] name = "thiserror" version = "1.0.49" @@ -2334,7 +2448,7 @@ checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -2425,7 +2539,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -2491,7 +2605,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -2593,6 +2707,12 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +[[package]] +name = "unindent" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" + [[package]] name = "url" version = "2.4.1" @@ -2675,7 +2795,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.38", "wasm-bindgen-shared", ] @@ -2709,7 +2829,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/Cargo.toml b/Cargo.toml index 3f3410d2c..881f247df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,8 +25,6 @@ install-wheel-rs = { version = "0.0.1" } mailparse = { version = "0.14.0" } memchr = { version = "2.6.4" } once_cell = { version = "1.18.0" } -pep440_rs = { version = "0.3.12" } -pep508_rs = { version = "0.2.3" } platform-info = { version = "2.0.2" } plist = { version = "1.5.0" } regex = { version = "1.9.6" } @@ -44,4 +42,5 @@ tokio-util = { version = "0.7.9", features = ["compat"] } tracing = { version = "0.1.37" } tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } tracing-tree = { version = "0.2.5" } +unicode-width = { version = "0.1.8" } url = { version = "2.4.1" } diff --git a/crates/pep440-rs/Cargo.toml b/crates/pep440-rs/Cargo.toml index bf6297ab8..6ad668f0b 100644 --- a/crates/pep440-rs/Cargo.toml +++ b/crates/pep440-rs/Cargo.toml @@ -4,7 +4,6 @@ version = "0.3.12" description = "A library for python version numbers and specifiers, implementing PEP 440" edition = "2021" include = ["/src", "Changelog.md", "License-Apache", "License-BSD", "Readme.md", "pyproject.toml"] -# Same license as pypa/packaging where the tests are from license = "Apache-2.0 OR BSD-2-Clause" repository = "https://github.com/konstin/pep440-rs" readme = "Readme.md" @@ -14,12 +13,13 @@ name = "pep440_rs" crate-type = ["rlib", "cdylib"] [dependencies] -lazy_static = "1.4.0" +once_cell = { workspace = true } +regex = { workspace = true } +serde = { workspace = true, features = ["derive"], optional = true } +tracing = { workspace = true, optional = true } +unicode-width = { workspace = true } + pyo3 = { version = "0.19", optional = true, features = ["extension-module", "abi3-py37"] } -regex = { version = "1.8.1", default-features = false, features = ["std", "perf", "unicode-case", "unicode-perl"] } -serde = { version = "1.0.162", features = ["derive"], optional = true } -tracing = { version = "0.1.37", optional = true } -unicode-width = "0.1.10" [dev-dependencies] indoc = "2.0.1" diff --git a/crates/pep440-rs/pyproject.toml b/crates/pep440-rs/pyproject.toml deleted file mode 100644 index 681bccef1..000000000 --- a/crates/pep440-rs/pyproject.toml +++ /dev/null @@ -1,15 +0,0 @@ -[project] -name = "pep440_rs" -readme = "python/Readme.md" - -[build-system] -requires = ["maturin>=1.0.0,<2.0.0"] -build-backend = "maturin" - -[tool.maturin] -features = ["pyo3"] -python-source = "python" -module-name = "pep440_rs._pep440_rs" - -[tool.ruff.per-file-ignores] -"python/pep440_rs/__init__.py" = ["F403", "F405"] diff --git a/crates/pep440-rs/src/version.rs b/crates/pep440-rs/src/version.rs index 93f4b569a..0c083e501 100644 --- a/crates/pep440-rs/src/version.rs +++ b/crates/pep440-rs/src/version.rs @@ -1,4 +1,4 @@ -use lazy_static::lazy_static; +use once_cell::sync::Lazy; #[cfg(feature = "pyo3")] use pyo3::{ basic::CompareOp, exceptions::PyValueError, pyclass, pymethods, FromPyObject, IntoPy, PyAny, @@ -53,12 +53,9 @@ pub(crate) const VERSION_RE_INNER: &str = r" (?P\.\*)? # allow for version matching `.*` "; -lazy_static! { - /// Matches a python version, such as `1.19.a1`. Based on the PEP 440 regex - static ref VERSION_RE: Regex = Regex::new(&format!( - r#"(?xi)^(?:\s*){}(?:\s*)$"#, VERSION_RE_INNER - )).unwrap(); -} +/// Matches a python version, such as `1.19.a1`. Based on the PEP 440 regex +static VERSION_RE: Lazy = + Lazy::new(|| Regex::new(&format!(r#"(?xi)^(?:\s*){VERSION_RE_INNER}(?:\s*)$"#)).unwrap()); /// One of `~=` `==` `!=` `<=` `>=` `<` `>` `===` #[derive(Eq, PartialEq, Debug, Hash, Clone)] @@ -115,8 +112,7 @@ impl FromStr for Operator { // Should be forbidden by the regex if called from normal parsing other => { return Err(format!( - "No such comparison operator '{}', must be one of ~= == != <= >= < > ===", - other + "No such comparison operator '{other}', must be one of ~= == != <= >= < > ===", )); } }; @@ -125,7 +121,7 @@ impl FromStr for Operator { } impl Display for Operator { - /// Note the EqualStar is also `==` + /// Note the `EqualStar` is also `==`. fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let operator = match self { Operator::Equal => "==", @@ -142,7 +138,7 @@ impl Display for Operator { Operator::GreaterThanEqual => ">=", }; - write!(f, "{}", operator) + write!(f, "{operator}") } } @@ -181,8 +177,7 @@ impl FromStr for PreRelease { "b" | "beta" => Ok(Self::Beta), "c" | "rc" | "pre" | "preview" => Ok(Self::Rc), _ => Err(format!( - "'{}' isn't recognized as alpha, beta or release candidate", - prerelease + "'{prerelease}' isn't recognized as alpha, beta or release candidate", )), } } @@ -212,7 +207,7 @@ impl Display for PreRelease { /// > shorter local version’s segments match the beginning of the longer local version’s segments /// > exactly. /// -/// Luckily the default Ord impl for Vec matches the PEP 440 rules +/// Luckily the default `Ord` implementation for `Vec` matches the PEP 440 rules. #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum LocalSegment { /// Not-parseable as integer segment of local version @@ -224,8 +219,8 @@ pub enum LocalSegment { impl Display for LocalSegment { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - Self::String(string) => write!(f, "{}", string), - Self::Number(number) => write!(f, "{}", number), + Self::String(string) => write!(f, "{string}"), + Self::Number(number) => write!(f, "{number}"), } } } @@ -254,9 +249,9 @@ impl FromStr for LocalSegment { /// /// Beware that the sorting implemented with [Ord] and [Eq] is not consistent with the operators /// from PEP 440, i.e. compare two versions in rust with `>` gives a different result than a -/// VersionSpecifier with `>` as operator. +/// `VersionSpecifier` with `>` as operator. /// -/// Parse with [Version::from_str]: +/// Parse with [`Version::from_str`]: /// /// ```rust /// use std::str::FromStr; @@ -360,25 +355,25 @@ impl PyVersion { #[getter] #[allow(clippy::get_first)] pub fn major(&self) -> usize { - self.release().get(0).cloned().unwrap_or_default() + self.release().get(0).copied().unwrap_or_default() } /// The second item of release or 0 if unavailable. #[getter] pub fn minor(&self) -> usize { - self.release().get(1).cloned().unwrap_or_default() + self.release().get(1).copied().unwrap_or_default() } /// The third item of release or 0 if unavailable. #[getter] pub fn micro(&self) -> usize { - self.release().get(2).cloned().unwrap_or_default() + self.release().get(2).copied().unwrap_or_default() } /// Parses a PEP 440 version string #[cfg(feature = "pyo3")] #[new] - pub fn parse(version: String) -> PyResult { + pub fn parse(version: &str) -> PyResult { Ok(Self( - Version::from_str(&version).map_err(PyValueError::new_err)?, + Version::from_str(version).map_err(PyValueError::new_err)?, )) } @@ -386,8 +381,8 @@ impl PyVersion { /// Parse a PEP 440 version optionally ending with `.*` #[cfg(feature = "pyo3")] #[staticmethod] - pub fn parse_star(version_specifier: String) -> PyResult<(Self, bool)> { - Version::from_str_star(&version_specifier) + pub fn parse_star(version_specifier: &str) -> PyResult<(Self, bool)> { + Version::from_str_star(version_specifier) .map_err(PyValueError::new_err) .map(|(version, star)| (Self(version), star)) } @@ -422,7 +417,7 @@ impl PyVersion { } } -/// https://github.com/serde-rs/serde/issues/1316#issue-332908452 +/// #[cfg(feature = "serde")] impl<'de> Deserialize<'de> for Version { fn deserialize(deserializer: D) -> Result @@ -434,7 +429,7 @@ impl<'de> Deserialize<'de> for Version { } } -/// https://github.com/serde-rs/serde/issues/1316#issue-332908452 +/// #[cfg(feature = "serde")] impl Serialize for Version { fn serialize(&self, serializer: S) -> Result @@ -500,29 +495,26 @@ impl Version { impl Display for Version { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let epoch = if self.epoch == 0 { - "".to_string() + String::new() } else { format!("{}!", self.epoch) }; let release = self .release .iter() - .map(|x| x.to_string()) + .map(ToString::to_string) .collect::>() .join("."); let pre = self .pre .as_ref() - .map(|(pre_kind, pre_version)| format!("{}{}", pre_kind, pre_version)) + .map(|(pre_kind, pre_version)| format!("{pre_kind}{pre_version}")) .unwrap_or_default(); let post = self .post - .map(|post| format!(".post{}", post)) - .unwrap_or_default(); - let dev = self - .dev - .map(|dev| format!(".dev{}", dev)) + .map(|post| format!(".post{post}")) .unwrap_or_default(); + let dev = self.dev.map(|dev| format!(".dev{dev}")).unwrap_or_default(); let local = self .local .as_ref() @@ -531,19 +523,19 @@ impl Display for Version { "+{}", segments .iter() - .map(|x| x.to_string()) + .map(std::string::ToString::to_string) .collect::>() .join(".") ) }) .unwrap_or_default(); - write!(f, "{}{}{}{}{}{}", epoch, release, pre, post, dev, local) + write!(f, "{epoch}{release}{pre}{post}{dev}{local}") } } /// Compare the release parts of two versions, e.g. `4.3.1` > `4.2`, `1.1.0` == `1.1` and /// `1.16` < `1.19` -pub fn compare_release(this: &[usize], other: &[usize]) -> Ordering { +pub(crate) fn compare_release(this: &[usize], other: &[usize]) -> Ordering { // "When comparing release segments with different numbers of components, the shorter segment // is padded out with additional zeros as necessary" let iterator: Vec<(&usize, &usize)> = if this.len() < other.len() { @@ -632,7 +624,7 @@ impl PartialEq for Version { impl Eq for Version {} impl Hash for Version { - /// Custom implementation to ignoring trailing zero because PartialEq zero pads + /// Custom implementation to ignoring trailing zero because `PartialEq` zero pads fn hash(&self, state: &mut H) { self.epoch.hash(state); // Skip trailing zeros @@ -693,11 +685,11 @@ impl FromStr for Version { /// Parses a version such as `1.19`, `1.0a1`,`1.0+abc.5` or `1!2012.2` /// /// Note that this variant doesn't allow the version to end with a star, see - /// [Self::from_str_star] if you want to parse versions for specifiers + /// [`Self::from_str_star`] if you want to parse versions for specifiers fn from_str(version: &str) -> Result { let captures = VERSION_RE .captures(version) - .ok_or_else(|| format!("Version `{}` doesn't match PEP 440 rules", version))?; + .ok_or_else(|| format!("Version `{version}` doesn't match PEP 440 rules"))?; let (version, star) = Version::parse_impl(&captures)?; if star { return Err("A star (`*`) must not be used in a fixed version (use `Version::from_string_star` otherwise)".to_string()); @@ -707,7 +699,7 @@ impl FromStr for Version { } impl Version { - /// Like [Self::from_str], but also allows the version to end with a star and returns whether it + /// Like [`Self::from_str`], but also allows the version to end with a star and returns whether it /// did. This variant is for use in specifiers. /// * `1.2.3` -> false /// * `1.2.3.*` -> true @@ -716,7 +708,7 @@ impl Version { pub fn from_str_star(version: &str) -> Result<(Self, bool), String> { let captures = VERSION_RE .captures(version) - .ok_or_else(|| format!("Version `{}` doesn't match PEP 440 rules", version))?; + .ok_or_else(|| format!("Version `{version}` doesn't match PEP 440 rules"))?; let (version, star) = Version::parse_impl(&captures)?; Ok((version, star)) } @@ -895,7 +887,7 @@ mod test { ]; for version in versions { Version::from_str(version).unwrap(); - VersionSpecifier::from_str(&format!("=={}", version)).unwrap(); + VersionSpecifier::from_str(&format!("=={version}")).unwrap(); } } @@ -915,14 +907,11 @@ mod test { for version in versions { assert_eq!( Version::from_str(version).unwrap_err(), - format!("Version `{}` doesn't match PEP 440 rules", version) + format!("Version `{version}` doesn't match PEP 440 rules") ); assert_eq!( - VersionSpecifier::from_str(&format!("=={}", version)).unwrap_err(), - format!( - "Version specifier `=={}` doesn't match PEP 440 rules", - version - ) + VersionSpecifier::from_str(&format!("=={version}")).unwrap_err(), + format!("Version specifier `=={version}` doesn't match PEP 440 rules") ); } } @@ -1048,19 +1037,17 @@ mod test { let version = Version::from_str(version_str).unwrap(); let normalized = Version::from_str(normalized_str).unwrap(); // Just test version parsing again - assert_eq!(version, normalized, "{} {}", version_str, normalized_str); + assert_eq!(version, normalized, "{version_str} {normalized_str}"); // Test version normalization assert_eq!( version.to_string(), normalized.to_string(), - "{} {}", - version_str, - normalized_str + "{version_str} {normalized_str}" ); } } - /// https://github.com/pypa/packaging/blob/237ff3aa348486cf835a980592af3a59fccd6101/tests/test_version.py#L229-L277 + /// #[test] fn test_equality_and_normalization2() { let versions = [ @@ -1115,22 +1102,18 @@ mod test { for (version_str, normalized_str) in versions { let version = Version::from_str(version_str).unwrap(); let normalized = Version::from_str(normalized_str).unwrap(); - assert_eq!(version, normalized, "{} {}", version_str, normalized_str); + assert_eq!(version, normalized, "{version_str} {normalized_str}"); // Test version normalization assert_eq!( version.to_string(), normalized_str, - "{} {}", - version_str, - normalized_str + "{version_str} {normalized_str}" ); // Since we're already at it assert_eq!( version.to_string(), normalized.to_string(), - "{} {}", - version_str, - normalized_str + "{version_str} {normalized_str}" ); } } diff --git a/crates/pep440-rs/src/version_specifier.rs b/crates/pep440-rs/src/version_specifier.rs index 68eb72115..298a89c17 100644 --- a/crates/pep440-rs/src/version_specifier.rs +++ b/crates/pep440-rs/src/version_specifier.rs @@ -2,7 +2,7 @@ use crate::version::PyVersion; use crate::version::VERSION_RE_INNER; use crate::{version, Operator, Pep440Error, Version}; -use lazy_static::lazy_static; +use once_cell::sync::Lazy; #[cfg(feature = "pyo3")] use pyo3::{ exceptions::{PyIndexError, PyNotImplementedError, PyValueError}, @@ -27,14 +27,14 @@ use unicode_width::UnicodeWidthStr; #[cfg(feature = "tracing")] use tracing::warn; -lazy_static! { - /// Matches a python version specifier, such as `>=1.19.a1` or `4.1.*`. Extends the PEP 440 - /// version regex to version specifiers - static ref VERSION_SPECIFIER_RE: Regex = Regex::new(&format!( - r#"(?xi)^(?:\s*)(?P(~=|==|!=|<=|>=|<|>|===))(?:\s*){}(?:\s*)$"#, - VERSION_RE_INNER - )).unwrap(); -} +/// Matches a python version specifier, such as `>=1.19.a1` or `4.1.*`. Extends the PEP 440 +/// version regex to version specifiers +static VERSION_SPECIFIER_RE: Lazy = Lazy::new(|| { + Regex::new(&format!( + r#"(?xi)^(?:\s*)(?P(~=|==|!=|<=|>=|<|>|===))(?:\s*){VERSION_RE_INNER}(?:\s*)$"#, + )) + .unwrap() +}); /// A thin wrapper around `Vec` with a serde implementation /// @@ -91,10 +91,10 @@ impl Display for VersionSpecifiers { for (idx, version_specifier) in self.0.iter().enumerate() { // Separate version specifiers by comma, but we need one comma less than there are // specifiers - if idx != 0 { - write!(f, ", {}", version_specifier)?; + if idx == 0 { + write!(f, "{version_specifier}")?; } else { - write!(f, "{}", version_specifier)?; + write!(f, ", {version_specifier}")?; } } Ok(()) @@ -125,8 +125,8 @@ impl VersionSpecifiersIter { impl VersionSpecifiers { /// PEP 440 parsing #[new] - pub fn __new__(version_specifiers: String) -> PyResult { - Self::from_str(&version_specifiers).map_err(|err| PyValueError::new_err(err.to_string())) + pub fn __new__(version_specifiers: &str) -> PyResult { + Self::from_str(version_specifiers).map_err(|err| PyValueError::new_err(err.to_string())) } /// PEP 440 serialization @@ -150,6 +150,7 @@ impl VersionSpecifiers { }) } + #[allow(clippy::needless_pass_by_value)] fn __iter__(slf: PyRef<'_, Self>) -> PyResult> { let iter = VersionSpecifiersIter { inner: slf.0.clone().into_iter(), @@ -207,8 +208,8 @@ impl Serialize for VersionSpecifiers { /// let version_specifier = VersionSpecifier::from_str("== 1.*").unwrap(); /// assert!(version_specifier.contains(&version)); /// ``` -#[cfg_attr(feature = "pyo3", pyclass(get_all))] #[derive(Eq, PartialEq, Debug, Clone, Hash)] +#[cfg_attr(feature = "pyo3", pyclass(get_all))] pub struct VersionSpecifier { /// ~=|==|!=|<=|>=|<|>|===, plus whether the version ended with a star pub(crate) operator: Operator, @@ -222,8 +223,8 @@ impl VersionSpecifier { // Since we don't bring FromStr to python /// Parse a PEP 440 version #[new] - pub fn parse(version_specifier: String) -> PyResult { - Self::from_str(&version_specifier).map_err(PyValueError::new_err) + pub fn parse(version_specifier: &str) -> PyResult { + Self::from_str(version_specifier).map_err(PyValueError::new_err) } /// See [VersionSpecifier::contains] @@ -244,7 +245,7 @@ impl VersionSpecifier { /// Returns the normalized representation pub fn __repr__(&self) -> String { - format!(r#""#, self) + format!(r#""#) } fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult { @@ -265,7 +266,7 @@ impl VersionSpecifier { } } -/// https://github.com/serde-rs/serde/issues/1316#issue-332908452 +/// #[cfg(feature = "serde")] impl<'de> Deserialize<'de> for VersionSpecifier { fn deserialize(deserializer: D) -> Result @@ -277,7 +278,7 @@ impl<'de> Deserialize<'de> for VersionSpecifier { } } -/// https://github.com/serde-rs/serde/issues/1316#issue-332908452 +/// #[cfg(feature = "serde")] impl Serialize for VersionSpecifier { fn serialize(&self, serializer: S) -> Result @@ -309,7 +310,7 @@ impl VersionSpecifier { operator, local .iter() - .map(|x| x.to_string()) + .map(std::string::ToString::to_string) .collect::>() .join(".") )); @@ -323,8 +324,7 @@ impl VersionSpecifier { Operator::NotEqual => Operator::NotEqualStar, other => { return Err(format!( - "Operator {} must not be used in version ending with a star", - other + "Operator {other} must not be used in version ending with a star" )) } } @@ -489,7 +489,7 @@ impl FromStr for VersionSpecifier { fn from_str(spec: &str) -> Result { let captures = VERSION_SPECIFIER_RE .captures(spec) - .ok_or_else(|| format!("Version specifier `{}` doesn't match PEP 440 rules", spec))?; + .ok_or_else(|| format!("Version specifier `{spec}` doesn't match PEP 440 rules"))?; let (version, star) = Version::parse_impl(&captures)?; // operator but we don't know yet if it has a star let operator = Operator::from_str(&captures["operator"])?; @@ -509,7 +509,7 @@ impl Display for VersionSpecifier { /// Parses a list of specifiers such as `>= 1.0, != 1.3.*, < 2.0`. /// -/// I recommend using [VersionSpecifiers::from_str] instead. +/// I recommend using [`VersionSpecifiers::from_str`] instead. /// /// ```rust /// use std::str::FromStr; @@ -625,8 +625,8 @@ mod test { "1!1.2.rev33+123456", ]; - /// https://github.com/pypa/packaging/blob/237ff3aa348486cf835a980592af3a59fccd6101/tests/test_version.py#L666-L707 - /// https://github.com/pypa/packaging/blob/237ff3aa348486cf835a980592af3a59fccd6101/tests/test_version.py#L709-L750 + /// + /// /// /// These tests are a lot shorter than the pypa/packaging version since we implement all /// comparisons through one method @@ -667,7 +667,7 @@ mod test { .collect(); for (a, b, ordering) in operations { - assert_eq!(a.cmp(b), ordering, "{} {:?} {}", a, ordering, b); + assert_eq!(a.cmp(b), ordering, "{a} {ordering:?} {b}"); } } @@ -792,7 +792,7 @@ mod test { /// Test for tilde equal (~=) and star equal (== x.y.*) recorded from pypa/packaging /// - /// Well, except for https://github.com/pypa/packaging/issues/617 + /// Well, except for #[test] fn test_operators_other() { let versions: Vec = VERSIONS_0 @@ -809,9 +809,10 @@ mod test { .iter() .map(|specifier| specifier.contains(version)) .collect::>(); - for ((actual, expected), specifier) in actual.iter().zip(expected).zip(SPECIFIERS_OTHER) + for ((actual, expected), _specifier) in + actual.iter().zip(expected).zip(SPECIFIERS_OTHER) { - assert_eq!(actual, expected, "{} {}", version, specifier); + assert_eq!(actual, expected); } } } @@ -924,9 +925,7 @@ mod test { VersionSpecifier::from_str(specifier) .unwrap() .contains(&Version::from_str(version).unwrap()), - "{} {}", - version, - specifier + "{version} {specifier}" ); } } @@ -1027,9 +1026,7 @@ mod test { !VersionSpecifier::from_str(specifier) .unwrap() .contains(&Version::from_str(version).unwrap()), - "{} {}", - version, - specifier + "{version} {specifier}" ); } } @@ -1246,15 +1243,12 @@ mod test { ]; for (specifier, error) in specifiers { if let Some(error) = error { - assert_eq!(VersionSpecifier::from_str(specifier).unwrap_err(), error) + assert_eq!(VersionSpecifier::from_str(specifier).unwrap_err(), error); } else { assert_eq!( VersionSpecifier::from_str(specifier).unwrap_err(), - format!( - "Version specifier `{}` doesn't match PEP 440 rules", - specifier - ) - ) + format!("Version specifier `{specifier}` doesn't match PEP 440 rules",) + ); } } } diff --git a/crates/pep508-rs/.github/workflows/release.yml b/crates/pep508-rs/.github/workflows/release.yml deleted file mode 100644 index bbff151d7..000000000 --- a/crates/pep508-rs/.github/workflows/release.yml +++ /dev/null @@ -1,177 +0,0 @@ -name: Release - -on: - push: - tags: - - v* - - -jobs: - crates-io: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - - name: Push to crates.io - env: - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} - run: cargo publish - - macos: - runs-on: macos-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: 3.9 - architecture: x64 - - uses: dtolnay/rust-toolchain@stable - - name: Build wheels - x86_64 - uses: PyO3/maturin-action@v1 - with: - target: x86_64 - args: --release --strip --out dist --sdist - env: - RUSTFLAGS: "-C link-arg=-undefined -C link-arg=dynamic_lookup" - - name: Build wheels - universal2 - uses: PyO3/maturin-action@v1 - with: - args: --release --strip --target --target universal2-apple-darwin --out dist - env: - RUSTFLAGS: "-C link-arg=-undefined -C link-arg=dynamic_lookup" - - name: Upload wheels - uses: actions/upload-artifact@v3 - with: - name: wheels - path: dist - - windows: - runs-on: windows-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: 3.9 - - uses: dtolnay/rust-toolchain@stable - - name: Build wheels - uses: PyO3/maturin-action@v1 - with: - args: --release --strip --out dist - - name: Upload wheels - uses: actions/upload-artifact@v3 - with: - name: wheels - path: dist - - linux: - runs-on: ubuntu-latest - strategy: - matrix: - target: [ x86_64 ] - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: 3.9 - architecture: x64 - - name: Build wheels - uses: PyO3/maturin-action@v1 - with: - target: ${{ matrix.target }} - args: --release --strip --out dist - - name: Upload wheels - uses: actions/upload-artifact@v3 - with: - name: wheels - path: dist - - linux-cross: - runs-on: ubuntu-latest - strategy: - matrix: - target: [ aarch64, armv7, s390x, ppc64le, ppc64 ] - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: 3.9 - - name: Build wheels - uses: PyO3/maturin-action@v1 - with: - rust-toolchain: nightly - target: ${{ matrix.target }} - args: --release --strip --out dist - - name: Upload wheels - uses: actions/upload-artifact@v3 - with: - name: wheels - path: dist - - musllinux: - runs-on: ubuntu-latest - strategy: - matrix: - target: - - x86_64-unknown-linux-musl - - i686-unknown-linux-musl - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: 3.9 - architecture: x64 - - name: Build wheels - uses: PyO3/maturin-action@v1 - with: - target: ${{ matrix.target }} - manylinux: musllinux_1_2 - args: --release --strip --out dist - - name: Upload wheels - uses: actions/upload-artifact@v3 - with: - name: wheels - path: dist - - musllinux-cross: - runs-on: ubuntu-latest - strategy: - matrix: - platform: - - target: aarch64-unknown-linux-musl - arch: aarch64 - - target: armv7-unknown-linux-musleabihf - arch: armv7 - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: 3.9 - - name: Build wheels - uses: PyO3/maturin-action@v1 - with: - rust-toolchain: nightly - target: ${{ matrix.platform.target }} - manylinux: musllinux_1_2 - args: --release --strip --out dist - - name: Upload wheels - uses: actions/upload-artifact@v3 - with: - name: wheels - path: dist - - release: - name: Release - runs-on: ubuntu-latest - needs: [ macos, windows, linux, linux-cross, musllinux, musllinux-cross ] - environment: release - permissions: - id-token: write - steps: - - uses: actions/download-artifact@v3 - with: - name: wheels - - name: Publish to PyPI - uses: PyO3/maturin-action@v1 - with: - command: upload - args: --skip-existing * diff --git a/crates/pep508-rs/.github/workflows/test.yml b/crates/pep508-rs/.github/workflows/test.yml deleted file mode 100644 index 817e676cc..000000000 --- a/crates/pep508-rs/.github/workflows/test.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: test - -on: [ push, pull_request ] - -env: - CARGO_TERM_COLOR: always - -jobs: - test: - strategy: - matrix: - os: [ ubuntu-latest, windows-latest, macos-latest ] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v3 - - run: rustup toolchain install stable --profile minimal - - uses: Swatinem/rust-cache@v2 - - name: Run tests - run: cargo test - - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - with: - components: clippy, rustfmt - - uses: Swatinem/rust-cache@v2 - - name: Ruff - run: pipx run ruff check --format github . - - name: black - run: pipx run black --check . - - name: Rustfmt - run: cargo fmt --all -- --check - - name: Clippy (pure rust) - run: cargo clippy --tests -- -D warnings - - name: Clippy (pyo3) - run: cargo clippy --tests --all-features -- -D warnings diff --git a/crates/pep508-rs/.gitignore b/crates/pep508-rs/.gitignore deleted file mode 100644 index 41721ef44..000000000 --- a/crates/pep508-rs/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.ipynb_checkpoints -.venv -/pypi_analysis/pipy_requires_dist.ndjson -/pypi_analysis/pypi_all.ndjson -__pycache__ -target \ No newline at end of file diff --git a/crates/pep508-rs/Cargo.toml b/crates/pep508-rs/Cargo.toml index f24820fe7..b636fcc5f 100644 --- a/crates/pep508-rs/Cargo.toml +++ b/crates/pep508-rs/Cargo.toml @@ -4,25 +4,28 @@ version = "0.2.3" description = "A library for python dependency specifiers, better known as PEP 508" edition = "2021" include = ["/src", "Changelog.md", "License-Apache", "License-BSD", "Readme.md", "pyproject.toml"] -# Same license as pypa/packaging where the tests are from license = "Apache-2.0 OR BSD-2-Clause" readme = "Readme.md" repository = "https://github.com/konstin/pep508_rs" +[lib] +name = "pep508_rs" +crate-type = ["cdylib", "rlib"] + [dependencies] -anyhow = { version = "1.0.75", optional = true } -once_cell = "1.18.0" -pep440_rs = "0.3.11" +pep440_rs = { path = "../pep440-rs" } + +once_cell = { workspace = true } +regex = { workspace = true } +serde = { workspace = true, features = ["derive"], optional = true } +serde_json = { workspace = true, optional = true } +thiserror = { workspace = true } +tracing = { workspace = true, features = ["log"] } +unicode-width = { workspace = true } +url = { workspace = true, features = ["serde"] } + pyo3 = { version = "0.19.2", optional = true, features = ["abi3", "extension-module"] } pyo3-log = { version = "0.8.3", optional = true } -regex = { version = "1.9.5", default-features = false, features = ["std"] } -serde = { version = "1.0.188", features = ["derive"], optional = true } -serde_json = { version = "1.0.107", optional = true } -thiserror = "1.0.49" -toml = { version = "0.8.1", optional = true } -tracing = { version = "0.1.37", features = ["log"] } -unicode-width = "0.1.11" -url = { version = "2.4.1", features = ["serde"] } [dev-dependencies] indoc = "2.0.4" @@ -33,17 +36,4 @@ serde_json = "1.0.107" [features] pyo3 = ["dep:pyo3", "pep440_rs/pyo3", "pyo3-log"] serde = ["dep:serde", "pep440_rs/serde"] -modern = ["serde", "toml", "anyhow"] default = [] - -[lib] -name = "pep508_rs" -crate-type = ["cdylib", "rlib"] - -[profile.release] -debug = true - -[profile.maturin] -inherits = "release" -strip = true - diff --git a/crates/pep508-rs/poetry.lock b/crates/pep508-rs/poetry.lock deleted file mode 100644 index 7248ef805..000000000 --- a/crates/pep508-rs/poetry.lock +++ /dev/null @@ -1,580 +0,0 @@ -# This file is automatically @generated by Poetry 1.5.0 and should not be changed by hand. - -[[package]] -name = "appnope" -version = "0.1.3" -description = "Disable App Nap on macOS >= 10.9" -optional = false -python-versions = "*" -files = [ - {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, - {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, -] - -[[package]] -name = "backcall" -version = "0.2.0" -description = "Specifications for callback functions passed in to an API" -optional = false -python-versions = "*" -files = [ - {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, - {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, -] - -[[package]] -name = "black" -version = "23.3.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.7" -files = [ - {file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"}, - {file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"}, - {file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"}, - {file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"}, - {file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"}, - {file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"}, - {file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"}, - {file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"}, - {file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"}, - {file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"}, - {file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"}, - {file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"}, - {file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"}, - {file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"}, - {file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"}, - {file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"}, -] - -[package.dependencies] -click = ">=8.0.0" -ipython = {version = ">=7.8.0", optional = true, markers = "extra == \"jupyter\""} -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tokenize-rt = {version = ">=3.2.0", optional = true, markers = "extra == \"jupyter\""} -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} -typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "click" -version = "8.1.3" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "decorator" -version = "5.1.1" -description = "Decorators for Humans" -optional = false -python-versions = ">=3.5" -files = [ - {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, - {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.1.1" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, - {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "importlib-metadata" -version = "6.6.0" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"}, - {file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"}, -] - -[package.dependencies] -typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "ipython" -version = "7.34.0" -description = "IPython: Productive Interactive Computing" -optional = false -python-versions = ">=3.7" -files = [ - {file = "ipython-7.34.0-py3-none-any.whl", hash = "sha256:c175d2440a1caff76116eb719d40538fbb316e214eda85c5515c303aacbfb23e"}, - {file = "ipython-7.34.0.tar.gz", hash = "sha256:af3bdb46aa292bce5615b1b2ebc76c2080c5f77f54bda2ec72461317273e7cd6"}, -] - -[package.dependencies] -appnope = {version = "*", markers = "sys_platform == \"darwin\""} -backcall = "*" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -decorator = "*" -jedi = ">=0.16" -matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} -pickleshare = "*" -prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" -pygments = "*" -setuptools = ">=18.5" -traitlets = ">=4.2" - -[package.extras] -all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"] -doc = ["Sphinx (>=1.3)"] -kernel = ["ipykernel"] -nbconvert = ["nbconvert"] -nbformat = ["nbformat"] -notebook = ["ipywidgets", "notebook"] -parallel = ["ipyparallel"] -qtconsole = ["qtconsole"] -test = ["ipykernel", "nbformat", "nose (>=0.10.1)", "numpy (>=1.17)", "pygments", "requests", "testpath"] - -[[package]] -name = "jedi" -version = "0.18.2" -description = "An autocompletion tool for Python that can be used for text editors." -optional = false -python-versions = ">=3.6" -files = [ - {file = "jedi-0.18.2-py2.py3-none-any.whl", hash = "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e"}, - {file = "jedi-0.18.2.tar.gz", hash = "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612"}, -] - -[package.dependencies] -parso = ">=0.8.0,<0.9.0" - -[package.extras] -docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] - -[[package]] -name = "matplotlib-inline" -version = "0.1.6" -description = "Inline Matplotlib backend for Jupyter" -optional = false -python-versions = ">=3.5" -files = [ - {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, - {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, -] - -[package.dependencies] -traitlets = "*" - -[[package]] -name = "maturin" -version = "1.0.1" -description = "Build and publish crates with pyo3, rust-cpython and cffi bindings as well as rust binaries as python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "maturin-1.0.1-py3-none-linux_armv6l.whl", hash = "sha256:10097e2602330c0b9db16d7dfd002476f5e5cf99df58ba2f3abc6de64a69e9a6"}, - {file = "maturin-1.0.1-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:9ecebccb111c9c870fb2f5eee17518fe106f676227bb16f204a51e7a162aceec"}, - {file = "maturin-1.0.1-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:b39f9a42b3c8242e3f3ab990bd03ba989c6c07e4de9e21fcf877a2418119d445"}, - {file = "maturin-1.0.1-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:c0b1efa47f8b7d15bc5945159764ce57316f9d1bfb7c8caa07cebdd41318359b"}, - {file = "maturin-1.0.1-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:d392ec0578d9e6f03914837cef7bbb264d5708807e0b48176b6ff0b50083ba7c"}, - {file = "maturin-1.0.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:d271b24febbfc020561984b1acdfc39b132df21f4e42d7af0fe274ea738c8000"}, - {file = "maturin-1.0.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:8d88d1595d7514c27df96d5f4fe3dc5f24288528a746439403f27c3b448fca16"}, - {file = "maturin-1.0.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:04c0279dd0d6ccd317018bd1a43f52cbda715822537ae1a68015c9171f18b2fd"}, - {file = "maturin-1.0.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:787bb56c80eda482ece2dd4788d479dbd0e74d981b2e2c538228365c19290fb7"}, - {file = "maturin-1.0.1-py3-none-win32.whl", hash = "sha256:6d9b4ff7c2d501e91886b859296f5c0478fc08bc7d537a72f98a69d51ff4f519"}, - {file = "maturin-1.0.1-py3-none-win_amd64.whl", hash = "sha256:2907b345186a83db4bbe5571830509b3031784d08958b32d2ffa7857bd473725"}, - {file = "maturin-1.0.1-py3-none-win_arm64.whl", hash = "sha256:6b020b9abbd1e9fef468c171216dc4be053834b5bf638075264ee090a993b0b0"}, - {file = "maturin-1.0.1.tar.gz", hash = "sha256:71fdb2dbbd5bcc60bd91ddcbe34dba9f04cc53c2add089a95a79d0d8fc8337b8"}, -] - -[package.dependencies] -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} - -[package.extras] -patchelf = ["patchelf"] -zig = ["ziglang (>=0.10.0,<0.11.0)"] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "packaging" -version = "23.1" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, -] - -[[package]] -name = "parso" -version = "0.8.3" -description = "A Python Parser" -optional = false -python-versions = ">=3.6" -files = [ - {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, - {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, -] - -[package.extras] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["docopt", "pytest (<6.0.0)"] - -[[package]] -name = "pathspec" -version = "0.11.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.7" -files = [ - {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, - {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, -] - -[[package]] -name = "pexpect" -version = "4.8.0" -description = "Pexpect allows easy control of interactive console applications." -optional = false -python-versions = "*" -files = [ - {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, - {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, -] - -[package.dependencies] -ptyprocess = ">=0.5" - -[[package]] -name = "pickleshare" -version = "0.7.5" -description = "Tiny 'shelve'-like database with concurrency support" -optional = false -python-versions = "*" -files = [ - {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, - {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, -] - -[[package]] -name = "platformdirs" -version = "3.5.1" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -optional = false -python-versions = ">=3.7" -files = [ - {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, - {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.5", markers = "python_version < \"3.8\""} - -[package.extras] -docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] - -[[package]] -name = "pluggy" -version = "1.0.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] - -[package.dependencies] -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "prompt-toolkit" -version = "3.0.38" -description = "Library for building powerful interactive command lines in Python" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "prompt_toolkit-3.0.38-py3-none-any.whl", hash = "sha256:45ea77a2f7c60418850331366c81cf6b5b9cf4c7fd34616f733c5427e6abbb1f"}, - {file = "prompt_toolkit-3.0.38.tar.gz", hash = "sha256:23ac5d50538a9a38c8bde05fecb47d0b403ecd0662857a86f886f798563d5b9b"}, -] - -[package.dependencies] -wcwidth = "*" - -[[package]] -name = "ptyprocess" -version = "0.7.0" -description = "Run a subprocess in a pseudo terminal" -optional = false -python-versions = "*" -files = [ - {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, - {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, -] - -[[package]] -name = "pygments" -version = "2.15.1" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.7" -files = [ - {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, - {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, -] - -[package.extras] -plugins = ["importlib-metadata"] - -[[package]] -name = "pytest" -version = "7.3.1" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, - {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} - -[package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] - -[[package]] -name = "ruff" -version = "0.0.270" -description = "An extremely fast Python linter, written in Rust." -optional = false -python-versions = ">=3.7" -files = [ - {file = "ruff-0.0.270-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:f74c4d550f7b8e808455ac77bbce38daafc458434815ba0bc21ae4bdb276509b"}, - {file = "ruff-0.0.270-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:643de865fd35cb76c4f0739aea5afe7b8e4d40d623df7e9e6ea99054e5cead0a"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eca02e709b3308eb7255b5f74e779be23b5980fca3862eae28bb23069cd61ae4"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3ed3b198768d2b3a2300fb18f730cd39948a5cc36ba29ae9d4639a11040880be"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:739495d2dbde87cf4e3110c8d27bc20febf93112539a968a4e02c26f0deccd1d"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:08188f8351f4c0b6216e8463df0a76eb57894ca59a3da65e4ed205db980fd3ae"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0827b074635d37984fc98d99316bfab5c8b1231bb83e60dacc83bd92883eedb4"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d61ae4841313f6eeb8292dc349bef27b4ce426e62c36e80ceedc3824e408734"}, - {file = "ruff-0.0.270-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0eb412f20e77529a01fb94d578b19dcb8331b56f93632aa0cce4a2ea27b7aeba"}, - {file = "ruff-0.0.270-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b775e2c5fc869359daf8c8b8aa0fd67240201ab2e8d536d14a0edf279af18786"}, - {file = "ruff-0.0.270-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:21f00e47ab2308617c44435c8dfd9e2e03897461c9e647ec942deb2a235b4cfd"}, - {file = "ruff-0.0.270-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0bbfbf6fd2436165566ca85f6e57be03ed2f0a994faf40180cfbb3604c9232ef"}, - {file = "ruff-0.0.270-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8af391ef81f7be960be10886a3c1aac0b298bde7cb9a86ec2b05faeb2081ce6b"}, - {file = "ruff-0.0.270-py3-none-win32.whl", hash = "sha256:b4c037fe2f75bcd9aed0c89c7c507cb7fa59abae2bd4c8b6fc331a28178655a4"}, - {file = "ruff-0.0.270-py3-none-win_amd64.whl", hash = "sha256:0012f9b7dc137ab7f1f0355e3c4ca49b562baf6c9fa1180948deeb6648c52957"}, - {file = "ruff-0.0.270-py3-none-win_arm64.whl", hash = "sha256:9613456b0b375766244c25045e353bc8890c856431cd97893c97b10cc93bd28d"}, - {file = "ruff-0.0.270.tar.gz", hash = "sha256:95db07b7850b30ebf32b27fe98bc39e0ab99db3985edbbf0754d399eb2f0e690"}, -] - -[[package]] -name = "setuptools" -version = "67.8.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "setuptools-67.8.0-py3-none-any.whl", hash = "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f"}, - {file = "setuptools-67.8.0.tar.gz", hash = "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "tokenize-rt" -version = "5.0.0" -description = "A wrapper around the stdlib `tokenize` which roundtrips." -optional = false -python-versions = ">=3.7" -files = [ - {file = "tokenize_rt-5.0.0-py2.py3-none-any.whl", hash = "sha256:c67772c662c6b3dc65edf66808577968fb10badfc2042e3027196bed4daf9e5a"}, - {file = "tokenize_rt-5.0.0.tar.gz", hash = "sha256:3160bc0c3e8491312d0485171dea861fc160a240f5f5766b72a1165408d10740"}, -] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "traitlets" -version = "5.9.0" -description = "Traitlets Python configuration system" -optional = false -python-versions = ">=3.7" -files = [ - {file = "traitlets-5.9.0-py3-none-any.whl", hash = "sha256:9e6ec080259b9a5940c797d58b613b5e31441c2257b87c2e795c5228ae80d2d8"}, - {file = "traitlets-5.9.0.tar.gz", hash = "sha256:f6cde21a9c68cf756af02035f72d5a723bf607e862e7be33ece505abf4a3bad9"}, -] - -[package.extras] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] - -[[package]] -name = "typed-ast" -version = "1.5.4" -description = "a fork of Python 2 and 3 ast modules with type comment support" -optional = false -python-versions = ">=3.6" -files = [ - {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, - {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, - {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, - {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, - {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, - {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, - {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, - {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, - {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, - {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, -] - -[[package]] -name = "typing-extensions" -version = "4.6.2" -description = "Backported and Experimental Type Hints for Python 3.7+" -optional = false -python-versions = ">=3.7" -files = [ - {file = "typing_extensions-4.6.2-py3-none-any.whl", hash = "sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98"}, - {file = "typing_extensions-4.6.2.tar.gz", hash = "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c"}, -] - -[[package]] -name = "wcwidth" -version = "0.2.6" -description = "Measures the displayed width of unicode strings in a terminal" -optional = false -python-versions = "*" -files = [ - {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, - {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, -] - -[[package]] -name = "zipp" -version = "3.15.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.7" -files = [ - {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, - {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - -[metadata] -lock-version = "2.0" -python-versions = ">=3.7" -content-hash = "f64653737d40fcf3c7b9f9fea186e98322fea9e7ee1889b8f8ee181570b26e17" diff --git a/crates/pep508-rs/pyproject.toml b/crates/pep508-rs/pyproject.toml deleted file mode 100644 index ec9ee608b..000000000 --- a/crates/pep508-rs/pyproject.toml +++ /dev/null @@ -1,32 +0,0 @@ -[project] -name = "pep508_rs" -version = "0.2.2" -description = "A library for python dependency specifiers, better known as PEP 508" -readme = "Readme.md" - -[tool.poetry] -name = "pep508_rs" -version = "0.1.1" -description = "" -authors = ["konstin "] -readme = "Readme.md" - -[tool.poetry.dependencies] -python = ">=3.7" - -[tool.poetry.group.dev.dependencies] -black = { extras = ["jupyter"], version = "^23.1.0" } -maturin = "^1.0.0" -pytest = "^7.2.0" -ruff = "^0.0.270" - -[tool.maturin] -features = ["pyo3"] - -[tool.pytest.ini_options] -minversion = "7.2.0" -addopts = "--tb=short" - -[build-system] -requires = ["maturin>=1.0,<2.0"] -build-backend = "maturin" diff --git a/crates/pep508-rs/src/lib.rs b/crates/pep508-rs/src/lib.rs index d1d50e5cd..fdf025910 100644 --- a/crates/pep508-rs/src/lib.rs +++ b/crates/pep508-rs/src/lib.rs @@ -16,8 +16,6 @@ #![deny(missing_docs)] mod marker; -#[cfg(feature = "modern")] -pub mod modern; pub use marker::{ MarkerEnvironment, MarkerExpression, MarkerOperator, MarkerTree, MarkerValue, @@ -125,8 +123,8 @@ create_exception!( ); /// A PEP 508 dependency specification -#[cfg_attr(feature = "pyo3", pyclass(module = "pep508"))] #[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"` @@ -159,18 +157,18 @@ impl Display for Requirement { } VersionOrUrl::Url(url) => { // We add the space for markers later if necessary - write!(f, " @ {}", url)?; + write!(f, " @ {url}")?; } } } if let Some(marker) = &self.marker { - write!(f, " ; {}", marker)?; + write!(f, " ; {marker}")?; } Ok(()) } } -/// https://github.com/serde-rs/serde/issues/908#issuecomment-298027413 +/// #[cfg(feature = "serde")] impl<'de> Deserialize<'de> for Requirement { fn deserialize(deserializer: D) -> Result @@ -182,7 +180,7 @@ impl<'de> Deserialize<'de> for Requirement { } } -/// https://github.com/serde-rs/serde/issues/1316#issue-332908452 +/// #[cfg(feature = "serde")] impl Serialize for Requirement { fn serialize(&self, serializer: S) -> Result @@ -214,7 +212,7 @@ impl Requirement { /// `requests [security,tests] >= 2.8.1, == 2.8.* ; python_version > "3.8"` #[getter] pub fn marker(&self) -> Option { - self.marker.as_ref().map(|m| m.to_string()) + self.marker.as_ref().map(std::string::ToString::to_string) } /// Parses a PEP 440 string @@ -241,7 +239,7 @@ impl Requirement { } fn __repr__(&self) -> String { - format!(r#""{}""#, self) + format!(r#""{self}""#) } fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult { @@ -263,9 +261,10 @@ impl Requirement { } /// Returns whether the markers apply for the given environment + #[allow(clippy::needless_pass_by_value)] #[pyo3(name = "evaluate_markers")] pub fn py_evaluate_markers(&self, env: &MarkerEnvironment, extras: Vec) -> bool { - self.evaluate_markers(env, extras) + self.evaluate_markers(env, &extras) } /// Returns whether the requirement would be satisfied, independent of environment markers, i.e. @@ -274,6 +273,7 @@ impl Requirement { /// Note that unlike [Self::evaluate_markers] this does not perform any checks for bogus /// expressions but will simply return true. As caller you should separately perform a check /// with an environment and forward all warnings. + #[allow(clippy::needless_pass_by_value)] #[pyo3(name = "evaluate_extras_and_python_version")] pub fn py_evaluate_extras_and_python_version( &self, @@ -281,28 +281,29 @@ impl Requirement { python_versions: Vec, ) -> bool { self.evaluate_extras_and_python_version( - extras, - python_versions + &extras, + &python_versions .into_iter() .map(|py_version| py_version.0) - .collect(), + .collect::>(), ) } /// Returns whether the markers apply for the given environment + #[allow(clippy::needless_pass_by_value)] #[pyo3(name = "evaluate_markers_and_report")] pub fn py_evaluate_markers_and_report( &self, env: &MarkerEnvironment, extras: Vec, ) -> (bool, Vec<(MarkerWarningKind, String, String)>) { - self.evaluate_markers_and_report(env, extras) + self.evaluate_markers_and_report(env, &extras) } } impl Requirement { /// Returns whether the markers apply for the given environment - pub fn evaluate_markers(&self, env: &MarkerEnvironment, extras: Vec) -> bool { + pub fn evaluate_markers(&self, env: &MarkerEnvironment, extras: &[String]) -> bool { if let Some(marker) = &self.marker { marker.evaluate( env, @@ -316,16 +317,16 @@ impl Requirement { /// Returns whether the requirement would be satisfied, independent of environment markers, i.e. /// if there is potentially an environment that could activate this requirement. /// - /// Note that unlike [Self::evaluate_markers] this does not perform any checks for bogus + /// Note that unlike [`Self::evaluate_markers`] this does not perform any checks for bogus /// expressions but will simply return true. As caller you should separately perform a check /// with an environment and forward all warnings. pub fn evaluate_extras_and_python_version( &self, - extras: HashSet, - python_versions: Vec, + extras: &HashSet, + python_versions: &[Version], ) -> bool { if let Some(marker) = &self.marker { - marker.evaluate_extras_and_python_version(&extras, &python_versions) + marker.evaluate_extras_and_python_version(extras, python_versions) } else { true } @@ -335,12 +336,15 @@ impl Requirement { pub fn evaluate_markers_and_report( &self, env: &MarkerEnvironment, - extras: Vec, + extras: &[String], ) -> (bool, Vec<(MarkerWarningKind, String, String)>) { if let Some(marker) = &self.marker { marker.evaluate_collect_warnings( env, - &extras.iter().map(|x| x.as_str()).collect::>(), + &extras + .iter() + .map(std::string::String::as_str) + .collect::>(), ) } else { (true, Vec::new()) @@ -448,11 +452,11 @@ impl<'a> CharIter<'a> { while let Some(char) = self.peek_char() { if !condition(char) { break; - } else { - substring.push(char); - self.next(); - len += 1; } + + substring.push(char); + self.next(); + len += 1; } (substring, start, len) } @@ -461,8 +465,7 @@ impl<'a> CharIter<'a> { match self.next() { None => Err(Pep508Error { message: Pep508ErrorSource::String(format!( - "Expected '{}', found end of dependency specification", - expected + "Expected '{expected}', found end of dependency specification" )), start: span_start, len: 1, @@ -471,8 +474,7 @@ impl<'a> CharIter<'a> { Some((_, value)) if value == expected => Ok(()), Some((pos, other)) => Err(Pep508Error { message: Pep508ErrorSource::String(format!( - "Expected '{}', found '{}'", - expected, other + "Expected '{expected}', found '{other}'" )), start: pos, len: 1, @@ -502,8 +504,7 @@ fn parse_name(chars: &mut CharIter) -> Result { } else { return Err(Pep508Error { message: Pep508ErrorSource::String(format!( - "Expected package name starting with an alphanumeric character, found '{}'", - char + "Expected package name starting with an alphanumeric character, found '{char}'" )), start: index, len: 1, @@ -528,8 +529,7 @@ fn parse_name(chars: &mut CharIter) -> Result { if chars.peek().is_none() && matches!(char, '.' | '-' | '_') { return Err(Pep508Error { message: Pep508ErrorSource::String(format!( - "Package name must end with an alphanumeric character, not '{}'", - char + "Package name must end with an alphanumeric character, not '{char}'" )), start: index, len: 1, @@ -544,9 +544,8 @@ fn parse_name(chars: &mut CharIter) -> Result { /// parses extras in the `[extra1,extra2] format` fn parse_extras(chars: &mut CharIter) -> Result>, Pep508Error> { - let bracket_pos = match chars.eat('[') { - Some(pos) => pos, - None => return Ok(None), + let Some(bracket_pos) = chars.eat('[') else { + return Ok(None); }; let mut extras = Vec::new(); @@ -568,14 +567,13 @@ fn parse_extras(chars: &mut CharIter) -> Result>, Pep508Error match chars.next() { // letterOrDigit Some((_, alphanumeric @ ('a'..='z' | 'A'..='Z' | '0'..='9'))) => { - buffer.push(alphanumeric) + buffer.push(alphanumeric); } Some((pos, other)) => { return Err(Pep508Error { message: Pep508ErrorSource::String(format!( - "Expected an alphanumeric character starting the extra name, found '{}'", - other - )), + "Expected an alphanumeric character starting the extra name, found '{other}'" + )), start: pos, len: 1, input: chars.copy_chars(), @@ -598,7 +596,7 @@ fn parse_extras(chars: &mut CharIter) -> Result>, Pep508Error Some((pos, char)) if char != ',' && char != ']' && !char.is_whitespace() => { return Err(Pep508Error { message: Pep508ErrorSource::String(format!( - "Invalid character in extras name, expected an alphanumeric character, '-', '_', '.', ',' or ']', found '{}'", char + "Invalid character in extras name, expected an alphanumeric character, '-', '_', '.', ',' or ']', found '{char}'" )), start: pos, len: 1, @@ -786,8 +784,7 @@ fn parse(chars: &mut CharIter) -> Result { Some(other) => { return Err(Pep508Error { message: Pep508ErrorSource::String(format!( - "Expected one of `@`, `(`, `<`, `=`, `>`, `~`, `!`, `;`, found `{}`", - other + "Expected one of `@`, `(`, `<`, `=`, `>`, `~`, `!`, `;`, found `{other}`" )), start: chars.get_pos(), len: 1, @@ -811,9 +808,9 @@ fn parse(chars: &mut CharIter) -> Result { if let Some((pos, char)) = chars.next() { return Err(Pep508Error { message: Pep508ErrorSource::String(if marker.is_none() { - format!(r#"Expected end of input or ';', found '{}'"#, char) + format!(r#"Expected end of input or ';', found '{char}'"#) } else { - format!(r#"Expected end of input, found '{}'"#, char) + format!(r#"Expected end of input, found '{char}'"#) }), start: pos, len: 1, @@ -854,7 +851,7 @@ pub fn python_module(py: Python<'_>, m: &PyModule) -> PyResult<()> { Ok(()) } -/// Half of these tests are copied from https://github.com/pypa/packaging/pull/624 +/// Half of these tests are copied from #[cfg(test)] mod tests { use crate::marker::{ diff --git a/crates/pep508-rs/src/marker.rs b/crates/pep508-rs/src/marker.rs index 423d16ced..2e73a34cc 100644 --- a/crates/pep508-rs/src/marker.rs +++ b/crates/pep508-rs/src/marker.rs @@ -24,8 +24,8 @@ use std::str::FromStr; use tracing::warn; /// Ways in which marker evaluation can fail -#[cfg_attr(feature = "pyo3", pyclass(module = "pep508"))] #[derive(Debug, Eq, Hash, Ord, PartialOrd, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "pyo3", pyclass(module = "pep508"))] pub enum MarkerWarningKind { /// Using an old name from PEP 345 instead of the modern equivalent /// @@ -46,12 +46,14 @@ pub enum MarkerWarningKind { #[cfg(feature = "pyo3")] #[pymethods] impl MarkerWarningKind { + #[allow(clippy::trivially_copy_pass_by_ref)] fn __hash__(&self) -> u8 { *self as u8 } - fn __richcmp__(&self, other: &Self, op: CompareOp) -> bool { - op.matches(self.cmp(other)) + #[allow(clippy::trivially_copy_pass_by_ref)] + fn __richcmp__(&self, other: Self, op: CompareOp) -> bool { + op.matches(self.cmp(&other)) } } @@ -84,15 +86,15 @@ pub enum MarkerValueString { ImplementationName, /// `os_name` OsName, - /// /// Deprecated `os.name` from https://peps.python.org/pep-0345/#environment-markers + /// Deprecated `os.name` from OsNameDeprecated, /// `platform_machine` PlatformMachine, - /// /// Deprecated `platform.machine` from https://peps.python.org/pep-0345/#environment-markers + /// Deprecated `platform.machine` from PlatformMachineDeprecated, /// `platform_python_implementation` PlatformPythonImplementation, - /// /// Deprecated `platform.python_implementation` from https://peps.python.org/pep-0345/#environment-markers + /// Deprecated `platform.python_implementation` from PlatformPythonImplementationDeprecated, /// `platform_release` PlatformRelease, @@ -100,11 +102,11 @@ pub enum MarkerValueString { PlatformSystem, /// `platform_version` PlatformVersion, - /// /// Deprecated `platform.version` from https://peps.python.org/pep-0345/#environment-markers + /// Deprecated `platform.version` from PlatformVersionDeprecated, /// `sys_platform` SysPlatform, - /// /// Deprecated `sys.platform` from https://peps.python.org/pep-0345/#environment-markers + /// Deprecated `sys.platform` from SysPlatformDeprecated, } @@ -184,7 +186,7 @@ impl FromStr for MarkerValue { "sys_platform" => Self::MarkerEnvString(MarkerValueString::SysPlatform), "sys.platform" => Self::MarkerEnvString(MarkerValueString::SysPlatformDeprecated), "extra" => Self::Extra, - _ => return Err(format!("Invalid key: {}", s)), + _ => return Err(format!("Invalid key: {s}")), }; Ok(value) } @@ -196,7 +198,7 @@ impl Display for MarkerValue { Self::MarkerEnvVersion(marker_value_version) => marker_value_version.fmt(f), Self::MarkerEnvString(marker_value_string) => marker_value_string.fmt(f), Self::Extra => f.write_str("extra"), - Self::QuotedString(value) => write!(f, "'{}'", value), + Self::QuotedString(value) => write!(f, "'{value}'"), } } } @@ -267,7 +269,7 @@ impl FromStr for MarkerOperator { { Self::NotIn } - other => return Err(format!("Invalid comparator: {}", other)), + other => return Err(format!("Invalid comparator: {other}")), }; Ok(value) } @@ -290,8 +292,8 @@ impl Display for MarkerOperator { } /// Helper type with a [Version] and its original text -#[cfg_attr(feature = "pyo3", pyclass(get_all, module = "pep508"))] #[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "pyo3", pyclass(get_all, module = "pep508"))] pub struct StringVersion { /// Original unchanged string pub string: String, @@ -344,7 +346,7 @@ impl Deref for StringVersion { /// /// /// Some are `(String, Version)` because we have to support version comparison -#[allow(missing_docs)] +#[allow(missing_docs, clippy::unsafe_derive_deserialize)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "pyo3", pyclass(get_all, module = "pep508"))] #[derive(Clone, Debug, Eq, Hash, PartialEq)] @@ -431,20 +433,17 @@ impl MarkerEnvironment { let implementation_version = StringVersion::from_str(implementation_version).map_err(|err| { PyValueError::new_err(format!( - "implementation_version is not a valid PEP440 version: {}", - err + "implementation_version is not a valid PEP440 version: {err}" )) })?; let python_full_version = StringVersion::from_str(python_full_version).map_err(|err| { PyValueError::new_err(format!( - "python_full_version is not a valid PEP440 version: {}", - err + "python_full_version is not a valid PEP440 version: {err}" )) })?; let python_version = StringVersion::from_str(python_version).map_err(|err| { PyValueError::new_err(format!( - "python_version is not a valid PEP440 version: {}", - err + "python_version is not a valid PEP440 version: {err}" )) })?; Ok(Self { @@ -483,10 +482,10 @@ impl MarkerEnvironment { info.getattr("major")?.extract::()?, info.getattr("minor")?.extract::()?, info.getattr("micro")?.extract::()?, - if kind != "final" { - format!("{}{}", kind, info.getattr("serial")?.extract::()?) + if kind == "final" { + String::new() } else { - "".to_string() + format!("{}{}", kind, info.getattr("serial")?.extract::()?) } ); let python_full_version: String = platform.getattr("python_version")?.call0()?.extract()?; @@ -496,20 +495,17 @@ impl MarkerEnvironment { let implementation_version = StringVersion::from_str(&implementation_version).map_err(|err| { PyValueError::new_err(format!( - "Broken python implementation, implementation_version is not a valid PEP440 version: {}", - err + "Broken python implementation, implementation_version is not a valid PEP440 version: {err}" )) })?; let python_full_version = StringVersion::from_str(&python_full_version).map_err(|err| { PyValueError::new_err(format!( - "Broken python implementation, python_full_version is not a valid PEP440 version: {}", - err + "Broken python implementation, python_full_version is not a valid PEP440 version: {err}" )) })?; let python_version = StringVersion::from_str(&python_version).map_err(|err| { PyValueError::new_err(format!( - "Broken python implementation, python_version is not a valid PEP440 version: {}", - err + "Broken python implementation, python_version is not a valid PEP440 version: {err}" )) })?; Ok(Self { @@ -546,7 +542,7 @@ pub struct MarkerExpression { } impl MarkerExpression { - /// Evaluate a expression + /// Evaluate a <`marker_value`> <`marker_op`> <`marker_value`> expression fn evaluate( &self, env: &MarkerEnvironment, @@ -592,7 +588,7 @@ impl MarkerExpression { Err(err) => { reporter( MarkerWarningKind::Pep440Error, - format!("Invalid operator/version combination: {}", err), + format!("Invalid operator/version combination: {err}"), self, ); return false; @@ -664,7 +660,7 @@ impl MarkerExpression { Err(err) => { reporter( MarkerWarningKind::Pep440Error, - format!("Invalid operator/version combination: {}", err), + format!("Invalid operator/version combination: {err}"), self, ); return false; @@ -685,8 +681,7 @@ impl MarkerExpression { // Not even pypa/packaging 22.0 supports this // https://github.com/pypa/packaging/issues/632 reporter(MarkerWarningKind::StringStringComparison, format!( - "Comparing two quoted strings with each other doesn't make sense: {}, evaluating to false", - self + "Comparing two quoted strings with each other doesn't make sense: {self}, evaluating to false" ), self); false } @@ -703,7 +698,7 @@ impl MarkerExpression { /// `python_version '...'` and /// `'...' python_version`. /// - /// Note that unlike [Self::evaluate] this does not perform any checks for bogus expressions but + /// Note that unlike [`Self::evaluate`] this does not perform any checks for bogus expressions but /// will simply return true. /// /// ```rust @@ -809,7 +804,7 @@ impl MarkerExpression { MarkerOperator::GreaterThan => { reporter( MarkerWarningKind::LexicographicComparison, - format!("Comparing {} and {} lexicographically", l_string, r_string), + format!("Comparing {l_string} and {r_string} lexicographically"), self, ); l_string > r_string @@ -817,7 +812,7 @@ impl MarkerExpression { MarkerOperator::GreaterEqual => { reporter( MarkerWarningKind::LexicographicComparison, - format!("Comparing {} and {} lexicographically", l_string, r_string), + format!("Comparing {l_string} and {r_string} lexicographically"), self, ); l_string >= r_string @@ -825,7 +820,7 @@ impl MarkerExpression { MarkerOperator::LessThan => { reporter( MarkerWarningKind::LexicographicComparison, - format!("Comparing {} and {} lexicographically", l_string, r_string), + format!("Comparing {l_string} and {r_string} lexicographically"), self, ); l_string < r_string @@ -833,7 +828,7 @@ impl MarkerExpression { MarkerOperator::LessEqual => { reporter( MarkerWarningKind::LexicographicComparison, - format!("Comparing {} and {} lexicographically", l_string, r_string), + format!("Comparing {l_string} and {r_string} lexicographically"), self, ); l_string <= r_string @@ -841,7 +836,7 @@ impl MarkerExpression { MarkerOperator::TildeEqual => { reporter( MarkerWarningKind::LexicographicComparison, - format!("Can't compare {} and {} with `~=`", l_string, r_string), + format!("Can't compare {l_string} and {r_string} with `~=`"), self, ); false @@ -880,8 +875,7 @@ impl FromStr for MarkerExpression { if let Some((pos, unexpected)) = chars.next() { return Err(Pep508Error { message: Pep508ErrorSource::String(format!( - "Unexpected character '{}', expected end of input", - unexpected + "Unexpected character '{unexpected}', expected end of input" )), start: pos, len: chars.chars.clone().count(), @@ -937,7 +931,7 @@ impl MarkerTree { } } - /// Same as [Self::evaluate], but instead of using logging to warn, you can pass your own + /// Same as [`Self::evaluate`], but instead of using logging to warn, you can pass your own /// handler for warnings pub fn evaluate_reporter( &self, @@ -971,7 +965,7 @@ impl MarkerTree { /// environment markers, i.e. if there is potentially an environment that could activate this /// requirement. /// - /// Note that unlike [Self::evaluate] this does not perform any checks for bogus expressions but + /// Note that unlike [`Self::evaluate`] this does not perform any checks for bogus expressions but /// will simply return true. As caller you should separately perform a check with an environment /// and forward all warnings. pub fn evaluate_extras_and_python_version( @@ -992,7 +986,7 @@ impl MarkerTree { } } - /// Same as [Self::evaluate], but instead of using logging to warn, you get a Vec with all + /// Same as [`Self::evaluate`], but instead of using logging to warn, you get a Vec with all /// warnings collected pub fn evaluate_collect_warnings( &self, @@ -1001,7 +995,7 @@ impl MarkerTree { ) -> (bool, Vec<(MarkerWarningKind, String, String)>) { let mut warnings = Vec::new(); let mut reporter = |kind, warning, marker: &MarkerExpression| { - warnings.push((kind, warning, marker.to_string())) + warnings.push((kind, warning, marker.to_string())); }; self.report_deprecated_options(&mut reporter); let result = self.evaluate_reporter_impl(env, extras, &mut reporter); @@ -1066,12 +1060,12 @@ impl MarkerTree { } MarkerTree::And(expressions) => { for expression in expressions { - expression.report_deprecated_options(reporter) + expression.report_deprecated_options(reporter); } } MarkerTree::Or(expressions) => { for expression in expressions { - expression.report_deprecated_options(reporter) + expression.report_deprecated_options(reporter); } } } @@ -1082,13 +1076,13 @@ impl Display for MarkerTree { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let format_inner = |expression: &MarkerTree| { if matches!(expression, MarkerTree::Expression(_)) { - format!("{}", expression) + format!("{expression}") } else { - format!("({})", expression) + format!("({expression})") } }; match self { - MarkerTree::Expression(expression) => write!(f, "{}", expression), + MarkerTree::Expression(expression) => write!(f, "{expression}"), MarkerTree::And(and_list) => f.write_str( &and_list .iter() @@ -1131,8 +1125,7 @@ fn parse_marker_operator(chars: &mut CharIter) -> Result { return Err(Pep508Error { message: Pep508ErrorSource::String(format!( - "Expected whitespace after 'not', found '{}'", - other + "Expected whitespace after 'not', found '{other}'" )), start: pos, len: 1, @@ -1147,8 +1140,7 @@ fn parse_marker_operator(chars: &mut CharIter) -> Result=' or 'not in'), found '{}'", - operator + "Expected a valid marker operator (such as '>=' or 'not in'), found '{operator}'" )), start, len, @@ -1156,10 +1148,10 @@ fn parse_marker_operator(chars: &mut CharIter) -> Result Result { // > User supplied constants are always encoded as strings with either ' or " quote marks. Note // > that backslash escapes are not defined, but existing implementations do support them. They @@ -1189,8 +1181,7 @@ fn parse_marker_value(chars: &mut CharIter) -> Result }); MarkerValue::from_str(&key).map_err(|_| Pep508Error { message: Pep508ErrorSource::String(format!( - "Expected a valid marker name, found '{}'", - key + "Expected a valid marker name, found '{key}'" )), start, len, @@ -1303,8 +1294,7 @@ pub(crate) fn parse_markers_impl(chars: &mut CharIter) -> Result - pub extras: Option>, - /// The list of markers . - /// Note that this will not accept extras. - /// - /// TODO: Deserialize into `MarkerTree` that does not accept the extras key - pub markers: Option, -} - -/// Instead of only PEP 440 specifier, you can also set a single version (exact) or TODO use -/// the semver caret -#[derive(Eq, PartialEq, Debug, Clone, Serialize)] -pub enum VersionSpecifierModern { - /// e.g. `4.12.1-beta.1` - Version(Version), - /// e.g. `== 4.12.1-beta.1` or `>=3.8,<4.0` - VersionSpecifier(VersionSpecifiers), -} - -impl VersionSpecifierModern { - /// `4.12.1-beta.1` -> `== 4.12.1-beta.1` - /// `== 4.12.1-beta.1` -> `== 4.12.1-beta.1` - /// `>=3.8,<4.0` -> `>=3.8,<4.0` - /// TODO: `^1.19` -> `>=1.19,<2.0` - pub fn to_pep508_specifier(&self) -> VersionSpecifiers { - match self { - // unwrapping is safe here because we're using Operator::Equal - VersionSpecifierModern::Version(version) => { - [VersionSpecifier::new(Operator::Equal, version.clone(), false).unwrap()] - .into_iter() - .collect() - } - VersionSpecifierModern::VersionSpecifier(version_specifiers) => { - version_specifiers.clone() - } - } - } -} - -impl FromStr for VersionSpecifierModern { - /// TODO: Modern needs it's own error type - type Err = Pep440Error; - - /// dispatching between just a version and a version specifier set - fn from_str(s: &str) -> Result { - // If it starts with - if s.trim_start().starts_with(|x: char| x.is_ascii_digit()) { - Ok(Self::Version(Version::from_str(s).map_err(|err| { - // TODO: Fix this in pep440_rs - Pep440Error { - message: err, - line: s.to_string(), - start: 0, - width: 1, - } - })?)) - } else if s.starts_with('^') { - todo!("TODO caret operator is not supported yet"); - } else { - Ok(Self::VersionSpecifier(VersionSpecifiers::from_str(s)?)) - } - } -} - -/// https://github.com/serde-rs/serde/issues/908#issuecomment-298027413 -impl<'de> Deserialize<'de> for VersionSpecifierModern { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - FromStr::from_str(&s).map_err(de::Error::custom) - } -} - -/// WIP Draft for a poetry/cargo like, modern dependency specification -#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize)] -#[serde(untagged)] -pub enum RequirementModern { - /// e.g. `numpy = "1.24.1"` - Dependency(VersionSpecifierModern), - /// e.g. `numpy = { version = "1.24.1" }` or `django-anymail = { version = "1.24.1", extras = ["sendgrid"], optional = true }` - LongDependency { - /// e.g. `1.2.3.beta1` - version: VersionSpecifierModern, - #[serde(flatten)] - #[allow(missing_docs)] - common: RequirementModernCommon, - }, - /// e.g. `tqdm = { git = "https://github.com/tqdm/tqdm", rev = "0bb91857eca0d4aea08f66cf1c8949abe0cd6b7a" }` - GitDependency { - /// URL of the git repository e.g. `https://github.com/tqdm/tqdm` - git: Url, - /// The git branch to use - branch: Option, - /// The git revision to use. Can be the short revision (`0bb9185`) or the long revision - /// (`0bb91857eca0d4aea08f66cf1c8949abe0cd6b7a`) - rev: Option, - #[serde(flatten)] - #[allow(missing_docs)] - common: RequirementModernCommon, - }, - /// e.g. `tqdm = { file = "tqdm-4.65.0-py3-none-any.whl" }` - FileDependency { - /// Path to a source distribution (e.g. `tqdm-4.65.0.tar.gz`) or wheel (e.g. `tqdm-4.65.0-py3-none-any.whl`) - file: String, - #[serde(flatten)] - #[allow(missing_docs)] - common: RequirementModernCommon, - }, - /// Path to a directory with source distributions and/or wheels e.g. - /// `scilib_core = { path = "build_wheels/scilib_core/" }`. - /// - /// Use this option if you e.g. have multiple platform platform dependent wheels or want to - /// have a fallback to a source distribution for you wheel. - PathDependency { - /// e.g. `dist/`, `target/wheels` or `vendored` - path: String, - #[serde(flatten)] - #[allow(missing_docs)] - common: RequirementModernCommon, - }, - /// e.g. `jax = { url = "https://storage.googleapis.com/jax-releases/cuda112/jaxlib-0.1.64+cuda112-cp39-none-manylinux2010_x86_64.whl" }` - UrlDependency { - /// URL to a source distribution or wheel. The file available there must be named - /// appropriately for a source distribution or a wheel. - url: Url, - #[serde(flatten)] - #[allow(missing_docs)] - common: RequirementModernCommon, - }, -} - -/// Adopted from the grammar at -static EXTRA_REGEX: Lazy = - Lazy::new(|| Regex::new(r"^[a-zA-Z0-9]([-_.]*[a-zA-Z0-9])*$").unwrap()); - -impl RequirementModern { - /// Check the things that serde doesn't check, namely that extra names are valid - pub fn check(&self) -> anyhow::Result<()> { - match self { - Self::LongDependency { common, .. } - | Self::GitDependency { common, .. } - | Self::FileDependency { common, .. } - | Self::PathDependency { common, .. } - | Self::UrlDependency { common, .. } => { - if let Some(extras) = &common.extras { - for extra in extras { - if !EXTRA_REGEX.is_match(extra) { - bail!("Not a valid extra name: '{}'", extra) - } - } - } - } - _ => {} - } - Ok(()) - } - - /// WIP Converts the modern format to PEP 508 - pub fn to_pep508( - &self, - name: &str, - extras: &HashMap>, - ) -> Result { - let default = RequirementModernCommon { - optional: false, - extras: None, - markers: None, - }; - - let common = match self { - RequirementModern::Dependency(..) => &default, - RequirementModern::LongDependency { common, .. } - | RequirementModern::GitDependency { common, .. } - | RequirementModern::FileDependency { common, .. } - | RequirementModern::PathDependency { common, .. } - | RequirementModern::UrlDependency { common, .. } => common, - }; - - let marker = if common.optional { - // invert the extras table from the modern format - // extra1 -> optional_dep1, optional_dep2, ... - // to the PEP 508 format - // optional_dep1; extra == "extra1" or extra == "extra2" - let dep_markers = extras - .iter() - .filter(|(_marker, dependencies)| dependencies.contains(&name.to_string())) - .map(|(marker, _dependencies)| { - MarkerTree::Expression(MarkerExpression { - l_value: MarkerValue::Extra, - operator: MarkerOperator::Equal, - r_value: QuotedString(marker.to_string()), - }) - }) - .collect(); - // any of these extras activates the dependency -> or clause - let dep_markers = MarkerTree::Or(dep_markers); - let joined_marker = if let Some(user_markers) = &common.markers { - let user_markers = MarkerTree::from_str(user_markers) - .context("TODO: parse this in serde already")?; - // but the dependency needs to be activated and match the other markers - // -> and clause - MarkerTree::And(vec![user_markers, dep_markers]) - } else { - dep_markers - }; - Some(joined_marker) - } else { - None - }; - - if let Some(extras) = &common.extras { - debug_assert!(extras.iter().all(|extra| EXTRA_REGEX.is_match(extra))); - } - - let version_or_url = match self { - RequirementModern::Dependency(version) => { - VersionOrUrl::VersionSpecifier(version.to_pep508_specifier()) - } - RequirementModern::LongDependency { version, .. } => { - VersionOrUrl::VersionSpecifier(version.to_pep508_specifier()) - } - RequirementModern::GitDependency { - git, branch, rev, .. - } => { - // TODO: Read https://peps.python.org/pep-0440/#direct-references properly - // set_scheme doesn't like us adding `git+` to https, therefore this hack - let mut url = - Url::parse(&format!("git+{}", git)).expect("TODO: Better url validation"); - match (branch, rev) { - (Some(_branch), Some(_rev)) => { - bail!("You can set both branch and rev (for {})", name) - } - (Some(branch), None) => url.set_path(&format!("{}@{}", url.path(), branch)), - (None, Some(rev)) => url.set_path(&format!("{}@{}", url.path(), rev)), - (None, None) => {} - } - - VersionOrUrl::Url(url) - } - RequirementModern::FileDependency { file, .. } => VersionOrUrl::Url( - Url::from_file_path(file) - .map_err(|()| format_err!("File must be absolute (for {})", name))?, - ), - RequirementModern::PathDependency { path, .. } => VersionOrUrl::Url( - Url::from_directory_path(path) - .map_err(|()| format_err!("Path must be absolute (for {})", name))?, - ), - RequirementModern::UrlDependency { url, .. } => VersionOrUrl::Url(url.clone()), - }; - - Ok(Requirement { - name: name.to_string(), - extras: common.extras.clone(), - version_or_url: Some(version_or_url), - marker, - }) - } -} - -#[cfg(test)] -mod test { - use crate::modern::{RequirementModern, VersionSpecifierModern}; - use crate::Requirement; - use indoc::indoc; - use pep440_rs::VersionSpecifiers; - use serde::Deserialize; - use std::collections::{BTreeMap, HashMap}; - - use std::str::FromStr; - - #[test] - fn test_basic() { - let deps: HashMap = - toml::from_str(r#"numpy = "==1.19""#).unwrap(); - assert_eq!( - deps["numpy"], - RequirementModern::Dependency(VersionSpecifierModern::VersionSpecifier( - VersionSpecifiers::from_str("==1.19").unwrap() - )) - ); - assert_eq!( - deps["numpy"].to_pep508("numpy", &HashMap::new()).unwrap(), - Requirement::from_str("numpy== 1.19").unwrap() - ); - } - - #[test] - fn test_conversion() { - #[derive(Deserialize)] - struct PyprojectToml { - // BTreeMap to keep the order - #[serde(rename = "modern-dependencies")] - modern_dependencies: BTreeMap, - extras: HashMap>, - } - - let pyproject_toml = indoc! {r#" - [modern-dependencies] - pydantic = "1.10.5" - numpy = ">=1.24.2, <2.0.0" - pandas = { version = ">=1.5.3, <2.0.0" } - flask = { version = "2.2.3 ", extras = ["dotenv"], optional = true } - tqdm = { git = "https://github.com/tqdm/tqdm", rev = "0bb91857eca0d4aea08f66cf1c8949abe0cd6b7a" } - jax = { url = "https://storage.googleapis.com/jax-releases/cuda112/jaxlib-0.1.64+cuda112-cp39-none-manylinux2010_x86_64.whl" } - zstandard = { file = "/home/ferris/wheels/zstandard/zstandard-0.20.0.tar.gz" } - h5py = { path = "/home/ferris/wheels/h5py/" } - - [extras] - internet = ["flask"] - "# - }; - - let deps: PyprojectToml = toml::from_str(pyproject_toml).unwrap(); - - let actual: Vec = deps - .modern_dependencies - .iter() - .map(|(name, spec)| spec.to_pep508(name, &deps.extras).unwrap().to_string()) - .collect(); - let expected: Vec = vec![ - "flask[dotenv] ==2.2.3 ; extra == 'internet'".to_string(), - "h5py @ file:///home/ferris/wheels/h5py/".to_string(), - "jax @ https://storage.googleapis.com/jax-releases/cuda112/jaxlib-0.1.64+cuda112-cp39-none-manylinux2010_x86_64.whl".to_string(), - "numpy >=1.24.2, <2.0.0".to_string(), - "pandas >=1.5.3, <2.0.0".to_string(), - "pydantic ==1.10.5".to_string(), - "tqdm @ git+https://github.com/tqdm/tqdm@0bb91857eca0d4aea08f66cf1c8949abe0cd6b7a".to_string(), - "zstandard @ file:///home/ferris/wheels/zstandard/zstandard-0.20.0.tar.gz".to_string() - ]; - assert_eq!(actual, expected) - } -} diff --git a/crates/pep508-rs/test/test_pep508.py b/crates/pep508-rs/test/test_pep508.py deleted file mode 100644 index d86e9e565..000000000 --- a/crates/pep508-rs/test/test_pep508.py +++ /dev/null @@ -1,97 +0,0 @@ -from collections import namedtuple -from unittest import mock - -import pytest -from pep508_rs import Requirement, MarkerEnvironment, Pep508Error, VersionSpecifier - - -def test_pep508(): - req = Requirement("numpy; python_version >= '3.7'") - assert req.name == "numpy" - env = MarkerEnvironment.current() - assert req.evaluate_markers(env, []) - req2 = Requirement("numpy; python_version < '3.7'") - assert not req2.evaluate_markers(env, []) - - requests = Requirement( - 'requests [security,tests] >=2.8.1, ==2.8.* ; python_version > "3.8"' - ) - assert requests.name == "requests" - assert requests.extras == ["security", "tests"] - assert requests.version_or_url == [ - VersionSpecifier(">=2.8.1"), - VersionSpecifier("==2.8.*"), - ] - assert requests.marker == "python_version > '3.8'" - - -def test_marker(): - env = MarkerEnvironment.current() - assert not Requirement("numpy; extra == 'science'").evaluate_markers(env, []) - assert Requirement("numpy; extra == 'science'").evaluate_markers(env, ["science"]) - assert not Requirement( - "numpy; extra == 'science' and extra == 'arrays'" - ).evaluate_markers(env, ["science"]) - assert Requirement( - "numpy; extra == 'science' or extra == 'arrays'" - ).evaluate_markers(env, ["science"]) - - -class FakeVersionInfo( - namedtuple("FakeVersionInfo", ["major", "minor", "micro", "releaselevel", "serial"]) -): - pass - - -@pytest.mark.parametrize( - ("version", "version_str"), - [ - (FakeVersionInfo(3, 10, 11, "final", 0), "3.10.11"), - (FakeVersionInfo(3, 10, 11, "rc", 1), "3.10.11rc1"), - ], -) -def test_marker_values(version, version_str): - with mock.patch("sys.implementation.version", version): - env = MarkerEnvironment.current() - assert str(env.implementation_version.version) == version_str - - -def test_marker_values_current_platform(): - MarkerEnvironment.current() - - -def test_errors(): - with pytest.raises( - Pep508Error, - match="Expected an alphanumeric character starting the extra name, found 'ö'", - ): - Requirement("numpy[ö]; python_version < '3.7'") - - -def test_warnings(caplog): - env = MarkerEnvironment.current() - assert not Requirement("numpy; '3.6' < '3.7'").evaluate_markers(env, []) - assert caplog.messages == [ - "Comparing two quoted strings with each other doesn't make sense: " - "'3.6' < '3.7', evaluating to false" - ] - caplog.clear() - assert not Requirement("numpy; 'a' < 'b'").evaluate_markers(env, []) - assert caplog.messages == [ - "Comparing two quoted strings with each other doesn't make sense: " - "'a' < 'b', evaluating to false" - ] - caplog.clear() - Requirement("numpy; python_version >= '3.9.'").evaluate_markers(env, []) - assert caplog.messages == [ - "Expected PEP 440 version to compare with python_version, found '3.9.', " - "evaluating to false: Version `3.9.` doesn't match PEP 440 rules" - ] - caplog.clear() - # pickleshare 0.7.5 - Requirement("numpy; python_version in '2.6 2.7 3.2 3.3'").evaluate_markers(env, []) - assert caplog.messages == [ - "Expected PEP 440 version to compare with python_version, " - "found '2.6 2.7 3.2 3.3', " - "evaluating to false: Version `2.6 2.7 3.2 3.3` doesn't match PEP 440 rules" - ] diff --git a/crates/puffin-cli/Cargo.toml b/crates/puffin-cli/Cargo.toml index 63b02d1bd..c4752945c 100644 --- a/crates/puffin-cli/Cargo.toml +++ b/crates/puffin-cli/Cargo.toml @@ -17,8 +17,8 @@ colored = { workspace = true } directories = { workspace = true } futures = { workspace = true } install-wheel-rs = { workspace = true } -pep508_rs = { workspace = true } -pep440_rs = { workspace = true } +pep508_rs = { path = "../pep508-rs" } +pep440_rs = { path = "../pep440-rs" } tracing = { workspace = true } tracing-tree = { workspace = true } tracing-subscriber = { workspace = true } diff --git a/crates/puffin-interpreter/Cargo.toml b/crates/puffin-interpreter/Cargo.toml index 745442f57..76ecb7846 100644 --- a/crates/puffin-interpreter/Cargo.toml +++ b/crates/puffin-interpreter/Cargo.toml @@ -10,10 +10,10 @@ authors.workspace = true license.workspace = true [dependencies] +pep440_rs = { path = "../pep440-rs" } +pep508_rs = { path = "../pep508-rs" } puffin-platform = { path = "../puffin-platform" } anyhow = { workspace = true } -pep440_rs = { workspace = true } -pep508_rs = { workspace = true } serde_json = { workspace = true } tracing = { workspace = true } diff --git a/crates/puffin-package/Cargo.toml b/crates/puffin-package/Cargo.toml index c61924c84..68ce5127e 100644 --- a/crates/puffin-package/Cargo.toml +++ b/crates/puffin-package/Cargo.toml @@ -4,14 +4,14 @@ version = "0.1.0" edition = "2021" [dependencies] +pep440_rs = { path = "../pep440-rs", features = ["serde"] } +pep508_rs = { path = "../pep508-rs", features = ["serde"] } puffin-platform = { path = "../puffin-platform" } anyhow = { workspace = true } mailparse = { workspace = true } memchr = { workspace = true } once_cell = { workspace = true } -pep440_rs = { workspace = true, features = ["serde"] } -pep508_rs = { workspace = true, features = ["serde"] } regex = { workspace = true } rfc2047-decoder = { workspace = true } serde = { workspace = true } diff --git a/crates/puffin-platform/Cargo.toml b/crates/puffin-platform/Cargo.toml index 9c7b82827..d83b6064e 100644 --- a/crates/puffin-platform/Cargo.toml +++ b/crates/puffin-platform/Cargo.toml @@ -12,7 +12,7 @@ license.workspace = true [dependencies] glibc_version = { workspace = true } goblin = { workspace = true } -pep440_rs = { workspace = true } +pep440_rs = { path = "../pep440-rs" } platform-info = { workspace = true } plist = { workspace = true } regex = { workspace = true } diff --git a/crates/puffin-resolver/Cargo.toml b/crates/puffin-resolver/Cargo.toml index 0e57c37b6..30d6bbedd 100644 --- a/crates/puffin-resolver/Cargo.toml +++ b/crates/puffin-resolver/Cargo.toml @@ -17,6 +17,6 @@ puffin-package = { path = "../puffin-package" } anyhow = { workspace = true } bitflags = { workspace = true } futures = { workspace = true } -pep440_rs = { workspace = true } -pep508_rs = { workspace = true } +pep440_rs = { path = "../pep440-rs" } +pep508_rs = { path = "../pep508-rs" } tracing = { workspace = true } diff --git a/crates/puffin-resolver/src/lib.rs b/crates/puffin-resolver/src/lib.rs index 219b1fc47..b56752c59 100644 --- a/crates/puffin-resolver/src/lib.rs +++ b/crates/puffin-resolver/src/lib.rs @@ -156,8 +156,7 @@ pub async fn resolve( for dependency in metadata.requires_dist { if !dependency.evaluate_markers( markers, - // TODO(charlie): Remove this clone. - requirement.extras.clone().unwrap_or_default(), + requirement.extras.as_ref().map_or(&[], Vec::as_slice), ) { debug!("--> ignoring {dependency} due to environment mismatch"); continue;