mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 13:25:00 +00:00
Add a requirements.txt parser
This commit is contained in:
parent
8b9ac30507
commit
53607df7c6
9 changed files with 872 additions and 2 deletions
|
@ -6,3 +6,10 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
puffin-requirements = { path = "../puffin-requirements" }
|
||||||
|
|
||||||
|
anyhow = "1.0.75"
|
||||||
|
clap = { version = "4.4.6", features = ["derive"] }
|
||||||
|
colored = { version = "2.0.4" }
|
||||||
|
memchr = { version = "2.6.4" }
|
||||||
|
pep508_rs = { version = "0.2.3" }
|
||||||
|
|
22
crates/puffin-cli/src/commands/install.rs
Normal file
22
crates/puffin-cli/src/commands/install.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
use std::path::Path;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use crate::commands::ExitStatus;
|
||||||
|
|
||||||
|
pub(crate) fn install(src: &Path) -> Result<ExitStatus> {
|
||||||
|
// Read the `requirements.txt` from disk.
|
||||||
|
let requirements_txt = std::fs::read_to_string(src)?;
|
||||||
|
|
||||||
|
// Parse the `requirements.txt` into a list of requirements.
|
||||||
|
let requirements = puffin_requirements::Requirements::from_str(&requirements_txt)?;
|
||||||
|
for requirement in requirements.iter() {
|
||||||
|
#[allow(clippy::print_stdout)]
|
||||||
|
{
|
||||||
|
println!("{requirement:#?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ExitStatus::Success)
|
||||||
|
}
|
30
crates/puffin-cli/src/commands/mod.rs
Normal file
30
crates/puffin-cli/src/commands/mod.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
use std::process::ExitCode;
|
||||||
|
|
||||||
|
pub(crate) use install::install;
|
||||||
|
|
||||||
|
mod install;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub(crate) enum ExitStatus {
|
||||||
|
/// The command succeeded.
|
||||||
|
#[allow(unused)]
|
||||||
|
Success,
|
||||||
|
|
||||||
|
/// The command failed due to an error in the user input.
|
||||||
|
#[allow(unused)]
|
||||||
|
Failure,
|
||||||
|
|
||||||
|
/// The command failed with an unexpected error.
|
||||||
|
#[allow(unused)]
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ExitStatus> for ExitCode {
|
||||||
|
fn from(status: ExitStatus) -> Self {
|
||||||
|
match status {
|
||||||
|
ExitStatus::Success => ExitCode::from(0),
|
||||||
|
ExitStatus::Failure => ExitCode::from(1),
|
||||||
|
ExitStatus::Error => ExitCode::from(2),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,51 @@
|
||||||
fn main() {
|
use std::path::PathBuf;
|
||||||
println!("Hello, world!");
|
use std::process::ExitCode;
|
||||||
|
|
||||||
|
use clap::{Args, Parser, Subcommand};
|
||||||
|
use colored::Colorize;
|
||||||
|
|
||||||
|
use crate::commands::ExitStatus;
|
||||||
|
|
||||||
|
mod commands;
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(author, version, about)]
|
||||||
|
#[command(propagate_version = true)]
|
||||||
|
struct Cli {
|
||||||
|
#[command(subcommand)]
|
||||||
|
command: Commands,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
enum Commands {
|
||||||
|
/// Install dependencies from a `requirements.text` file.
|
||||||
|
Install(InstallArgs),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
struct InstallArgs {
|
||||||
|
/// Path to the `requirements.text` file to install.
|
||||||
|
src: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> ExitCode {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
let result = match &cli.command {
|
||||||
|
Commands::Install(install) => commands::install(&install.src),
|
||||||
|
};
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(code) => code.into(),
|
||||||
|
Err(err) => {
|
||||||
|
#[allow(clippy::print_stderr)]
|
||||||
|
{
|
||||||
|
eprintln!("{}", "puffin failed".red().bold());
|
||||||
|
for cause in err.chain() {
|
||||||
|
eprintln!(" {} {cause}", "Cause:".bold());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExitStatus::Error.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
21
crates/puffin-requirements/Cargo.toml
Normal file
21
crates/puffin-requirements/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
[package]
|
||||||
|
name = "puffin-requirements"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.75"
|
||||||
|
clap = { version = "4.4.6", features = ["derive"] }
|
||||||
|
colored = { version = "2.0.4" }
|
||||||
|
insta = "1.33.0"
|
||||||
|
memchr = { version = "2.6.4" }
|
||||||
|
pep508_rs = { version = "0.2.3" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
criterion = "0.5.1"
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "parser"
|
||||||
|
harness = false
|
96
crates/puffin-requirements/benches/parser.rs
Normal file
96
crates/puffin-requirements/benches/parser.rs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
|
|
||||||
|
use puffin_requirements::Requirements;
|
||||||
|
|
||||||
|
const REQUIREMENTS_TXT: &str = r#"
|
||||||
|
#
|
||||||
|
# This file is autogenerated by pip-compile with Python 3.7
|
||||||
|
# by the following command:
|
||||||
|
#
|
||||||
|
# pip-compile --generate-hashes --output-file=requirements.txt --resolver=backtracking pyproject.toml
|
||||||
|
#
|
||||||
|
attrs==23.1.0 \
|
||||||
|
--hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \
|
||||||
|
--hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015
|
||||||
|
# via
|
||||||
|
# cattrs
|
||||||
|
# lsprotocol
|
||||||
|
cattrs==23.1.2 \
|
||||||
|
--hash=sha256:b2bb14311ac17bed0d58785e5a60f022e5431aca3932e3fc5cc8ed8639de50a4 \
|
||||||
|
--hash=sha256:db1c821b8c537382b2c7c66678c3790091ca0275ac486c76f3c8f3920e83c657
|
||||||
|
# via lsprotocol
|
||||||
|
exceptiongroup==1.1.3 \
|
||||||
|
--hash=sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9 \
|
||||||
|
--hash=sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3
|
||||||
|
# via cattrs
|
||||||
|
importlib-metadata==6.7.0 \
|
||||||
|
--hash=sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4 \
|
||||||
|
--hash=sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5
|
||||||
|
# via
|
||||||
|
# attrs
|
||||||
|
# typeguard
|
||||||
|
lsprotocol==2023.0.0b1 \
|
||||||
|
--hash=sha256:ade2cd0fa0ede7965698cb59cd05d3adbd19178fd73e83f72ef57a032fbb9d62 \
|
||||||
|
--hash=sha256:f7a2d4655cbd5639f373ddd1789807450c543341fa0a32b064ad30dbb9f510d4
|
||||||
|
# via
|
||||||
|
# pygls
|
||||||
|
# ruff-lsp (pyproject.toml)
|
||||||
|
packaging==23.2 \
|
||||||
|
--hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \
|
||||||
|
--hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7
|
||||||
|
# via ruff-lsp (pyproject.toml)
|
||||||
|
pygls==1.1.0 \
|
||||||
|
--hash=sha256:70acb6fe0df1c8a17b7ce08daa0afdb4aedc6913a6a6696003e1434fda80a06e \
|
||||||
|
--hash=sha256:eb19b818039d3d705ec8adbcdf5809a93af925f30cd7a3f3b7573479079ba00e
|
||||||
|
# via ruff-lsp (pyproject.toml)
|
||||||
|
ruff==0.0.292 \
|
||||||
|
--hash=sha256:02f29db018c9d474270c704e6c6b13b18ed0ecac82761e4fcf0faa3728430c96 \
|
||||||
|
--hash=sha256:1093449e37dd1e9b813798f6ad70932b57cf614e5c2b5c51005bf67d55db33ac \
|
||||||
|
--hash=sha256:69654e564342f507edfa09ee6897883ca76e331d4bbc3676d8a8403838e9fade \
|
||||||
|
--hash=sha256:6bdfabd4334684a4418b99b3118793f2c13bb67bf1540a769d7816410402a205 \
|
||||||
|
--hash=sha256:6c3c91859a9b845c33778f11902e7b26440d64b9d5110edd4e4fa1726c41e0a4 \
|
||||||
|
--hash=sha256:7f67a69c8f12fbc8daf6ae6d36705037bde315abf8b82b6e1f4c9e74eb750f68 \
|
||||||
|
--hash=sha256:87616771e72820800b8faea82edd858324b29bb99a920d6aa3d3949dd3f88fb0 \
|
||||||
|
--hash=sha256:8e087b24d0d849c5c81516ec740bf4fd48bf363cfb104545464e0fca749b6af9 \
|
||||||
|
--hash=sha256:9889bac18a0c07018aac75ef6c1e6511d8411724d67cb879103b01758e110a81 \
|
||||||
|
--hash=sha256:aa7c77c53bfcd75dbcd4d1f42d6cabf2485d2e1ee0678da850f08e1ab13081a8 \
|
||||||
|
--hash=sha256:ac153eee6dd4444501c4bb92bff866491d4bfb01ce26dd2fff7ca472c8df9ad0 \
|
||||||
|
--hash=sha256:b76deb3bdbea2ef97db286cf953488745dd6424c122d275f05836c53f62d4016 \
|
||||||
|
--hash=sha256:be8eb50eaf8648070b8e58ece8e69c9322d34afe367eec4210fdee9a555e4ca7 \
|
||||||
|
--hash=sha256:e854b05408f7a8033a027e4b1c7f9889563dd2aca545d13d06711e5c39c3d003 \
|
||||||
|
--hash=sha256:f160b5ec26be32362d0774964e218f3fcf0a7da299f7e220ef45ae9e3e67101a \
|
||||||
|
--hash=sha256:f27282bedfd04d4c3492e5c3398360c9d86a295be00eccc63914438b4ac8a83c \
|
||||||
|
--hash=sha256:f4476f1243af2d8c29da5f235c13dca52177117935e1f9393f9d90f9833f69e4
|
||||||
|
# via ruff-lsp (pyproject.toml)
|
||||||
|
typeguard==3.0.2 \
|
||||||
|
--hash=sha256:bbe993854385284ab42fd5bd3bee6f6556577ce8b50696d6cb956d704f286c8e \
|
||||||
|
--hash=sha256:fee5297fdb28f8e9efcb8142b5ee219e02375509cd77ea9d270b5af826358d5a
|
||||||
|
# via pygls
|
||||||
|
typing-extensions==4.7.1 \
|
||||||
|
--hash=sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36 \
|
||||||
|
--hash=sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2
|
||||||
|
# via
|
||||||
|
# cattrs
|
||||||
|
# importlib-metadata
|
||||||
|
# ruff-lsp (pyproject.toml)
|
||||||
|
# typeguard
|
||||||
|
zipp==3.15.0 \
|
||||||
|
--hash=sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b \
|
||||||
|
--hash=sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556
|
||||||
|
# via importlib-metadata
|
||||||
|
"#;
|
||||||
|
|
||||||
|
fn bench_fibs(c: &mut Criterion) {
|
||||||
|
let mut group = c.benchmark_group("Parser");
|
||||||
|
|
||||||
|
group.bench_function("Parse", |b| {
|
||||||
|
b.iter(|| Requirements::from_str(black_box(REQUIREMENTS_TXT)).unwrap());
|
||||||
|
});
|
||||||
|
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, bench_fibs);
|
||||||
|
criterion_main!(benches);
|
291
crates/puffin-requirements/src/lib.rs
Normal file
291
crates/puffin-requirements/src/lib.rs
Normal file
|
@ -0,0 +1,291 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use memchr::{memchr2, memchr_iter};
|
||||||
|
use pep508_rs::{Pep508Error, Requirement};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Requirements(Vec<Requirement>);
|
||||||
|
|
||||||
|
impl FromStr for Requirements {
|
||||||
|
type Err = Pep508Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(Self(
|
||||||
|
RequirementsIterator::new(s)
|
||||||
|
.map(|requirement| Requirement::from_str(requirement.as_str()))
|
||||||
|
.collect::<Result<Vec<Requirement>, Pep508Error>>()?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Requirements {
|
||||||
|
type Target = [Requirement];
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct RequirementsIterator<'a> {
|
||||||
|
text: &'a str,
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RequirementsIterator<'a> {
|
||||||
|
fn new(text: &'a str) -> Self {
|
||||||
|
Self { text, index: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct RequirementLine<'a> {
|
||||||
|
/// The line as included in the `requirements.txt`, including comments and `--hash` extras.
|
||||||
|
line: Cow<'a, str>,
|
||||||
|
/// The line, with comments and `--hash` extras stripped.
|
||||||
|
len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RequirementLine<'a> {
|
||||||
|
/// Create a new `RequirementLine` from a line of text.
|
||||||
|
fn from_line(line: Cow<'a, str>) -> Self {
|
||||||
|
Self {
|
||||||
|
len: Self::strip_trivia(&line),
|
||||||
|
line,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a parseable requirement line.
|
||||||
|
fn as_str(&self) -> &str {
|
||||||
|
&self.line[..self.len]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Strip trivia (comments and `--hash` extras) from a requirement, returning the length of the
|
||||||
|
/// requirement itself.
|
||||||
|
fn strip_trivia(requirement: &str) -> usize {
|
||||||
|
let mut len = requirement.len();
|
||||||
|
|
||||||
|
// Strip comments.
|
||||||
|
for position in memchr_iter(b'#', requirement[..len].as_bytes()) {
|
||||||
|
// The comment _must_ be preceded by whitespace.
|
||||||
|
if requirement[..len + position]
|
||||||
|
.chars()
|
||||||
|
.rev()
|
||||||
|
.next()
|
||||||
|
.is_some_and(char::is_whitespace)
|
||||||
|
{
|
||||||
|
len = position;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip `--hash` extras.
|
||||||
|
if let Some(index) = requirement[..len].find("--hash") {
|
||||||
|
len = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for RequirementsIterator<'a> {
|
||||||
|
type Item = RequirementLine<'a>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<RequirementLine<'a>> {
|
||||||
|
if self.index == self.text.len() - 1 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the next line break.
|
||||||
|
let Some((start, length)) = find_newline(&self.text[self.index..]) else {
|
||||||
|
// Parse the rest of the text.
|
||||||
|
let line = &self.text[self.index..];
|
||||||
|
self.index = self.text.len() - 1;
|
||||||
|
|
||||||
|
// Skip fully-commented lines.
|
||||||
|
if line.trim_start().starts_with('#') {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip empty lines.
|
||||||
|
if line.trim().is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some(RequirementLine::from_line(Cow::Borrowed(line)));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Skip fully-commented lines.
|
||||||
|
if self.text[self.index..].trim_start().starts_with('#') {
|
||||||
|
self.index += start + length;
|
||||||
|
return self.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip empty lines.
|
||||||
|
if self.text[self.index..self.index + start].trim().is_empty() {
|
||||||
|
self.index += start + length;
|
||||||
|
return self.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the newline is preceded by a continuation (\\), keep going.
|
||||||
|
if self.text[..self.index + start]
|
||||||
|
.chars()
|
||||||
|
.rev()
|
||||||
|
.next()
|
||||||
|
.is_some_and(|c| c == '\\')
|
||||||
|
{
|
||||||
|
// Add the line contents, preceding the continuation.
|
||||||
|
let mut line = self.text[self.index..self.index + start - 1].to_owned();
|
||||||
|
self.index += start + length;
|
||||||
|
|
||||||
|
// Eat lines until we see a non-continuation.
|
||||||
|
while let Some((start, length)) = find_newline(&self.text[self.index..]) {
|
||||||
|
if self.text[..self.index + start]
|
||||||
|
.chars()
|
||||||
|
.rev()
|
||||||
|
.next()
|
||||||
|
.is_some_and(|c| c == '\\')
|
||||||
|
{
|
||||||
|
// Add the line contents, preceding the continuation.
|
||||||
|
line.push_str(&self.text[self.index..self.index + start - 1]);
|
||||||
|
self.index += start + length;
|
||||||
|
} else {
|
||||||
|
// Add the line contents, excluding the continuation.
|
||||||
|
line.push_str(&self.text[self.index..self.index + start]);
|
||||||
|
self.index += start + length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(RequirementLine::from_line(Cow::Owned(line)))
|
||||||
|
} else {
|
||||||
|
let line = &self.text[self.index..self.index + start];
|
||||||
|
self.index += start + length;
|
||||||
|
Some(RequirementLine::from_line(Cow::Borrowed(line)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the start and end position of the first newline character in the given text.
|
||||||
|
#[inline]
|
||||||
|
fn find_newline(text: &str) -> Option<(usize, usize)> {
|
||||||
|
let bytes = text.as_bytes();
|
||||||
|
let position = memchr2(b'\n', b'\r', bytes)?;
|
||||||
|
|
||||||
|
// SAFETY: memchr guarantees to return valid positions
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
let newline_character = unsafe { *bytes.get_unchecked(position) };
|
||||||
|
|
||||||
|
Some(match newline_character {
|
||||||
|
// Explicit branch for `\n` as this is the most likely path
|
||||||
|
b'\n' => (position, 1),
|
||||||
|
// '\r\n'
|
||||||
|
b'\r' if bytes.get(position.saturating_add(1)) == Some(&b'\n') => (position, 2),
|
||||||
|
// '\r'
|
||||||
|
_ => (position, 1),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use insta::assert_debug_snapshot;
|
||||||
|
|
||||||
|
use crate::Requirements;
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple() -> Result<()> {
|
||||||
|
assert_debug_snapshot!(Requirements::from_str(r#"flask==2.0"#)?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pip_compile() -> Result<()> {
|
||||||
|
assert_debug_snapshot!(Requirements::from_str(
|
||||||
|
r#"
|
||||||
|
#
|
||||||
|
# This file is autogenerated by pip-compile with Python 3.7
|
||||||
|
# by the following command:
|
||||||
|
#
|
||||||
|
# pip-compile --generate-hashes --output-file=requirements.txt --resolver=backtracking pyproject.toml
|
||||||
|
#
|
||||||
|
attrs==23.1.0 \
|
||||||
|
--hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \
|
||||||
|
--hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015
|
||||||
|
# via
|
||||||
|
# cattrs
|
||||||
|
# lsprotocol
|
||||||
|
cattrs==23.1.2 \
|
||||||
|
--hash=sha256:b2bb14311ac17bed0d58785e5a60f022e5431aca3932e3fc5cc8ed8639de50a4 \
|
||||||
|
--hash=sha256:db1c821b8c537382b2c7c66678c3790091ca0275ac486c76f3c8f3920e83c657
|
||||||
|
# via lsprotocol
|
||||||
|
exceptiongroup==1.1.3 \
|
||||||
|
--hash=sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9 \
|
||||||
|
--hash=sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3
|
||||||
|
# via cattrs
|
||||||
|
importlib-metadata==6.7.0 \
|
||||||
|
--hash=sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4 \
|
||||||
|
--hash=sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5
|
||||||
|
# via
|
||||||
|
# attrs
|
||||||
|
# typeguard
|
||||||
|
lsprotocol==2023.0.0b1 \
|
||||||
|
--hash=sha256:ade2cd0fa0ede7965698cb59cd05d3adbd19178fd73e83f72ef57a032fbb9d62 \
|
||||||
|
--hash=sha256:f7a2d4655cbd5639f373ddd1789807450c543341fa0a32b064ad30dbb9f510d4
|
||||||
|
# via
|
||||||
|
# pygls
|
||||||
|
# ruff-lsp (pyproject.toml)
|
||||||
|
packaging==23.2 \
|
||||||
|
--hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \
|
||||||
|
--hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7
|
||||||
|
# via ruff-lsp (pyproject.toml)
|
||||||
|
pygls==1.1.0 \
|
||||||
|
--hash=sha256:70acb6fe0df1c8a17b7ce08daa0afdb4aedc6913a6a6696003e1434fda80a06e \
|
||||||
|
--hash=sha256:eb19b818039d3d705ec8adbcdf5809a93af925f30cd7a3f3b7573479079ba00e
|
||||||
|
# via ruff-lsp (pyproject.toml)
|
||||||
|
ruff==0.0.292 \
|
||||||
|
--hash=sha256:02f29db018c9d474270c704e6c6b13b18ed0ecac82761e4fcf0faa3728430c96 \
|
||||||
|
--hash=sha256:1093449e37dd1e9b813798f6ad70932b57cf614e5c2b5c51005bf67d55db33ac \
|
||||||
|
--hash=sha256:69654e564342f507edfa09ee6897883ca76e331d4bbc3676d8a8403838e9fade \
|
||||||
|
--hash=sha256:6bdfabd4334684a4418b99b3118793f2c13bb67bf1540a769d7816410402a205 \
|
||||||
|
--hash=sha256:6c3c91859a9b845c33778f11902e7b26440d64b9d5110edd4e4fa1726c41e0a4 \
|
||||||
|
--hash=sha256:7f67a69c8f12fbc8daf6ae6d36705037bde315abf8b82b6e1f4c9e74eb750f68 \
|
||||||
|
--hash=sha256:87616771e72820800b8faea82edd858324b29bb99a920d6aa3d3949dd3f88fb0 \
|
||||||
|
--hash=sha256:8e087b24d0d849c5c81516ec740bf4fd48bf363cfb104545464e0fca749b6af9 \
|
||||||
|
--hash=sha256:9889bac18a0c07018aac75ef6c1e6511d8411724d67cb879103b01758e110a81 \
|
||||||
|
--hash=sha256:aa7c77c53bfcd75dbcd4d1f42d6cabf2485d2e1ee0678da850f08e1ab13081a8 \
|
||||||
|
--hash=sha256:ac153eee6dd4444501c4bb92bff866491d4bfb01ce26dd2fff7ca472c8df9ad0 \
|
||||||
|
--hash=sha256:b76deb3bdbea2ef97db286cf953488745dd6424c122d275f05836c53f62d4016 \
|
||||||
|
--hash=sha256:be8eb50eaf8648070b8e58ece8e69c9322d34afe367eec4210fdee9a555e4ca7 \
|
||||||
|
--hash=sha256:e854b05408f7a8033a027e4b1c7f9889563dd2aca545d13d06711e5c39c3d003 \
|
||||||
|
--hash=sha256:f160b5ec26be32362d0774964e218f3fcf0a7da299f7e220ef45ae9e3e67101a \
|
||||||
|
--hash=sha256:f27282bedfd04d4c3492e5c3398360c9d86a295be00eccc63914438b4ac8a83c \
|
||||||
|
--hash=sha256:f4476f1243af2d8c29da5f235c13dca52177117935e1f9393f9d90f9833f69e4
|
||||||
|
# via ruff-lsp (pyproject.toml)
|
||||||
|
typeguard==3.0.2 \
|
||||||
|
--hash=sha256:bbe993854385284ab42fd5bd3bee6f6556577ce8b50696d6cb956d704f286c8e \
|
||||||
|
--hash=sha256:fee5297fdb28f8e9efcb8142b5ee219e02375509cd77ea9d270b5af826358d5a
|
||||||
|
# via pygls
|
||||||
|
typing-extensions==4.7.1 \
|
||||||
|
--hash=sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36 \
|
||||||
|
--hash=sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2
|
||||||
|
# via
|
||||||
|
# cattrs
|
||||||
|
# importlib-metadata
|
||||||
|
# ruff-lsp (pyproject.toml)
|
||||||
|
# typeguard
|
||||||
|
zipp==3.15.0 \
|
||||||
|
--hash=sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b \
|
||||||
|
--hash=sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556
|
||||||
|
# via importlib-metadata
|
||||||
|
"#
|
||||||
|
)?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,320 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-requirements/src/lib.rs
|
||||||
|
expression: "Requirements::from_str(r#\"\n#\n# This file is autogenerated by pip-compile with Python 3.7\n# by the following command:\n#\n# pip-compile --generate-hashes --output-file=requirements.txt --resolver=backtracking pyproject.toml\n#\nattrs==23.1.0 \\\n --hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \\\n --hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015\n # via\n # cattrs\n # lsprotocol\ncattrs==23.1.2 \\\n --hash=sha256:b2bb14311ac17bed0d58785e5a60f022e5431aca3932e3fc5cc8ed8639de50a4 \\\n --hash=sha256:db1c821b8c537382b2c7c66678c3790091ca0275ac486c76f3c8f3920e83c657\n # via lsprotocol\nexceptiongroup==1.1.3 \\\n --hash=sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9 \\\n --hash=sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3\n # via cattrs\nimportlib-metadata==6.7.0 \\\n --hash=sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4 \\\n --hash=sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5\n # via\n # attrs\n # typeguard\nlsprotocol==2023.0.0b1 \\\n --hash=sha256:ade2cd0fa0ede7965698cb59cd05d3adbd19178fd73e83f72ef57a032fbb9d62 \\\n --hash=sha256:f7a2d4655cbd5639f373ddd1789807450c543341fa0a32b064ad30dbb9f510d4\n # via\n # pygls\n # ruff-lsp (pyproject.toml)\npackaging==23.2 \\\n --hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \\\n --hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7\n # via ruff-lsp (pyproject.toml)\npygls==1.1.0 \\\n --hash=sha256:70acb6fe0df1c8a17b7ce08daa0afdb4aedc6913a6a6696003e1434fda80a06e \\\n --hash=sha256:eb19b818039d3d705ec8adbcdf5809a93af925f30cd7a3f3b7573479079ba00e\n # via ruff-lsp (pyproject.toml)\nruff==0.0.292 \\\n --hash=sha256:02f29db018c9d474270c704e6c6b13b18ed0ecac82761e4fcf0faa3728430c96 \\\n --hash=sha256:1093449e37dd1e9b813798f6ad70932b57cf614e5c2b5c51005bf67d55db33ac \\\n --hash=sha256:69654e564342f507edfa09ee6897883ca76e331d4bbc3676d8a8403838e9fade \\\n --hash=sha256:6bdfabd4334684a4418b99b3118793f2c13bb67bf1540a769d7816410402a205 \\\n --hash=sha256:6c3c91859a9b845c33778f11902e7b26440d64b9d5110edd4e4fa1726c41e0a4 \\\n --hash=sha256:7f67a69c8f12fbc8daf6ae6d36705037bde315abf8b82b6e1f4c9e74eb750f68 \\\n --hash=sha256:87616771e72820800b8faea82edd858324b29bb99a920d6aa3d3949dd3f88fb0 \\\n --hash=sha256:8e087b24d0d849c5c81516ec740bf4fd48bf363cfb104545464e0fca749b6af9 \\\n --hash=sha256:9889bac18a0c07018aac75ef6c1e6511d8411724d67cb879103b01758e110a81 \\\n --hash=sha256:aa7c77c53bfcd75dbcd4d1f42d6cabf2485d2e1ee0678da850f08e1ab13081a8 \\\n --hash=sha256:ac153eee6dd4444501c4bb92bff866491d4bfb01ce26dd2fff7ca472c8df9ad0 \\\n --hash=sha256:b76deb3bdbea2ef97db286cf953488745dd6424c122d275f05836c53f62d4016 \\\n --hash=sha256:be8eb50eaf8648070b8e58ece8e69c9322d34afe367eec4210fdee9a555e4ca7 \\\n --hash=sha256:e854b05408f7a8033a027e4b1c7f9889563dd2aca545d13d06711e5c39c3d003 \\\n --hash=sha256:f160b5ec26be32362d0774964e218f3fcf0a7da299f7e220ef45ae9e3e67101a \\\n --hash=sha256:f27282bedfd04d4c3492e5c3398360c9d86a295be00eccc63914438b4ac8a83c \\\n --hash=sha256:f4476f1243af2d8c29da5f235c13dca52177117935e1f9393f9d90f9833f69e4\n # via ruff-lsp (pyproject.toml)\ntypeguard==3.0.2 \\\n --hash=sha256:bbe993854385284ab42fd5bd3bee6f6556577ce8b50696d6cb956d704f286c8e \\\n --hash=sha256:fee5297fdb28f8e9efcb8142b5ee219e02375509cd77ea9d270b5af826358d5a\n # via pygls\ntyping-extensions==4.7.1 \\\n --hash=sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36 \\\n --hash=sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2\n # via\n # cattrs\n # importlib-metadata\n # ruff-lsp (pyproject.toml)\n # typeguard\nzipp==3.15.0 \\\n --hash=sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b \\\n --hash=sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556\n # via importlib-metadata\n\"#)?"
|
||||||
|
---
|
||||||
|
Requirements(
|
||||||
|
[
|
||||||
|
Requirement {
|
||||||
|
name: "attrs",
|
||||||
|
extras: None,
|
||||||
|
version_or_url: Some(
|
||||||
|
VersionSpecifier(
|
||||||
|
VersionSpecifiers(
|
||||||
|
[
|
||||||
|
VersionSpecifier {
|
||||||
|
operator: Equal,
|
||||||
|
version: Version {
|
||||||
|
epoch: 0,
|
||||||
|
release: [
|
||||||
|
23,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
pre: None,
|
||||||
|
post: None,
|
||||||
|
dev: None,
|
||||||
|
local: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
marker: None,
|
||||||
|
},
|
||||||
|
Requirement {
|
||||||
|
name: "cattrs",
|
||||||
|
extras: None,
|
||||||
|
version_or_url: Some(
|
||||||
|
VersionSpecifier(
|
||||||
|
VersionSpecifiers(
|
||||||
|
[
|
||||||
|
VersionSpecifier {
|
||||||
|
operator: Equal,
|
||||||
|
version: Version {
|
||||||
|
epoch: 0,
|
||||||
|
release: [
|
||||||
|
23,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
pre: None,
|
||||||
|
post: None,
|
||||||
|
dev: None,
|
||||||
|
local: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
marker: None,
|
||||||
|
},
|
||||||
|
Requirement {
|
||||||
|
name: "exceptiongroup",
|
||||||
|
extras: None,
|
||||||
|
version_or_url: Some(
|
||||||
|
VersionSpecifier(
|
||||||
|
VersionSpecifiers(
|
||||||
|
[
|
||||||
|
VersionSpecifier {
|
||||||
|
operator: Equal,
|
||||||
|
version: Version {
|
||||||
|
epoch: 0,
|
||||||
|
release: [
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
pre: None,
|
||||||
|
post: None,
|
||||||
|
dev: None,
|
||||||
|
local: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
marker: None,
|
||||||
|
},
|
||||||
|
Requirement {
|
||||||
|
name: "importlib-metadata",
|
||||||
|
extras: None,
|
||||||
|
version_or_url: Some(
|
||||||
|
VersionSpecifier(
|
||||||
|
VersionSpecifiers(
|
||||||
|
[
|
||||||
|
VersionSpecifier {
|
||||||
|
operator: Equal,
|
||||||
|
version: Version {
|
||||||
|
epoch: 0,
|
||||||
|
release: [
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
pre: None,
|
||||||
|
post: None,
|
||||||
|
dev: None,
|
||||||
|
local: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
marker: None,
|
||||||
|
},
|
||||||
|
Requirement {
|
||||||
|
name: "lsprotocol",
|
||||||
|
extras: None,
|
||||||
|
version_or_url: Some(
|
||||||
|
VersionSpecifier(
|
||||||
|
VersionSpecifiers(
|
||||||
|
[
|
||||||
|
VersionSpecifier {
|
||||||
|
operator: Equal,
|
||||||
|
version: Version {
|
||||||
|
epoch: 0,
|
||||||
|
release: [
|
||||||
|
2023,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
pre: Some(
|
||||||
|
(
|
||||||
|
Beta,
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
post: None,
|
||||||
|
dev: None,
|
||||||
|
local: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
marker: None,
|
||||||
|
},
|
||||||
|
Requirement {
|
||||||
|
name: "packaging",
|
||||||
|
extras: None,
|
||||||
|
version_or_url: Some(
|
||||||
|
VersionSpecifier(
|
||||||
|
VersionSpecifiers(
|
||||||
|
[
|
||||||
|
VersionSpecifier {
|
||||||
|
operator: Equal,
|
||||||
|
version: Version {
|
||||||
|
epoch: 0,
|
||||||
|
release: [
|
||||||
|
23,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
pre: None,
|
||||||
|
post: None,
|
||||||
|
dev: None,
|
||||||
|
local: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
marker: None,
|
||||||
|
},
|
||||||
|
Requirement {
|
||||||
|
name: "pygls",
|
||||||
|
extras: None,
|
||||||
|
version_or_url: Some(
|
||||||
|
VersionSpecifier(
|
||||||
|
VersionSpecifiers(
|
||||||
|
[
|
||||||
|
VersionSpecifier {
|
||||||
|
operator: Equal,
|
||||||
|
version: Version {
|
||||||
|
epoch: 0,
|
||||||
|
release: [
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
pre: None,
|
||||||
|
post: None,
|
||||||
|
dev: None,
|
||||||
|
local: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
marker: None,
|
||||||
|
},
|
||||||
|
Requirement {
|
||||||
|
name: "ruff",
|
||||||
|
extras: None,
|
||||||
|
version_or_url: Some(
|
||||||
|
VersionSpecifier(
|
||||||
|
VersionSpecifiers(
|
||||||
|
[
|
||||||
|
VersionSpecifier {
|
||||||
|
operator: Equal,
|
||||||
|
version: Version {
|
||||||
|
epoch: 0,
|
||||||
|
release: [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
292,
|
||||||
|
],
|
||||||
|
pre: None,
|
||||||
|
post: None,
|
||||||
|
dev: None,
|
||||||
|
local: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
marker: None,
|
||||||
|
},
|
||||||
|
Requirement {
|
||||||
|
name: "typeguard",
|
||||||
|
extras: None,
|
||||||
|
version_or_url: Some(
|
||||||
|
VersionSpecifier(
|
||||||
|
VersionSpecifiers(
|
||||||
|
[
|
||||||
|
VersionSpecifier {
|
||||||
|
operator: Equal,
|
||||||
|
version: Version {
|
||||||
|
epoch: 0,
|
||||||
|
release: [
|
||||||
|
3,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
pre: None,
|
||||||
|
post: None,
|
||||||
|
dev: None,
|
||||||
|
local: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
marker: None,
|
||||||
|
},
|
||||||
|
Requirement {
|
||||||
|
name: "typing-extensions",
|
||||||
|
extras: None,
|
||||||
|
version_or_url: Some(
|
||||||
|
VersionSpecifier(
|
||||||
|
VersionSpecifiers(
|
||||||
|
[
|
||||||
|
VersionSpecifier {
|
||||||
|
operator: Equal,
|
||||||
|
version: Version {
|
||||||
|
epoch: 0,
|
||||||
|
release: [
|
||||||
|
4,
|
||||||
|
7,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
pre: None,
|
||||||
|
post: None,
|
||||||
|
dev: None,
|
||||||
|
local: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
marker: None,
|
||||||
|
},
|
||||||
|
Requirement {
|
||||||
|
name: "zipp",
|
||||||
|
extras: None,
|
||||||
|
version_or_url: Some(
|
||||||
|
VersionSpecifier(
|
||||||
|
VersionSpecifiers(
|
||||||
|
[
|
||||||
|
VersionSpecifier {
|
||||||
|
operator: Equal,
|
||||||
|
version: Version {
|
||||||
|
epoch: 0,
|
||||||
|
release: [
|
||||||
|
3,
|
||||||
|
15,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
pre: None,
|
||||||
|
post: None,
|
||||||
|
dev: None,
|
||||||
|
local: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
marker: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1,35 @@
|
||||||
|
---
|
||||||
|
source: crates/puffin-requirements/src/lib.rs
|
||||||
|
expression: "Requirements::from_str(r#\"flask==2.0\"#)?"
|
||||||
|
---
|
||||||
|
Requirements(
|
||||||
|
[
|
||||||
|
Requirement {
|
||||||
|
name: "flask",
|
||||||
|
extras: None,
|
||||||
|
version_or_url: Some(
|
||||||
|
VersionSpecifier(
|
||||||
|
VersionSpecifiers(
|
||||||
|
[
|
||||||
|
VersionSpecifier {
|
||||||
|
operator: Equal,
|
||||||
|
version: Version {
|
||||||
|
epoch: 0,
|
||||||
|
release: [
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
pre: None,
|
||||||
|
post: None,
|
||||||
|
dev: None,
|
||||||
|
local: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
marker: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
Loading…
Add table
Add a link
Reference in a new issue