Skip to content

Commit f7b97b1

Browse files
authored
Add poetry lockfile v2 support (#780)
This adds tests and documentation for supporting v2 of the poetry lockfile. There are no changes to our parser, since none of the v2 changes affect the packages or their versions. Closes #761.
1 parent 21fed13 commit f7b97b1

File tree

4 files changed

+251
-5
lines changed

4 files changed

+251
-5
lines changed

docs/knowledge_base/analyzing-dependencies.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ The Phylum CLI natively supports processing the lock/requirements files for seve
1414
* `requirements.txt`
1515
* `Pipfile.lock`
1616
* `Pipfile`
17-
* `poetry.lock`
17+
* `poetry.lock` (Version 1 + 2)
1818
* NuGet
1919
* `*.csproj`
2020
* Maven

lockfile/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ mod tests {
261261
(LockfileFormat::Npm, 2),
262262
(LockfileFormat::Gem, 1),
263263
(LockfileFormat::Pipenv, 2),
264-
(LockfileFormat::Poetry, 1),
264+
(LockfileFormat::Poetry, 2),
265265
(LockfileFormat::Maven, 2),
266266
(LockfileFormat::Gradle, 1),
267267
(LockfileFormat::Msbuild, 2),

lockfile/src/python.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,11 @@ impl Parse for Poetry {
104104
let mut lock: PoetryLock = toml::from_str(data)?;
105105

106106
// Warn if the version of this lockfile might not be supported.
107-
if !lock.metadata.lock_version.starts_with("1.") {
107+
if !lock.metadata.lock_version.starts_with("1.")
108+
&& !lock.metadata.lock_version.starts_with("2.")
109+
{
108110
log::debug!(
109-
"Expected poetry lockfile version ^1.0.0, found {}.",
111+
"Expected poetry lockfile version ^1.0.0 or ^2.0.0, found {}.",
110112
lock.metadata.lock_version
111113
);
112114
}
@@ -269,7 +271,7 @@ mod tests {
269271
}
270272

271273
#[test]
272-
fn parse_poetry_lock() {
274+
fn parse_poetry_lock_v1() {
273275
let pkgs = Poetry.parse(include_str!("../../tests/fixtures/poetry.lock")).unwrap();
274276
assert_eq!(pkgs.len(), 45);
275277

@@ -301,6 +303,34 @@ mod tests {
301303
}
302304
}
303305

306+
#[test]
307+
fn parse_poetry_lock_v2() {
308+
let pkgs = Poetry.parse(include_str!("../../tests/fixtures/poetry_v2.lock")).unwrap();
309+
assert_eq!(pkgs.len(), 9);
310+
311+
let expected_pkgs = [
312+
PackageDescriptor {
313+
name: "certifi".into(),
314+
version: "2020.12.5".into(),
315+
package_type: PackageType::PyPi,
316+
},
317+
PackageDescriptor {
318+
name: "pywin32".into(),
319+
version: "227".into(),
320+
package_type: PackageType::PyPi,
321+
},
322+
PackageDescriptor {
323+
name: "docker".into(),
324+
version: "4.3.1".into(),
325+
package_type: PackageType::PyPi,
326+
},
327+
];
328+
329+
for expected_pkg in expected_pkgs {
330+
assert!(pkgs.contains(&expected_pkg), "missing package {expected_pkg:?}");
331+
}
332+
}
333+
304334
/// Ensure sources other than PyPi are ignored.
305335
#[test]
306336
fn poetry_ignore_other_sources() {

tests/fixtures/poetry_v2.lock

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
[[package]]
2+
name = "certifi"
3+
version = "2020.12.5"
4+
description = "Python package for providing Mozilla's CA Bundle."
5+
category = "main"
6+
optional = false
7+
python-versions = "*"
8+
9+
[[package.files]]
10+
file = "certifi-2020.12.5-py2.py3-none-any.whl"
11+
hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"
12+
13+
[[package.files]]
14+
file = "certifi-2020.12.5.tar.gz"
15+
hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"
16+
17+
[[package]]
18+
name = "chardet"
19+
version = "4.0.0"
20+
description = "Universal encoding detector for Python 2 and 3"
21+
category = "main"
22+
optional = false
23+
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
24+
25+
[[package.files]]
26+
file = "chardet-4.0.0-py2.py3-none-any.whl"
27+
hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
28+
29+
[[package.files]]
30+
file = "chardet-4.0.0.tar.gz"
31+
hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"
32+
33+
[[package]]
34+
name = "docker"
35+
version = "4.3.1"
36+
description = "A Python library for the Docker Engine API."
37+
category = "main"
38+
optional = false
39+
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
40+
41+
[[package.files]]
42+
file = "docker-4.3.1-py2.py3-none-any.whl"
43+
hash = "sha256:13966471e8bc23b36bfb3a6fb4ab75043a5ef1dac86516274777576bed3b9828"
44+
45+
[[package.files]]
46+
file = "docker-4.3.1.tar.gz"
47+
hash = "sha256:bad94b8dd001a8a4af19ce4becc17f41b09f228173ffe6a4e0355389eef142f2"
48+
49+
[package.dependencies]
50+
pywin32 = {version = "227", markers = "sys_platform == \"win32\""}
51+
requests = ">=2.14.2,<2.18.0 || >2.18.0"
52+
six = ">=1.4.0"
53+
websocket-client = ">=0.32.0"
54+
55+
[package.extras]
56+
ssh = ["paramiko (>=2.4.2)"]
57+
tls = ["pyOpenSSL (>=17.5.0)", "cryptography (>=1.3.4)", "idna (>=2.0.0)"]
58+
59+
[[package]]
60+
name = "idna"
61+
version = "2.10"
62+
description = "Internationalized Domain Names in Applications (IDNA)"
63+
category = "main"
64+
optional = false
65+
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
66+
67+
[[package.files]]
68+
file = "idna-2.10-py2.py3-none-any.whl"
69+
hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
70+
71+
[[package.files]]
72+
file = "idna-2.10.tar.gz"
73+
hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"
74+
75+
[[package]]
76+
name = "pywin32"
77+
version = "227"
78+
description = "Python for Window Extensions"
79+
category = "main"
80+
optional = false
81+
python-versions = "*"
82+
83+
[[package.files]]
84+
file = "pywin32-227-cp27-cp27m-win32.whl"
85+
hash = "sha256:371fcc39416d736401f0274dd64c2302728c9e034808e37381b5e1b22be4a6b0"
86+
87+
[[package.files]]
88+
file = "pywin32-227-cp27-cp27m-win_amd64.whl"
89+
hash = "sha256:4cdad3e84191194ea6d0dd1b1b9bdda574ff563177d2adf2b4efec2a244fa116"
90+
91+
[[package.files]]
92+
file = "pywin32-227-cp35-cp35m-win32.whl"
93+
hash = "sha256:f4c5be1a293bae0076d93c88f37ee8da68136744588bc5e2be2f299a34ceb7aa"
94+
95+
[[package.files]]
96+
file = "pywin32-227-cp35-cp35m-win_amd64.whl"
97+
hash = "sha256:a929a4af626e530383a579431b70e512e736e9588106715215bf685a3ea508d4"
98+
99+
[[package.files]]
100+
file = "pywin32-227-cp36-cp36m-win32.whl"
101+
hash = "sha256:300a2db938e98c3e7e2093e4491439e62287d0d493fe07cce110db070b54c0be"
102+
103+
[[package.files]]
104+
file = "pywin32-227-cp36-cp36m-win_amd64.whl"
105+
hash = "sha256:9b31e009564fb95db160f154e2aa195ed66bcc4c058ed72850d047141b36f3a2"
106+
107+
[[package.files]]
108+
file = "pywin32-227-cp37-cp37m-win32.whl"
109+
hash = "sha256:47a3c7551376a865dd8d095a98deba954a98f326c6fe3c72d8726ca6e6b15507"
110+
111+
[[package.files]]
112+
file = "pywin32-227-cp37-cp37m-win_amd64.whl"
113+
hash = "sha256:31f88a89139cb2adc40f8f0e65ee56a8c585f629974f9e07622ba80199057511"
114+
115+
[[package.files]]
116+
file = "pywin32-227-cp38-cp38-win32.whl"
117+
hash = "sha256:7f18199fbf29ca99dff10e1f09451582ae9e372a892ff03a28528a24d55875bc"
118+
119+
[[package.files]]
120+
file = "pywin32-227-cp38-cp38-win_amd64.whl"
121+
hash = "sha256:7c1ae32c489dc012930787f06244426f8356e129184a02c25aef163917ce158e"
122+
123+
[[package.files]]
124+
file = "pywin32-227-cp39-cp39-win32.whl"
125+
hash = "sha256:c054c52ba46e7eb6b7d7dfae4dbd987a1bb48ee86debe3f245a2884ece46e295"
126+
127+
[[package.files]]
128+
file = "pywin32-227-cp39-cp39-win_amd64.whl"
129+
hash = "sha256:f27cec5e7f588c3d1051651830ecc00294f90728d19c3bf6916e6dba93ea357c"
130+
131+
[[package]]
132+
name = "requests"
133+
version = "2.25.1"
134+
description = "Python HTTP for Humans."
135+
category = "main"
136+
optional = false
137+
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
138+
139+
[[package.files]]
140+
file = "requests-2.25.1-py2.py3-none-any.whl"
141+
hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
142+
143+
[[package.files]]
144+
file = "requests-2.25.1.tar.gz"
145+
hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"
146+
147+
[package.dependencies]
148+
certifi = ">=2017.4.17"
149+
chardet = ">=3.0.2,<5"
150+
idna = ">=2.5,<3"
151+
urllib3 = ">=1.21.1,<1.27"
152+
153+
[package.extras]
154+
security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
155+
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
156+
157+
[[package]]
158+
name = "six"
159+
version = "1.15.0"
160+
description = "Python 2 and 3 compatibility utilities"
161+
category = "main"
162+
optional = false
163+
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
164+
165+
[[package.files]]
166+
file = "six-1.15.0-py2.py3-none-any.whl"
167+
hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
168+
169+
[[package.files]]
170+
file = "six-1.15.0.tar.gz"
171+
hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"
172+
173+
[[package]]
174+
name = "urllib3"
175+
version = "1.26.3"
176+
description = "HTTP library with thread-safe connection pooling, file post, and more."
177+
category = "main"
178+
optional = false
179+
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
180+
181+
[[package.files]]
182+
file = "urllib3-1.26.3-py2.py3-none-any.whl"
183+
hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"
184+
185+
[[package.files]]
186+
file = "urllib3-1.26.3.tar.gz"
187+
hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"
188+
189+
[package.extras]
190+
brotli = ["brotlipy (>=0.6.0)"]
191+
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
192+
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
193+
194+
[[package]]
195+
name = "websocket-client"
196+
version = "0.58.0"
197+
description = "WebSocket client for Python with low level API options"
198+
category = "main"
199+
optional = false
200+
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
201+
202+
[[package.files]]
203+
file = "websocket_client-0.58.0-py2.py3-none-any.whl"
204+
hash = "sha256:44b5df8f08c74c3d82d28100fdc81f4536809ce98a17f0757557813275fbb663"
205+
206+
[[package.files]]
207+
file = "websocket_client-0.58.0.tar.gz"
208+
hash = "sha256:63509b41d158ae5b7f67eb4ad20fecbb4eee99434e73e140354dc3ff8e09716f"
209+
210+
[package.dependencies]
211+
six = "*"
212+
213+
[metadata]
214+
lock-version = "2.0"
215+
python-versions = "^3.8"
216+
content-hash = "0cd068218f235c162f7b74bc8faf4ce3387b82daee1c1bb7a97af034f27ee116"

0 commit comments

Comments
 (0)