Skip to content

Commit 0cd5a68

Browse files
committed
Split up licenses and shared model to appropriate test and files
1 parent dc25568 commit 0cd5a68

File tree

8 files changed

+372
-354
lines changed

8 files changed

+372
-354
lines changed

exasol/toolbox/nox/_dependencies.py

Lines changed: 6 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -3,191 +3,21 @@
33
import argparse
44
import json
55
import subprocess
6-
import tempfile
7-
from inspect import cleandoc
8-
from json import loads
96
from pathlib import Path
107

118
import nox
129
from nox import Session
1310

11+
from exasol.toolbox.util.dependencies.licenses import (
12+
licenses,
13+
packages_to_markdown,
14+
)
1415
from exasol.toolbox.util.dependencies.poetry_dependencies import (
15-
Package,
1616
PoetryDependencies,
17-
PoetryDependency,
1817
PoetryToml,
1918
)
2019

2120

22-
class PackageLicense(Package):
23-
package_link: str
24-
license: str
25-
license_link: str
26-
27-
28-
def _normalize(_license: str) -> str:
29-
def is_multi_license(l):
30-
return ";" in l
31-
32-
def select_most_restrictive(licenses: list) -> str:
33-
_max = 0
34-
lic = "Unknown"
35-
_mapping = {
36-
"Unknown": -1,
37-
"Unlicensed": 0,
38-
"BSD": 1,
39-
"MIT": 2,
40-
"MPLv2": 3,
41-
"LGPLv2": 4,
42-
"GPLv2": 5,
43-
"GPLv3": 6,
44-
}
45-
for l in licenses:
46-
if l in _mapping:
47-
if _mapping[l] > _mapping[lic]:
48-
lic = l
49-
else:
50-
return "<br>".join(licenses)
51-
return lic
52-
53-
mapping = {
54-
"BSD License": "BSD",
55-
"MIT License": "MIT",
56-
"The Unlicensed (Unlicensed)": "Unlicensed",
57-
"Mozilla Public License 2.0 (MPL 2.0)": "MPLv2",
58-
"GNU General Public License (GPL)": "GPL",
59-
"GNU Lesser General Public License v2 (LGPLv2)": "LGPLv2",
60-
"GNU General Public License v2 (GPLv2)": "GPLv2",
61-
"GNU General Public License v2 or later (GPLv2+)": "GPLv2+",
62-
"GNU General Public License v3 (GPLv3)": "GPLv3",
63-
"Apache Software License": "Apache",
64-
}
65-
66-
if is_multi_license(_license):
67-
items = []
68-
for item in _license.split(";"):
69-
item = str(item).strip()
70-
if item in mapping:
71-
items.append(mapping[item])
72-
else:
73-
items.append(item)
74-
return select_most_restrictive(items)
75-
76-
if _license not in mapping:
77-
return _license
78-
79-
return mapping[_license]
80-
81-
82-
def _packages_from_json(json: str) -> list[PackageLicense]:
83-
packages = loads(json)
84-
packages_list = []
85-
mapping = {
86-
"GPLv1": "https://www.gnu.org/licenses/old-licenses/gpl-1.0.html",
87-
"GPLv2": "https://www.gnu.org/licenses/old-licenses/gpl-2.0.html",
88-
"LGPLv2": "https://www.gnu.org/licenses/old-licenses/lgpl-2.0.html",
89-
"GPLv3": "https://www.gnu.org/licenses/gpl-3.0.html",
90-
"LGPLv3": "https://www.gnu.org/licenses/lgpl-3.0.html",
91-
"Apache": "https://www.apache.org/licenses/LICENSE-2.0",
92-
"MIT": "https://mit-license.org/",
93-
"BSD": "https://opensource.org/license/bsd-3-clause",
94-
}
95-
for package in packages:
96-
package_license = _normalize(package["License"])
97-
packages_list.append(
98-
PackageLicense(
99-
name=package["Name"],
100-
package_link="" if package["URL"] == "UNKNOWN" else package["URL"],
101-
version=package["Version"],
102-
license=package_license,
103-
license_link=(
104-
"" if package_license not in mapping else mapping[package_license]
105-
),
106-
)
107-
)
108-
return packages_list
109-
110-
111-
def _licenses() -> list[PackageLicense]:
112-
with tempfile.NamedTemporaryFile() as file:
113-
subprocess.run(
114-
[
115-
"poetry",
116-
"run",
117-
"pip-licenses",
118-
"--format=json",
119-
"--output-file=" + file.name,
120-
"--with-system",
121-
"--with-urls",
122-
],
123-
capture_output=True,
124-
)
125-
result = _packages_from_json(file.read().decode())
126-
return result
127-
128-
129-
def _packages_to_markdown(
130-
dependencies: dict[str, list], packages: list[PackageLicense]
131-
) -> str:
132-
def heading():
133-
text = "# Dependencies\n"
134-
return text
135-
136-
def dependency(
137-
group: str,
138-
group_packages: list[PoetryDependency],
139-
packages: list[PackageLicense],
140-
) -> str:
141-
def _header(_group: str):
142-
_group = "".join([word.capitalize() for word in _group.strip().split()])
143-
text = f"## {_group} Dependencies\n"
144-
text += "|Package|version|Licence|\n"
145-
text += "|---|---|---|\n"
146-
return text
147-
148-
def _rows(
149-
_group_packages: list[PoetryDependency], _packages: list[PackageLicense]
150-
) -> str:
151-
text = ""
152-
for package in _group_packages:
153-
consistent = filter(
154-
lambda elem: elem.normalized_name == package.normalized_name,
155-
_packages,
156-
)
157-
for content in consistent:
158-
if content.package_link:
159-
text += f"|[{content.name}]({content.package_link})"
160-
else:
161-
text += f"|{content.name}"
162-
text += f"|{content.version}"
163-
if content.license_link:
164-
text += f"|[{content.license}]({content.license_link})|\n"
165-
else:
166-
text += f"|{content.license}|\n"
167-
text += "\n"
168-
return text
169-
170-
_template = cleandoc(
171-
"""
172-
{header}{rows}
173-
"""
174-
)
175-
return _template.format(
176-
header=_header(group), rows=_rows(group_packages, packages)
177-
)
178-
179-
template = cleandoc(
180-
"""
181-
{heading}{rows}
182-
"""
183-
)
184-
185-
rows = ""
186-
for group in dependencies:
187-
rows += dependency(group, dependencies[group], packages)
188-
return template.format(heading=heading(), rows=rows)
189-
190-
19121
class Audit:
19222
@staticmethod
19323
def _filter_json_for_vulnerabilities(audit_json_bytes: bytes) -> dict:
@@ -262,8 +92,8 @@ def dependency_licenses(session: Session) -> None:
26292
dependencies = PoetryDependencies(
26393
groups=groups, working_directory=working_directory
26494
).direct_dependencies
265-
package_infos = _licenses()
266-
print(_packages_to_markdown(dependencies=dependencies, packages=package_infos))
95+
package_infos = licenses()
96+
print(packages_to_markdown(dependencies=dependencies, packages=package_infos))
26797

26898

26999
@nox.session(name="dependency:audit", python=False)
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
from __future__ import annotations
2+
3+
import subprocess
4+
import tempfile
5+
from inspect import cleandoc
6+
from json import loads
7+
8+
from exasol.toolbox.util.dependencies.poetry_dependencies import (
9+
PoetryDependency,
10+
)
11+
from exasol.toolbox.util.dependencies.shared_models import Package
12+
13+
14+
class PackageLicense(Package):
15+
package_link: str
16+
license: str
17+
license_link: str
18+
19+
20+
def _normalize(_license: str) -> str:
21+
def is_multi_license(l):
22+
return ";" in l
23+
24+
def select_most_restrictive(licenses: list) -> str:
25+
_max = 0
26+
lic = "Unknown"
27+
_mapping = {
28+
"Unknown": -1,
29+
"Unlicensed": 0,
30+
"BSD": 1,
31+
"MIT": 2,
32+
"MPLv2": 3,
33+
"LGPLv2": 4,
34+
"GPLv2": 5,
35+
"GPLv3": 6,
36+
}
37+
for l in licenses:
38+
if l in _mapping:
39+
if _mapping[l] > _mapping[lic]:
40+
lic = l
41+
else:
42+
return "<br>".join(licenses)
43+
return lic
44+
45+
mapping = {
46+
"BSD License": "BSD",
47+
"MIT License": "MIT",
48+
"The Unlicensed (Unlicensed)": "Unlicensed",
49+
"Mozilla Public License 2.0 (MPL 2.0)": "MPLv2",
50+
"GNU General Public License (GPL)": "GPL",
51+
"GNU Lesser General Public License v2 (LGPLv2)": "LGPLv2",
52+
"GNU General Public License v2 (GPLv2)": "GPLv2",
53+
"GNU General Public License v2 or later (GPLv2+)": "GPLv2+",
54+
"GNU General Public License v3 (GPLv3)": "GPLv3",
55+
"Apache Software License": "Apache",
56+
}
57+
58+
if is_multi_license(_license):
59+
items = []
60+
for item in _license.split(";"):
61+
item = str(item).strip()
62+
if item in mapping:
63+
items.append(mapping[item])
64+
else:
65+
items.append(item)
66+
return select_most_restrictive(items)
67+
68+
if _license not in mapping:
69+
return _license
70+
71+
return mapping[_license]
72+
73+
74+
def _packages_from_json(json: str) -> list[PackageLicense]:
75+
packages = loads(json)
76+
packages_list = []
77+
mapping = {
78+
"GPLv1": "https://www.gnu.org/licenses/old-licenses/gpl-1.0.html",
79+
"GPLv2": "https://www.gnu.org/licenses/old-licenses/gpl-2.0.html",
80+
"LGPLv2": "https://www.gnu.org/licenses/old-licenses/lgpl-2.0.html",
81+
"GPLv3": "https://www.gnu.org/licenses/gpl-3.0.html",
82+
"LGPLv3": "https://www.gnu.org/licenses/lgpl-3.0.html",
83+
"Apache": "https://www.apache.org/licenses/LICENSE-2.0",
84+
"MIT": "https://mit-license.org/",
85+
"BSD": "https://opensource.org/license/bsd-3-clause",
86+
}
87+
for package in packages:
88+
package_license = _normalize(package["License"])
89+
packages_list.append(
90+
PackageLicense(
91+
name=package["Name"],
92+
package_link="" if package["URL"] == "UNKNOWN" else package["URL"],
93+
version=package["Version"],
94+
license=package_license,
95+
license_link=(
96+
"" if package_license not in mapping else mapping[package_license]
97+
),
98+
)
99+
)
100+
return packages_list
101+
102+
103+
def licenses() -> list[PackageLicense]:
104+
with tempfile.NamedTemporaryFile() as file:
105+
subprocess.run(
106+
[
107+
"poetry",
108+
"run",
109+
"pip-licenses",
110+
"--format=json",
111+
"--output-file=" + file.name,
112+
"--with-system",
113+
"--with-urls",
114+
],
115+
capture_output=True,
116+
)
117+
result = _packages_from_json(file.read().decode())
118+
return result
119+
120+
121+
def packages_to_markdown(
122+
dependencies: dict[str, list], packages: list[PackageLicense]
123+
) -> str:
124+
def heading():
125+
text = "# Dependencies\n"
126+
return text
127+
128+
def dependency(
129+
group: str,
130+
group_packages: list[PoetryDependency],
131+
packages: list[PackageLicense],
132+
) -> str:
133+
def _header(_group: str):
134+
_group = "".join([word.capitalize() for word in _group.strip().split()])
135+
text = f"## {_group} Dependencies\n"
136+
text += "|Package|version|Licence|\n"
137+
text += "|---|---|---|\n"
138+
return text
139+
140+
def _rows(
141+
_group_packages: list[PoetryDependency], _packages: list[PackageLicense]
142+
) -> str:
143+
text = ""
144+
for package in _group_packages:
145+
consistent = filter(
146+
lambda elem: elem.normalized_name == package.normalized_name,
147+
_packages,
148+
)
149+
for content in consistent:
150+
if content.package_link:
151+
text += f"|[{content.name}]({content.package_link})"
152+
else:
153+
text += f"|{content.name}"
154+
text += f"|{content.version}"
155+
if content.license_link:
156+
text += f"|[{content.license}]({content.license_link})|\n"
157+
else:
158+
text += f"|{content.license}|\n"
159+
text += "\n"
160+
return text
161+
162+
_template = cleandoc(
163+
"""
164+
{header}{rows}
165+
"""
166+
)
167+
return _template.format(
168+
header=_header(group), rows=_rows(group_packages, packages)
169+
)
170+
171+
template = cleandoc(
172+
"""
173+
{heading}{rows}
174+
"""
175+
)
176+
177+
rows = ""
178+
for group in dependencies:
179+
rows += dependency(group, dependencies[group], packages)
180+
return template.format(heading=heading(), rows=rows)

0 commit comments

Comments
 (0)