Skip to content

Commit f2d42e6

Browse files
committed
test parse.gather calls NativeInfer.gather for lock files and sboms
1 parent 10d849f commit f2d42e6

5 files changed

Lines changed: 148 additions & 20 deletions

File tree

‎depgather/native.py‎

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,24 @@
1818
import tomli
1919
from packaging.requirements import Requirement
2020
from packaging.utils import canonicalize_name
21+
from pydantic import BaseModel
2122

2223
from depgather.interface import DepGatherInterface
2324
from depgather.models.cyclonedx import Bom
2425
from depgather.models.spdx import SPDX
2526
from depgather.utils import c14n_reqs, conditional_log, sanitize
2627

2728

29+
class ReqContainer(BaseModel):
30+
name: str
31+
version: str | None = None
32+
33+
def as_requirement(self) -> str:
34+
if self.version:
35+
return f"{self.name}=={self.version}"
36+
return self.name
37+
38+
2839
class NativeInferState:
2940
def __init__(self) -> None:
3041
self.reqs: dict[str, Requirement] = {}
@@ -114,31 +125,34 @@ def gather_lock_requirements(
114125
self, pyproject: dict[str, Any], pakagekey: str = "package"
115126
) -> bool:
116127
for pkg in pyproject[pakagekey]:
117-
if pkg.get("version"):
118-
self.appendRequirement(f"{pkg['name']}=={pkg['version']}")
119-
else:
120-
self.appendRequirement(pkg["name"])
128+
self.appendRequirement(
129+
ReqContainer(name=pkg["name"], version=pkg.get("version")).as_requirement()
130+
)
121131

122132
return True
123133

124134
def gather_cyclonedx_requirements(self, sbom: dict[str, Any]) -> bool:
125135
sbom_: Bom = Bom.model_validate(sbom)
126-
for component in sbom_.components:
127-
if "pkg:pypi/" in component.purl:
128-
if component.version:
129-
self.appendRequirement(f"{component.name}=={component.version}")
130-
else:
131-
self.appendRequirement(component.name)
136+
for x in sbom_.components:
137+
if "pkg:pypi/" in x.purl:
138+
self.appendRequirement(
139+
ReqContainer(
140+
name=x.name,
141+
version=x.version,
142+
).as_requirement()
143+
)
132144

133145
return True
134146

135147
def gather_spdx_requirements(self, sbom: dict[str, Any]) -> bool:
136148
sbom_: SPDX = SPDX.model_validate(sbom)
137-
for package in sbom_.packages:
138-
if package.version_info:
139-
self.appendRequirement(f"{package.name}=={package.version_info}")
140-
else:
141-
self.appendRequirement(package.name)
149+
for x in sbom_.packages:
150+
self.appendRequirement(
151+
ReqContainer(
152+
name=x.name,
153+
version=x.version_info,
154+
).as_requirement()
155+
)
142156

143157
return True
144158

‎depgather/pip_cli.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ def gather(
5353

5454
requirementsPathName = requirementsPath.as_posix()
5555

56-
if requirementsPathName.endswith((".lock", ".toml")):
57-
msg = "pyproject toml, and lock files are not supported."
56+
if requirementsPathName.endswith((".lock", ".toml", ".json")):
57+
msg = "pyproject toml, and lock files, and sboms are not supported."
5858
raise RuntimeError(msg)
5959

6060
skip_names = {canonicalize_name(x) for x in skipDependencies}

‎depgather/uv_cli.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ def gather(
4343

4444
requirementsPathName = requirementsPath.as_posix()
4545

46-
if requirementsPathName.endswith((".lock", "lock.toml")):
47-
msg = "lock files are not supported, use `NativeInfer.gather`."
46+
if requirementsPathName.endswith((".lock", "lock.toml", ".json")):
47+
msg = "lock files, and sboms are not supported, use `NativeInfer.gather`."
4848
raise RuntimeError(msg)
4949

5050
if not requirementsPathName.endswith("pyproject.toml") and requirementsPathName.endswith(

‎tests/test_parse.py‎

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
from pathlib import Path
2+
from unittest.mock import MagicMock
23

34
import pytest
45

6+
from depgather.native import NativeInfer
57
from depgather.parse import ResolverName, gather
68
from depgather.utils import enable_verbose
9+
from tests.utils import assert_eq_packages
710

811
THISDIR = Path(__file__).resolve().parent
912
enable_verbose()
@@ -36,3 +39,114 @@ def test_lockfiles(resolvername: ResolverName) -> None:
3639
requirementsPath=requirementsPath,
3740
preferred_resolver=resolvername,
3841
)
42+
43+
44+
@pytest.mark.parametrize(
45+
("lockfile", "extradelta"),
46+
[
47+
("poetry.lock", {"pip", "uv", "pygments"}),
48+
("uv_.lock", {"pip", "uv", "depgather"}),
49+
("pylock.toml", {"depgather"}),
50+
],
51+
)
52+
def test_lockfiles_fav_uv(
53+
monkeypatch: pytest.MonkeyPatch, lockfile: str, extradelta: set[str]
54+
) -> None:
55+
"""This takes the minimal lockfiles from the following commands as applied to this library
56+
57+
The commands to generate these files are as follows:
58+
uvx poetry lock
59+
uv export --format pylock.toml > pylock
60+
uv sync
61+
"""
62+
63+
mock = MagicMock(wraps=NativeInfer.gather)
64+
65+
monkeypatch.setattr(
66+
NativeInfer,
67+
"gather",
68+
mock,
69+
)
70+
71+
requirementsPath: Path = THISDIR / "data/example1" / lockfile
72+
skipDependencies: set[str] = {"TOSKIP"}
73+
74+
deps = gather(
75+
skipDependencies=skipDependencies,
76+
extras=set(),
77+
groups=set(),
78+
requirementsPath=requirementsPath,
79+
preferred_resolver=ResolverName.UV,
80+
)
81+
82+
mock.assert_called_once()
83+
84+
expected = {
85+
"basedpyright",
86+
"colorama",
87+
"coverage",
88+
"iniconfig",
89+
"nodejs-wheel-binaries",
90+
"packaging",
91+
"pluggy",
92+
"pytest",
93+
"requirements-parser",
94+
"ruff",
95+
"tomli",
96+
}
97+
98+
assert len(deps) == len(expected) + len(extradelta)
99+
100+
# assert depgather.native.NativeInfer.gather( called
101+
102+
assert_eq_packages(deps, expected | extradelta)
103+
104+
105+
@pytest.mark.parametrize(
106+
("sbom"),
107+
[
108+
("cyclonedx.json"),
109+
("spdx.json"),
110+
],
111+
)
112+
def test_sbom_fav_uv(monkeypatch: pytest.MonkeyPatch, sbom: str) -> None:
113+
"""This takes the minimal sbom from as applied to depgather (with the dev group,
114+
no extras)
115+
"""
116+
117+
mock = MagicMock(wraps=NativeInfer.gather)
118+
119+
monkeypatch.setattr(
120+
NativeInfer,
121+
"gather",
122+
mock,
123+
)
124+
sbomPath: Path = THISDIR / "data" / sbom
125+
126+
deps = gather(
127+
requirementsPath=sbomPath,
128+
skipDependencies=set(),
129+
extras=set(),
130+
groups={"dev"},
131+
preferred_resolver=ResolverName.UV,
132+
)
133+
134+
mock.assert_called_once()
135+
136+
expected = {
137+
"basedpyright",
138+
"colorama", # windows only requirement
139+
"coverage",
140+
"iniconfig",
141+
"nodejs-wheel-binaries",
142+
"packaging",
143+
"pluggy",
144+
"pytest",
145+
"requirements-parser",
146+
"ruff",
147+
"tomli",
148+
"loguru",
149+
"win32-setctime",
150+
}
151+
152+
assert_eq_packages(deps, expected)

‎tests/test_uv_cli.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def test_lockfiles(lockfile: str) -> None:
3737
requirementsPath: Path = THISDIR / "data/example1" / lockfile
3838
skipDependencies: set[str] = {"TOSKIP"}
3939

40-
with pytest.raises(RuntimeError, match="lock files are not supported"):
40+
with pytest.raises(RuntimeError, match="lock files, and sboms are not supported"):
4141
UvCli.gather(
4242
skipDependencies=skipDependencies,
4343
extras=set(),

0 commit comments

Comments
 (0)