Skip to content

Commit d68a8bd

Browse files
committed
fix: split pip show output on the exact package separator
`\n---` also matches dashed lines inside a package's License (pytest-django), so the block got shredded and indexing died.
1 parent 8b60bbc commit d68a8bd

3 files changed

Lines changed: 87 additions & 1 deletion

File tree

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import assert from 'assert';
2+
import PythonPackage from './PythonPackage';
3+
4+
// Real `pip show -f` output for two packages, taken from issue #173. pytest-django
5+
// puts its full license into the `License:` metadata field, so the block contains
6+
// dashed rule lines (`----...`). Those start with `\n---`, which is what the old
7+
// split matched, shattering pytest-django across several blocks and making
8+
// PythonPackage.fromPipShow throw "Unexpected. Thought I should be getting files now".
9+
const pipShowOutput = `Name: pytest-django
10+
Version: 4.11.1
11+
Summary: A Django plugin for pytest.
12+
Home-page:
13+
Author:
14+
Author-email: Andreas Pelme <andreas@pelme.se>
15+
License: pytest-django is released under the BSD (3-clause) license
16+
----------------------------------------------------------
17+
Copyright (c) 2015-2018, pytest-django authors (see AUTHORS file)
18+
All rights reserved.
19+
20+
Redistribution and use in source and binary forms, with or without
21+
modification, are permitted provided that the following conditions are met:
22+
23+
* Redistributions of source code must retain the above copyright notice, this
24+
list of conditions and the following disclaimer.
25+
26+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
27+
ANY EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED.
28+
29+
30+
This version of pytest-django is a fork of pytest_django created by Ben Firshman.
31+
---------------------------------------------------------------------------------
32+
Copyright (c) 2009, Ben Firshman
33+
All rights reserved.
34+
35+
Redistribution and use in source and binary forms, with or without
36+
modification, are permitted provided that the following conditions are met.
37+
38+
Location: /home/leni/.cache/pypoetry/virtualenvs/test-infra-back-lSRTar0T-py3.12/lib/python3.12/site-packages
39+
Requires: pytest
40+
Required-by:
41+
Files:
42+
pytest_django-4.11.1.dist-info/INSTALLER
43+
pytest_django-4.11.1.dist-info/METADATA
44+
pytest_django-4.11.1.dist-info/RECORD
45+
pytest_django/__init__.py
46+
pytest_django/fixtures.py
47+
pytest_django/plugin.py
48+
pytest_django/py.typed
49+
---
50+
Name: attrs
51+
Version: 25.3.0
52+
Summary: Classes Without Boilerplate
53+
Location: /home/leni/.cache/pypoetry/virtualenvs/test-infra-back-lSRTar0T-py3.12/lib/python3.12/site-packages
54+
Requires:
55+
Required-by: pytest-django
56+
Files:
57+
attr/__init__.py
58+
attrs/__init__.py
59+
`;
60+
61+
test('splitPipShowBlocks keeps a package whose License contains dashed rules in one block', () => {
62+
const blocks = PythonPackage.splitPipShowBlocks(pipShowOutput);
63+
assert.strictEqual(blocks.length, 2);
64+
65+
const packages = blocks.map((block) => PythonPackage.fromPipShow(block));
66+
assert.deepStrictEqual(
67+
packages.map((pkg) => pkg.name),
68+
['pytest-django', 'attrs']
69+
);
70+
71+
assert.strictEqual(packages[0].version, '4.11.1');
72+
assert.ok(packages[0].files.includes('pytest_django/__init__.py'));
73+
assert.ok(packages[0].files.includes('pytest_django/plugin.py'));
74+
assert.ok(!packages[0].files.some((file) => file.includes('.dist-info')));
75+
76+
assert.strictEqual(packages[1].version, '25.3.0');
77+
assert.deepStrictEqual(packages[1].files, ['attr/__init__.py', 'attrs/__init__.py']);
78+
});

packages/pyright-scip/src/virtualenv/PythonPackage.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,12 @@ export default class PythonPackage {
4949

5050
return new PythonPackage(name, version, files);
5151
}
52+
53+
// pip separates each `pip show` block with a line containing only `---`.
54+
// Match that exact separator rather than a bare `\n---`, which also shows up
55+
// inside a package's License text (e.g. pytest-django's dashed rule lines) and
56+
// would split a single package across several blocks.
57+
static splitPipShowBlocks(output: string): string[] {
58+
return output.split(/\r?\n---\r?\n/).filter((block) => block.trim());
59+
}
5260
}

packages/pyright-scip/src/virtualenv/environment.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ function pipBulkShow(names: string[]): PipBulkShowResult {
180180

181181
return {
182182
success: true,
183-
data: result.stdout.split('\n---').filter((pkg) => pkg.trim()),
183+
data: PythonPackage.splitPipShowBlocks(result.stdout),
184184
};
185185
}
186186

0 commit comments

Comments
 (0)