Skip to content

Commit c64e5f6

Browse files
add nox task to output dependencies
1 parent 14be6eb commit c64e5f6

File tree

4 files changed

+188
-1955
lines changed

4 files changed

+188
-1955
lines changed
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
from __future__ import annotations
2+
3+
import json
4+
from collections import defaultdict
5+
from dataclasses import dataclass
6+
from inspect import cleandoc
7+
from pathlib import Path
8+
9+
import nox
10+
import tomlkit
11+
from nox import Session
12+
13+
14+
@dataclass(frozen=True)
15+
class Package:
16+
name: str
17+
package_link: str
18+
license: str
19+
version: str
20+
license_link: str
21+
22+
23+
def _dependencies(toml_str: str) -> dict[str, list]:
24+
toml = tomlkit.loads(toml_str)
25+
poetry = toml.get("tool", {}).get("poetry", {})
26+
dependencies: dict[str, list] = {}
27+
28+
packages = poetry.get("dependencies", {})
29+
dependencies["project"] = []
30+
for package in packages:
31+
dependencies["project"].append(package)
32+
33+
packages = poetry.get("dev", {}).get("dependencies", {})
34+
dependencies["dev"] = []
35+
for package in packages:
36+
dependencies["dependencies"].append(package)
37+
38+
groups = poetry.get("group", {})
39+
for group in groups:
40+
packages = groups.get(group, {}).get("dependencies")
41+
dependencies[group] = []
42+
for package in packages:
43+
dependencies[group].append(package)
44+
return dependencies
45+
46+
47+
def _licenses(session: Session) -> None:
48+
session.run(
49+
"poetry",
50+
"run",
51+
"pip-licenses",
52+
"--format=json",
53+
"--output-file=packages.json",
54+
"--with-system",
55+
"--with-urls",
56+
)
57+
58+
59+
def _normalize(_license: str) -> str:
60+
def is_multi_license(l):
61+
return ";" in l
62+
63+
def select_most_permissive(l: str) -> str:
64+
licenses = [_normalize(l.strip()) for l in l.split(";")]
65+
priority = defaultdict(
66+
lambda: 9999,
67+
{
68+
"Unlicensed": 0,
69+
"BSD": 1,
70+
"MIT": 2,
71+
"MPLv2": 3,
72+
"LGPLv2": 4,
73+
"GPLv2": 5,
74+
"GPLv3": 6,
75+
},
76+
)
77+
priority_to_license = defaultdict(
78+
lambda: "Unknown", {v: k for k, v in priority.items()}
79+
)
80+
selected = min(*[priority[lic] for lic in licenses])
81+
return priority_to_license[int(selected)]
82+
83+
mapping = {
84+
"BSD License": "BSD",
85+
"MIT License": "MIT",
86+
"The Unlicensed (Unlicensed)": "Unlicensed",
87+
"Mozilla Public License 2.0 (MPL 2.0)": "MPLv2",
88+
"GNU Lesser General Public License v2 (LGPLv2)": "LGPLv2",
89+
"GNU General Public License v2 (GPLv2)": "GPLv2",
90+
"GNU General Public License v3 (GPLv3)": "GPLv3",
91+
}
92+
93+
if is_multi_license(_license):
94+
return select_most_permissive(_license)
95+
96+
if _license not in mapping:
97+
return _license
98+
99+
return mapping[_license]
100+
101+
102+
def _packages_from_json(file: Path) -> list[Package]:
103+
packages = json.loads(file.read_text())
104+
packages_list = []
105+
for package in packages:
106+
packages_list.append(
107+
Package(
108+
name=package["Name"],
109+
package_link=package["URL"],
110+
version=package["Version"],
111+
license=_normalize(package["License"]),
112+
license_link="",
113+
)
114+
)
115+
return packages_list
116+
117+
118+
def dependency(group: str, group_packages: list, packages: list[Package]) -> str:
119+
def header(group: str):
120+
group = "".join([word.capitalize() for word in group.strip().split()])
121+
text = f"## {group} Dependencies\n"
122+
text += "---\n"
123+
text += "|Package|version|Licence|\n"
124+
text += "|---|---|---|\n"
125+
return text
126+
127+
def rows(group_packages: list, packages: list[Package]) -> str:
128+
def _normalize_package_name(name: str) -> str:
129+
_name = name.lower()
130+
while "_" in _name:
131+
_name = _name.replace("_", "-")
132+
return _name
133+
134+
text = ""
135+
for package in group_packages:
136+
consistent = filter(
137+
lambda elem: (_normalize_package_name(elem.name) == package),
138+
packages,
139+
)
140+
for content in consistent:
141+
text += f"|[{content.name}]({content.package_link})"
142+
text += f"|{content.version}"
143+
text += f"|[{content.license}]({content.license_link})|\n"
144+
text += "\n"
145+
return text
146+
147+
_template = cleandoc(
148+
"""
149+
{header}{rows}
150+
"""
151+
)
152+
return _template.format(
153+
header=header(group), rows=rows(group_packages, packages)
154+
)
155+
156+
157+
def packages_markdown(dependencies: dict[str, list], packages: list[Package]) -> str:
158+
def heading():
159+
text = "# Dependecies\n"
160+
text += "---\n"
161+
return text
162+
163+
template = cleandoc(
164+
"""
165+
{heading}{rows}
166+
"""
167+
)
168+
rows = ""
169+
170+
for group in dependencies:
171+
rows += dependency(group, dependencies[group], packages)
172+
return template.format(heading=heading(), rows=rows)
173+
174+
175+
@nox.session(name="dependency:licenses", python=False)
176+
def dependency_licenses(session: Session) -> None:
177+
"""returns the packages and their licenses"""
178+
toml = Path("pyproject.toml")
179+
dependencies = _dependencies(toml.read_text())
180+
_licenses(session)
181+
package_infos = _packages_from_json(Path("packages.json"))
182+
print(packages_markdown(dependencies, package_infos))

exasol/toolbox/nox/tasks.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,5 +78,10 @@ def check(session: Session) -> None:
7878
from exasol.toolbox.nox._artifacts import (
7979
check_artifacts
8080
)
81+
82+
from exasol.toolbox.nox._dependencies import (
83+
dependency_licenses,
84+
)
85+
8186
# isort: on
8287
# fmt: on

0 commit comments

Comments
 (0)