lockdiff/
python.rs

1use crate::Package;
2use anyhow::{bail, Result};
3
4pub(crate) fn parse_requirements_txt(contents: &str) -> Result<Vec<Package>> {
5    let mut packages = vec![];
6    for line in contents.lines() {
7        let line = line.trim();
8        let line = line.trim_end_matches('\\');
9        if line.is_empty() {
10            continue;
11        }
12        if line.trim_start().starts_with('#') {
13            continue;
14        }
15        if line.starts_with("--") {
16            continue;
17        }
18        let split: Vec<_> = line.split("==").collect();
19        if split.len() != 2 {
20            bail!("Expected <package>==<version> in '{line}'");
21        }
22        let name = &split[0].trim();
23        let version = &split[1].trim();
24        let package = Package::new(name, version);
25        packages.push(package);
26    }
27    Ok(packages)
28}
29
30#[cfg(test)]
31mod tests {
32
33    use super::*;
34
35    #[test]
36    fn test_parse_simple_requirements_txt() {
37        let contents = r#"
38# This file is autogenerated by pip-compile with Python 3.10
39# by the following command:
40#
41#    pip-compile --output-file=requirements.txt pyproject.toml
42#
43asgiref==3.6.0
44    # via django
45django==4.1.7
46    # via my-cool-django-app (pyproject.toml)
47"#;
48
49        let packages = parse_requirements_txt(contents).unwrap();
50        assert_eq!(
51            &packages,
52            &[
53                Package::new("asgiref", "3.6.0"),
54                Package::new("django", "4.1.7")
55            ]
56        );
57    }
58
59    #[test]
60    fn test_parse_requirements_txt_with_hashes() {
61        let contents = r#"
62# This file is autogenerated by pip-compile with Python 3.10
63# by the following command:
64#
65#    pip-compile --output-file=requirements.txt pyproject.toml
66#
67asgiref==3.6.0 \
68    --hash=sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac \
69    --hash=sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506
70
71    # via django
72django==4.1.7
73    --hash=sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506
74    --hash=sha256:f2f431e75adc40039ace496ad3b9f17227022e8b11566f4b363da44c7e44761e
75    # via -r requirements.in
76"#;
77
78        let packages = parse_requirements_txt(contents).unwrap();
79        assert_eq!(
80            &packages,
81            &[
82                Package::new("asgiref", "3.6.0"),
83                Package::new("django", "4.1.7")
84            ]
85        );
86    }
87}