Skip to content

Commit b4f3191

Browse files
committed
check for multiple roots
Signed-off-by: nikpivkin <nikita.pivkin@smartforce.io>
1 parent e67015d commit b4f3191

File tree

3 files changed

+54
-11
lines changed

3 files changed

+54
-11
lines changed

pkg/dependency/parser/python/uv/parse.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ func (l Lock) directDeps(root Package) map[string]struct{} {
3030
return deps
3131
}
3232

33-
func (l Lock) prodDeps(root Package) map[string]struct{} {
34-
packages := l.packages()
33+
func prodDeps(root Package, packages map[string]Package) map[string]struct{} {
3534
visited := make(map[string]struct{})
3635
walkPackageDeps(root, packages, visited)
3736
return visited
@@ -51,14 +50,25 @@ func walkPackageDeps(pkg Package, packages map[string]Package, visited map[strin
5150
}
5251
}
5352

54-
func (l Lock) root() Package {
53+
func (l Lock) root() (Package, error) {
54+
var pkgs []Package
5555
for _, pkg := range l.Packages {
5656
if pkg.isRoot() {
57-
return pkg
57+
pkgs = append(pkgs, pkg)
5858
}
5959
}
6060

61-
return Package{}
61+
if len(pkgs) > 1 {
62+
return Package{}, xerrors.New("uv lockfile contains multiple root projects")
63+
}
64+
65+
// lock file must include root package
66+
// cf. https://github.com/astral-sh/uv/blob/f80ddf10b63c3e7b421ca4658e63f97db1e0378c/crates/uv/src/commands/project/lock.rs#L933-L936
67+
if len(pkgs) != 1 {
68+
return Package{}, xerrors.New("uv lockfile does not contain a root package.")
69+
}
70+
71+
return pkgs[0], nil
6272
}
6373

6474
type Package struct {
@@ -94,16 +104,14 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc
94104
return nil, nil, xerrors.Errorf("failed to decode uv lock file: %w", err)
95105
}
96106

97-
rootPackage := lock.root()
98-
// lock file must include root package
99-
// cf. https://github.com/astral-sh/uv/blob/f80ddf10b63c3e7b421ca4658e63f97db1e0378c/crates/uv/src/commands/project/lock.rs#L933-L936
100-
if rootPackage.Name == "" {
101-
return nil, nil, xerrors.New("uv lockfile does not contain a root package.")
107+
rootPackage, err := lock.root()
108+
if err != nil {
109+
return nil, nil, err
102110
}
103111

104112
packages := lock.packages()
105113
directDeps := lock.directDeps(rootPackage)
106-
prodDeps := lock.prodDeps(rootPackage)
114+
prodDeps := prodDeps(rootPackage, packages)
107115

108116
var (
109117
pkgs ftypes.Packages

pkg/dependency/parser/python/uv/parse_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ func TestParser_Parse(t *testing.T) {
3535
file: "testdata/uv_without_root.lock",
3636
wantErr: "uv lockfile does not contain a root package",
3737
},
38+
{
39+
name: "multiple roots",
40+
file: "testdata/uv_multiple_roots.lock",
41+
wantErr: "uv lockfile contains multiple root projects",
42+
},
3843
}
3944

4045
for _, tt := range tests {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
version = 1
2+
requires-python = ">=3.11"
3+
4+
[[package]]
5+
name = "asyncio"
6+
version = "3.4.3"
7+
source = { registry = "https://pypi.org/simple" }
8+
sdist = { url = "https://files.pythonhosted.org/packages/da/54/054bafaf2c0fb8473d423743e191fcdf49b2c1fd5e9af3524efbe097bafd/asyncio-3.4.3.tar.gz", hash = "sha256:83360ff8bc97980e4ff25c964c7bd3923d333d177aa4f7fb736b019f26c7cb41", size = 204411 }
9+
wheels = [
10+
{ url = "https://files.pythonhosted.org/packages/22/74/07679c5b9f98a7cb0fc147b1ef1cc1853bc07a4eb9cb5731e24732c5f773/asyncio-3.4.3-py3-none-any.whl", hash = "sha256:c4d18b22701821de07bd6aea8b53d21449ec0ec5680645e5317062ea21817d2d", size = 101767 },
11+
]
12+
13+
[[package]]
14+
name = "foo"
15+
version = "0.1.0"
16+
source = { virtual = "." }
17+
dependencies = [
18+
{ name = "asyncio" },
19+
]
20+
21+
[package.metadata]
22+
requires-dist = [{ name = "asyncio", specifier = "==3.4.3" }]
23+
24+
[[package]]
25+
name = "bar"
26+
version = "0.1.0"
27+
source = { virtual = "." }
28+
dependencies = [
29+
{ name = "asyncio" },
30+
]

0 commit comments

Comments
 (0)