Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ The detector supports parsing the following lockfiles:
| `poetry.lock` | `PyPI` | `poetry` |
| `uv.lock` | `PyPI` | `uv` |
| `Pipfile.lock` | `PyPI` | `pipenv` |
| `pylock.toml` | `PyPI` | (none) |
| `pdm.lock` | `PyPI` | `pdm` |
| `pubspec.lock` | `Pub` | `pub` |
| `pom.xml`\* | `Maven` | `maven` |
Expand Down
1 change: 1 addition & 0 deletions __snapshots__/main_test.snap
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Don't know how to parse files as "my-file" - supported values are:
poetry.lock
pom.xml
pubspec.lock
pylock.toml
renv.lock
requirements.txt
uv.lock
Expand Down
4 changes: 2 additions & 2 deletions pkg/lockfile/ecosystems_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ func TestKnownEcosystems(t *testing.T) {
expectedCount := numberOfLockfileParsers(t)

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

ecosystems := lockfile.KnownEcosystems()

Expand Down
48 changes: 48 additions & 0 deletions pkg/lockfile/parse-pylock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package lockfile

import (
"fmt"
"os"

"github.com/BurntSushi/toml"
)

type PylockPackage struct {
Name string `toml:"name"`
Version string `toml:"version"`
}

type PylockLockfile struct {
Packages []PylockPackage `toml:"packages"`
}

const PylockEcosystem = PipEcosystem

func ParsePylock(pathToLockfile string) ([]PackageDetails, error) {
var parsedLockfile *PylockLockfile

lockfileContents, err := os.ReadFile(pathToLockfile)

if err != nil {
return []PackageDetails{}, fmt.Errorf("could not read %s: %w", pathToLockfile, err)
}

err = toml.Unmarshal(lockfileContents, &parsedLockfile)

if err != nil {
return []PackageDetails{}, fmt.Errorf("could not parse %s: %w", pathToLockfile, err)
}

packages := make([]PackageDetails, 0, len(parsedLockfile.Packages))

for _, pkg := range parsedLockfile.Packages {
packages = append(packages, PackageDetails{
Name: pkg.Name,
Version: pkg.Version,
Ecosystem: PylockEcosystem,
CompareAs: PylockEcosystem,
})
}

return packages, nil
}
103 changes: 103 additions & 0 deletions pkg/lockfile/parse-pylock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package lockfile_test

import (
"testing"

"github.com/g-rath/osv-detector/pkg/lockfile"
)

func TestParsePylock_FileDoesNotExist(t *testing.T) {
t.Parallel()

packages, err := lockfile.ParsePylock("testdata/pylock/does-not-exist")

expectErrContaining(t, err, "could not read")
expectPackages(t, packages, []lockfile.PackageDetails{})
}

func TestParsePylock_InvalidToml(t *testing.T) {
t.Parallel()

packages, err := lockfile.ParsePylock("testdata/pylock/not-toml.txt")

expectErrContaining(t, err, "could not parse")
expectPackages(t, packages, []lockfile.PackageDetails{})
}

func TestParsePylock_NoPackages(t *testing.T) {
t.Skip("todo: need a fixture")

t.Parallel()

packages, err := lockfile.ParsePylock("testdata/pylock/empty.toml")

if err != nil {
t.Errorf("Got unexpected error: %v", err)
}

expectPackages(t, packages, []lockfile.PackageDetails{})
}

func TestParsePylock_OnePackage(t *testing.T) {
t.Skip("todo: need a fixture")

t.Parallel()

packages, err := lockfile.ParsePylock("testdata/pylock/one-package.toml")

if err != nil {
t.Errorf("Got unexpected error: %v", err)
}

expectPackages(t, packages, []lockfile.PackageDetails{
// ...
})
}

func TestParsePylock_TwoPackages(t *testing.T) {
t.Skip("todo: need a fixture")

t.Parallel()

packages, err := lockfile.ParsePylock("testdata/pylock/two-packages.toml")

if err != nil {
t.Errorf("Got unexpected error: %v", err)
}

expectPackages(t, packages, []lockfile.PackageDetails{
// ...
})
}

func TestParsePylock_Example(t *testing.T) {
t.Parallel()

// from https://peps.python.org/pep-0751/#example
packages, err := lockfile.ParsePylock("testdata/pylock/example.toml")

if err != nil {
t.Errorf("Got unexpected error: %v", err)
}

expectPackages(t, packages, []lockfile.PackageDetails{
{
Name: "attrs",
Version: "25.1.0",
Ecosystem: lockfile.PylockEcosystem,
CompareAs: lockfile.PylockEcosystem,
},
{
Name: "cattrs",
Version: "24.1.2",
Ecosystem: lockfile.PylockEcosystem,
CompareAs: lockfile.PylockEcosystem,
},
{
Name: "numpy",
Version: "2.2.3",
Ecosystem: lockfile.PylockEcosystem,
CompareAs: lockfile.PylockEcosystem,
},
})
}
1 change: 1 addition & 0 deletions pkg/lockfile/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var parsers = map[string]PackageDetailsParser{
"pdm.lock": ParsePdmLock,
"pnpm-lock.yaml": ParsePnpmLock,
"poetry.lock": ParsePoetryLock,
"pylock.toml": ParsePylock,
"Pipfile.lock": ParsePipenvLock,
"pom.xml": ParseMavenLock,
"pubspec.lock": ParsePubspecLock,
Expand Down
1 change: 1 addition & 0 deletions pkg/lockfile/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ func TestParse_FindsExpectedParsers(t *testing.T) {
"pom.xml",
"pdm.lock",
"poetry.lock",
"pylock.toml",
"uv.lock",
"Pipfile.lock",
"pubspec.lock",
Expand Down
41 changes: 41 additions & 0 deletions pkg/lockfile/testdata/pylock/example.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
lock-version = '1.0'
environments = ["sys_platform == 'win32'", "sys_platform == 'linux'"]
requires-python = '==3.12'
created-by = 'mousebender'

[[packages]]
name = 'attrs'
version = '25.1.0'
requires-python = '>=3.8'
wheels = [
{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'}},
]
[[packages.attestation-identities]]
environment = 'release-pypi'
kind = 'GitHub'
repository = 'python-attrs/attrs'
workflow = 'pypi-package.yml'

[[packages]]
name = 'cattrs'
version = '24.1.2'
requires-python = '>=3.8'
dependencies = [
{name = 'attrs'},
]
wheels = [
{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'}},
]

[[packages]]
name = 'numpy'
version = '2.2.3'
requires-python = '>=3.10'
wheels = [
{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'}},
{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'}},
]

[tool.mousebender]
command = ['.', 'lock', '--platform', 'cpython3.12-windows-x64', '--platform', 'cpython3.12-manylinux2014-x64', 'cattrs', 'numpy']
run-on = 2025-03-06T12:28:57.760769
1 change: 1 addition & 0 deletions pkg/lockfile/testdata/pylock/not-toml.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
this is not valid toml! (I think)
Loading