Skip to content

Commit 09f5b5d

Browse files
Merge pull request #54 from TuriJ95/issue-52
fixed extra requirements
2 parents bf63755 + f597cf9 commit 09f5b5d

File tree

3 files changed

+86
-56
lines changed

3 files changed

+86
-56
lines changed

licensecheck/get_deps.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"""
33
from __future__ import annotations
44

5+
import re
56
from importlib import metadata
67
from pathlib import Path
78
from typing import Any
@@ -56,8 +57,10 @@ def _doGetReqs(
5657
using: str, extras: str | None, pyproject: dict[str, Any], requirementsPaths: list[Path]
5758
) -> set[ucstr]:
5859
resolveReq = lambda req: ucstr(pkg_resources.Requirement.parse(req).project_name)
60+
resolveExtraReq = lambda extra_req: re.sub("extra == ", "", re.findall(r"extra == '.*?'",extra_req)[0].replace("'", "")) if len(re.findall(r"extra == '.*?'",extra_req)) > 0 else None
5961

6062
reqs = set()
63+
extras_reqs = dict()
6164

6265
if using == "poetry":
6366
try:
@@ -75,8 +78,12 @@ def _doGetReqs(
7578
reqLists.append(project.get("dev-dependencies", {}))
7679
for reqList in reqLists:
7780
for req in reqList:
78-
reqs.add(resolveReq(req))
79-
81+
try:
82+
extra = re.search(r'(?<=\[)(.*?)(?=\])',req).group(0)
83+
extras_reqs[resolveReq(req)] = extra
84+
reqs.add(f"{resolveReq(req)}[{extra}]")
85+
except (KeyError, AttributeError):
86+
reqs.add(resolveReq(req))
8087
# PEP631 (hatch)
8188
if using == "PEP631":
8289
try:
@@ -90,7 +97,12 @@ def _doGetReqs(
9097
reqLists.extend(project["optional-dependencies"][x] for x in extras.split(";"))
9198
for reqList in reqLists:
9299
for req in reqList:
93-
reqs.add(resolveReq(req))
100+
try:
101+
extra = re.search(r'(?<=\[)(.*?)(?=\])',req).group(0)
102+
extras_reqs[resolveReq(req)] = extra
103+
reqs.add(f"{resolveReq(req)}[{extra}]")
104+
except (KeyError, AttributeError):
105+
reqs.add(resolveReq(req))
94106

95107
# Requirements
96108
if using == "requirements":
@@ -100,7 +112,12 @@ def _doGetReqs(
100112

101113
with open(reqPath, encoding="utf-8") as requirementsTxt:
102114
for req in requirements.parse(requirementsTxt):
103-
reqs.add(str(req.name).lower())
115+
if len(req.extras)>0:
116+
extras_reqs[resolveReq(req.name)] = req.extras
117+
for extra in req.extras:
118+
reqs.add(f"{resolveReq(req.name)}[{extra}]")
119+
else:
120+
reqs.add(resolveReq(req.name))
104121

105122
try:
106123
reqs.remove("PYTHON")
@@ -115,16 +132,19 @@ def _doGetReqs(
115132
for req in [resolveReq(req) for req in pkgMetadata.get_all("Requires-Dist") or []]:
116133
requirementsWithDeps.add(req)
117134
except metadata.PackageNotFoundError:
118-
request = session.get(f"https://pypi.org/pypi/{requirement}/json", timeout=60)
135+
request = session.get(f"https://pypi.org/pypi/{requirement.split('[')[0]}/json", timeout=60)
119136
response = request.json()
120137
try:
121-
for req in [resolveReq(req) for req in response["info"]["requires_dist"]]:
122-
requirementsWithDeps.add(req)
138+
for dependency in response["info"]["requires_dist"]:
139+
if resolveExtraReq(dependency) is not None:
140+
if (resolveReq(requirement) in extras_reqs.keys()) and (resolveExtraReq(dependency) in extras_reqs[resolveReq(requirement)]):
141+
requirementsWithDeps.add(resolveReq(dependency))
142+
else:
143+
requirementsWithDeps.add(resolveReq(dependency))
123144
except (KeyError, TypeError):
124145
pass
125146

126-
return requirementsWithDeps
127-
147+
return {r.split('[')[0] for r in requirementsWithDeps}
128148

129149
def getDepsWithLicenses(
130150
using: str,

tests/data/test_requirements.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#
2+
# This file is autogenerated by pip-compile with Python 3.10
3+
# by the following command:
4+
#
5+
# pip-compile --no-emit-index-url --output-file=requirements/tmp.txt subset.in
6+
#
7+
8+
pandas[excel]==2.0.3
9+
10+
-c requirements_dev.txt
11+
12+
# The following packages are considered to be unsafe in a requirements file:
13+
# setuptools

tests/test_get_deps.py

Lines changed: 44 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,58 +7,55 @@
77
THISDIR = Path(__file__).resolve().parent
88

99

10-
def test_doGetReqs():
10+
def test_doGetReqs_PEP631():
1111

1212
using = "PEP631"
1313
extras = "socks"
1414
pyproject = tomli.loads((THISDIR / "data/pep631_socks.toml").read_text(encoding="utf-8"))
1515
requirementsPaths = []
1616

1717
assert get_deps._doGetReqs(using, extras, pyproject, requirementsPaths) == {
18-
"ATTRS",
19-
"BACKPORTS.SHUTIL-GET-TERMINAL-SIZE",
20-
"BACKPORTS.SSL-MATCH-HOSTNAME",
21-
"CACHED-PROPERTY",
22-
"CERTIFI",
23-
"CHARDET",
24-
"CHARSET-NORMALIZER",
25-
"CLICK",
26-
"COLORAMA",
27-
"DISTRO",
28-
"DOCKER",
29-
"DOCKERPTY",
30-
"DOCOPT",
31-
"ENUM34",
32-
"FQDN",
33-
"IDNA",
34-
"IMPORTLIB-RESOURCES",
35-
"IPADDRESS",
36-
"ISODURATION",
37-
"JSONPOINTER",
38-
"JSONSCHEMA",
39-
"JSONSCHEMA-SPECIFICATIONS",
40-
"PACKAGING",
41-
"PARAMIKO",
42-
"PKGUTIL-RESOLVE-NAME",
43-
"PYSOCKS",
44-
"PYTHON-DOTENV",
45-
"PYTHON-SOCKS",
46-
"PYWIN32",
47-
"PYYAML",
48-
"REFERENCING",
49-
"REQUESTS",
50-
"RFC3339-VALIDATOR",
51-
"RFC3986-VALIDATOR",
52-
"RFC3987",
53-
"RPDS-PY",
54-
"SPHINX",
55-
"SPHINX-RTD-THEME",
56-
"SUBPROCESS32",
57-
"TEXTTABLE",
58-
"URI-TEMPLATE",
59-
"URLLIB3",
60-
"WEBCOLORS",
61-
"WEBSOCKET-CLIENT",
62-
"WEBSOCKETS",
63-
"WSACCEL",
18+
'DOCKERPTY',
19+
'PACKAGING',
20+
'ATTRS',
21+
'JSONSCHEMA',
22+
'PYYAML',
23+
'PYSOCKS',
24+
'CERTIFI',
25+
'ENUM34',
26+
'DOCKER',
27+
'TEXTTABLE',
28+
'PYWIN32',
29+
'JSONSCHEMA-SPECIFICATIONS',
30+
'IPADDRESS',
31+
'PKGUTIL-RESOLVE-NAME',
32+
'DOCOPT',
33+
'BACKPORTS.SSL-MATCH-HOSTNAME',
34+
'PARAMIKO',
35+
'IDNA',
36+
'COLORAMA',
37+
'IMPORTLIB-RESOURCES',
38+
'CACHED-PROPERTY',
39+
'DISTRO',
40+
'BACKPORTS.SHUTIL-GET-TERMINAL-SIZE',
41+
'CHARSET-NORMALIZER',
42+
'URLLIB3',
43+
'WEBSOCKET-CLIENT',
44+
'RPDS-PY',
45+
'SUBPROCESS32',
46+
'REQUESTS',
47+
'REFERENCING',
48+
'CHARDET',
49+
'PYTHON-DOTENV'
6450
}
51+
52+
def test_doGetReqs_requirements():
53+
54+
using = "requirements"
55+
extras = f"{THISDIR}/data/test_requirements.txt"
56+
pyproject = {}
57+
requirementsPaths = [Path(f"{THISDIR}/data/test_requirements.txt")]
58+
deps = get_deps._doGetReqs(using, extras, pyproject, requirementsPaths)
59+
assert deps == {'NUMPY', 'ODFPY', 'OPENPYXL', 'PANDAS', 'PYTHON-DATEUTIL', 'PYTZ', 'PYXLSB', 'TZDATA', 'XLRD', 'XLSXWRITER'}
60+
assert 'OPENPYXL' in deps
61+
assert 'XARRAY' not in deps #xarray is an optional dependecy of pandas associated with 'computation' key that is not tracked in test_requirements.txt

0 commit comments

Comments
 (0)