Skip to content

Commit fd5296d

Browse files
committed
feat: support parsing pylock.toml files
1 parent b8ee28e commit fd5296d

File tree

9 files changed

+199
-2
lines changed

9 files changed

+199
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ The detector supports parsing the following lockfiles:
7070
| `poetry.lock` | `PyPI` | `poetry` |
7171
| `uv.lock` | `PyPI` | `uv` |
7272
| `Pipfile.lock` | `PyPI` | `pipenv` |
73+
| `pylock.toml` | `PyPI` | (none) |
7374
| `pdm.lock` | `PyPI` | `pdm` |
7475
| `pubspec.lock` | `Pub` | `pub` |
7576
| `pom.xml`\* | `Maven` | `maven` |

__snapshots__/main_test.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Don't know how to parse files as "my-file" - supported values are:
3939
poetry.lock
4040
pom.xml
4141
pubspec.lock
42+
pylock.toml
4243
renv.lock
4344
requirements.txt
4445
uv.lock

pkg/lockfile/ecosystems_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ func TestKnownEcosystems(t *testing.T) {
3535
expectedCount := numberOfLockfileParsers(t)
3636

3737
// - npm, yarn, bun, and pnpm,
38-
// - pip, poetry, uv, pdm and pipenv,
38+
// - pip, poetry, uv, pdm, pylock, and pipenv,
3939
// - maven and gradle,
4040
// all use the same ecosystem so "ignore" those parsers in the count
41-
expectedCount -= 8
41+
expectedCount -= 9
4242

4343
ecosystems := lockfile.KnownEcosystems()
4444

pkg/lockfile/parse-pylock.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package lockfile
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"github.com/BurntSushi/toml"
8+
)
9+
10+
type PylockPackage struct {
11+
Name string `toml:"name"`
12+
Version string `toml:"version"`
13+
}
14+
15+
type PylockLockfile struct {
16+
Packages []PylockPackage `toml:"packages"`
17+
}
18+
19+
const PylockEcosystem = PipEcosystem
20+
21+
func ParsePylock(pathToLockfile string) ([]PackageDetails, error) {
22+
var parsedLockfile *PylockLockfile
23+
24+
lockfileContents, err := os.ReadFile(pathToLockfile)
25+
26+
if err != nil {
27+
return []PackageDetails{}, fmt.Errorf("could not read %s: %w", pathToLockfile, err)
28+
}
29+
30+
err = toml.Unmarshal(lockfileContents, &parsedLockfile)
31+
32+
if err != nil {
33+
return []PackageDetails{}, fmt.Errorf("could not parse %s: %w", pathToLockfile, err)
34+
}
35+
36+
packages := make([]PackageDetails, 0, len(parsedLockfile.Packages))
37+
38+
for _, pkg := range parsedLockfile.Packages {
39+
packages = append(packages, PackageDetails{
40+
Name: pkg.Name,
41+
Version: pkg.Version,
42+
Ecosystem: PylockEcosystem,
43+
CompareAs: PylockEcosystem,
44+
})
45+
}
46+
47+
return packages, nil
48+
}

pkg/lockfile/parse-pylock_test.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package lockfile_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/g-rath/osv-detector/pkg/lockfile"
7+
)
8+
9+
func TestParsePylock_FileDoesNotExist(t *testing.T) {
10+
t.Parallel()
11+
12+
packages, err := lockfile.ParsePylock("testdata/pylock/does-not-exist")
13+
14+
expectErrContaining(t, err, "could not read")
15+
expectPackages(t, packages, []lockfile.PackageDetails{})
16+
}
17+
18+
func TestParsePylock_InvalidToml(t *testing.T) {
19+
t.Parallel()
20+
21+
packages, err := lockfile.ParsePylock("testdata/pylock/not-toml.txt")
22+
23+
expectErrContaining(t, err, "could not parse")
24+
expectPackages(t, packages, []lockfile.PackageDetails{})
25+
}
26+
27+
func TestParsePylock_NoPackages(t *testing.T) {
28+
t.Skip("todo: need a fixture")
29+
30+
t.Parallel()
31+
32+
packages, err := lockfile.ParsePylock("testdata/pylock/empty.toml")
33+
34+
if err != nil {
35+
t.Errorf("Got unexpected error: %v", err)
36+
}
37+
38+
expectPackages(t, packages, []lockfile.PackageDetails{})
39+
}
40+
41+
func TestParsePylock_OnePackage(t *testing.T) {
42+
t.Skip("todo: need a fixture")
43+
44+
t.Parallel()
45+
46+
packages, err := lockfile.ParsePylock("testdata/pylock/one-package.toml")
47+
48+
if err != nil {
49+
t.Errorf("Got unexpected error: %v", err)
50+
}
51+
52+
expectPackages(t, packages, []lockfile.PackageDetails{
53+
// ...
54+
})
55+
}
56+
57+
func TestParsePylock_TwoPackages(t *testing.T) {
58+
t.Skip("todo: need a fixture")
59+
60+
t.Parallel()
61+
62+
packages, err := lockfile.ParsePylock("testdata/pylock/two-packages.toml")
63+
64+
if err != nil {
65+
t.Errorf("Got unexpected error: %v", err)
66+
}
67+
68+
expectPackages(t, packages, []lockfile.PackageDetails{
69+
// ...
70+
})
71+
}
72+
73+
func TestParsePylock_Example(t *testing.T) {
74+
t.Parallel()
75+
76+
// from https://peps.python.org/pep-0751/#example
77+
packages, err := lockfile.ParsePylock("testdata/pylock/example.toml")
78+
79+
if err != nil {
80+
t.Errorf("Got unexpected error: %v", err)
81+
}
82+
83+
expectPackages(t, packages, []lockfile.PackageDetails{
84+
{
85+
Name: "attrs",
86+
Version: "25.1.0",
87+
Ecosystem: lockfile.PylockEcosystem,
88+
CompareAs: lockfile.PylockEcosystem,
89+
},
90+
{
91+
Name: "cattrs",
92+
Version: "24.1.2",
93+
Ecosystem: lockfile.PylockEcosystem,
94+
CompareAs: lockfile.PylockEcosystem,
95+
},
96+
{
97+
Name: "numpy",
98+
Version: "2.2.3",
99+
Ecosystem: lockfile.PylockEcosystem,
100+
CompareAs: lockfile.PylockEcosystem,
101+
},
102+
})
103+
}

pkg/lockfile/parse.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ var parsers = map[string]PackageDetailsParser{
2929
"pdm.lock": ParsePdmLock,
3030
"pnpm-lock.yaml": ParsePnpmLock,
3131
"poetry.lock": ParsePoetryLock,
32+
"pylock.toml": ParsePylock,
3233
"Pipfile.lock": ParsePipenvLock,
3334
"pom.xml": ParseMavenLock,
3435
"pubspec.lock": ParsePubspecLock,

pkg/lockfile/parse_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ func TestParse_FindsExpectedParsers(t *testing.T) {
110110
"pom.xml",
111111
"pdm.lock",
112112
"poetry.lock",
113+
"pylock.toml",
113114
"uv.lock",
114115
"Pipfile.lock",
115116
"pubspec.lock",
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
lock-version = '1.0'
2+
environments = ["sys_platform == 'win32'", "sys_platform == 'linux'"]
3+
requires-python = '==3.12'
4+
created-by = 'mousebender'
5+
6+
[[packages]]
7+
name = 'attrs'
8+
version = '25.1.0'
9+
requires-python = '>=3.8'
10+
wheels = [
11+
{name = 'attrs-25.1.0-py3-none-any.whl', upload-time = 2025-01-25T11:30:10.164985+00:00, url = 'https://files.pythonhosted.org/packages/fc/30/d4986a882011f9df997a55e6becd864812ccfcd821d64aac8570ee39f719/attrs-25.1.0-py3-none-any.whl', size = 63152, hashes = {sha256 = 'c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a'}},
12+
]
13+
[[packages.attestation-identities]]
14+
environment = 'release-pypi'
15+
kind = 'GitHub'
16+
repository = 'python-attrs/attrs'
17+
workflow = 'pypi-package.yml'
18+
19+
[[packages]]
20+
name = 'cattrs'
21+
version = '24.1.2'
22+
requires-python = '>=3.8'
23+
dependencies = [
24+
{name = 'attrs'},
25+
]
26+
wheels = [
27+
{name = 'cattrs-24.1.2-py3-none-any.whl', upload-time = 2024-09-22T14:58:34.812643+00:00, url = 'https://files.pythonhosted.org/packages/c8/d5/867e75361fc45f6de75fe277dd085627a9db5ebb511a87f27dc1396b5351/cattrs-24.1.2-py3-none-any.whl', size = 66446, hashes = {sha256 = '67c7495b760168d931a10233f979b28dc04daf853b30752246f4f8471c6d68d0'}},
28+
]
29+
30+
[[packages]]
31+
name = 'numpy'
32+
version = '2.2.3'
33+
requires-python = '>=3.10'
34+
wheels = [
35+
{name = 'numpy-2.2.3-cp312-cp312-win_amd64.whl', upload-time = 2025-02-13T16:51:21.821880+00:00, url = 'https://files.pythonhosted.org/packages/42/6e/55580a538116d16ae7c9aa17d4edd56e83f42126cb1dfe7a684da7925d2c/numpy-2.2.3-cp312-cp312-win_amd64.whl', size = 12626357, hashes = {sha256 = '83807d445817326b4bcdaaaf8e8e9f1753da04341eceec705c001ff342002e5d'}},
36+
{name = 'numpy-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl', upload-time = 2025-02-13T16:50:00.079662+00:00, url = 'https://files.pythonhosted.org/packages/39/04/78d2e7402fb479d893953fb78fa7045f7deb635ec095b6b4f0260223091a/numpy-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl', size = 16116679, hashes = {sha256 = '3b787adbf04b0db1967798dba8da1af07e387908ed1553a0d6e74c084d1ceafe'}},
37+
]
38+
39+
[tool.mousebender]
40+
command = ['.', 'lock', '--platform', 'cpython3.12-windows-x64', '--platform', 'cpython3.12-manylinux2014-x64', 'cattrs', 'numpy']
41+
run-on = 2025-03-06T12:28:57.760769
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
this is not valid toml! (I think)

0 commit comments

Comments
 (0)