Skip to content

Commit 7675ac7

Browse files
authoredJan 28, 2025··
Merge pull request #200 from opossum-tool/refactor-spdx-on-new-internal-model
feat: change SPDX to the new internal opossum_model
2 parents ff5d5d8 + cfe5a54 commit 7675ac7

7 files changed

+78
-148
lines changed
 

‎src/opossum_lib/spdx/attribution_generation.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from spdx_tools.spdx.writer.tagvalue.package_writer import write_package
1313
from spdx_tools.spdx.writer.tagvalue.snippet_writer import write_snippet
1414

15-
from opossum_lib.opossum.opossum_file import OpossumPackage, SourceInfo
15+
from opossum_lib.opossum_model import OpossumPackage, SourceInfo
1616
from opossum_lib.spdx.constants import (
1717
PURL,
1818
SPDX_FILE_IDENTIFIER,
@@ -37,7 +37,7 @@ def create_package_attribution(package: Package) -> OpossumPackage:
3737
package_name=package.name,
3838
url=str(package.download_location),
3939
package_version=package.version,
40-
package_p_u_r_l_appendix=_get_purl(package),
40+
package_purl_appendix=_get_purl(package),
4141
copyright=str(package.copyright_text),
4242
comment=package_data.getvalue(),
4343
license_name=str(package.license_concluded),

‎src/opossum_lib/spdx/convert_to_opossum.py

+46-72
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
import logging
66
import sys
77
import uuid
8+
from pathlib import PurePath
9+
from typing import Any
810

9-
from networkx import DiGraph, shortest_path
11+
from networkx import DiGraph
1012
from spdx_tools.spdx.model.document import CreationInfo
1113
from spdx_tools.spdx.model.document import Document as SpdxDocument
1214
from spdx_tools.spdx.model.file import File
@@ -16,17 +18,16 @@
1618
from spdx_tools.spdx.parser.parse_anything import parse_file
1719
from spdx_tools.spdx.validation.document_validator import validate_full_spdx_document
1820

19-
from opossum_lib.opossum.opossum_file import (
21+
from opossum_lib.opossum.opossum_file_content import OpossumFileContent
22+
from opossum_lib.opossum_model import (
2023
ExternalAttributionSource,
2124
Metadata,
22-
OpossumInformation,
25+
Opossum,
2326
OpossumPackage,
24-
OpossumPackageIdentifier,
2527
Resource,
26-
ResourceType,
28+
ScanResults,
2729
SourceInfo,
2830
)
29-
from opossum_lib.opossum.opossum_file_content import OpossumFileContent
3031
from opossum_lib.spdx.attribution_generation import (
3132
create_document_attribution,
3233
create_file_attribution,
@@ -40,10 +41,10 @@
4041
)
4142
from opossum_lib.spdx.graph_generation import generate_graph_from_spdx
4243
from opossum_lib.spdx.helper_methods import (
43-
_create_file_path_from_graph_path,
44+
_get_file_path,
45+
_get_resource_type,
4446
_get_source_for_graph_traversal,
4547
_node_represents_a_spdx_element,
46-
_replace_node_ids_with_labels_and_add_resource_type,
4748
_weakly_connected_component_sub_graphs,
4849
)
4950
from opossum_lib.spdx.tree_generation import generate_tree_from_graph
@@ -69,15 +70,13 @@ def convert_spdx_to_opossum_information(filename: str) -> OpossumFileContent:
6970
)
7071
graph = generate_graph_from_spdx(document)
7172
tree = generate_tree_from_graph(graph)
72-
opossum_information = convert_tree_to_opossum_information(tree)
73-
return OpossumFileContent(input_file=opossum_information)
73+
return convert_tree_to_opossum(tree).to_opossum_file_format()
7474

7575

76-
def convert_tree_to_opossum_information(tree: DiGraph) -> OpossumInformation:
76+
def convert_tree_to_opossum(tree: DiGraph) -> Opossum:
7777
metadata = create_metadata(tree)
78-
resources = Resource(type=ResourceType.TOP_LEVEL)
79-
resources_to_attributions: dict[str, list[str]] = dict()
80-
external_attributions: dict[str, OpossumPackage] = dict()
78+
resources = []
79+
attribution_to_id: dict[OpossumPackage, str] = {}
8180
attribution_breakpoints = []
8281
external_attribution_sources = {
8382
SPDX_FILE_IDENTIFIER: ExternalAttributionSource(
@@ -97,78 +96,53 @@ def convert_tree_to_opossum_information(tree: DiGraph) -> OpossumInformation:
9796
raise RuntimeError(
9897
"A tree should always have a node without incoming edge."
9998
)
100-
for node in connected_subgraph.nodes():
101-
path: list[str] = shortest_path(connected_subgraph, source, node)
102-
path_with_labels: list[tuple[str, ResourceType]] = (
103-
_replace_node_ids_with_labels_and_add_resource_type(
104-
path, connected_subgraph
105-
)
106-
)
107-
resources = resources.add_path(path_with_labels)
108-
file_path: str = _create_file_path_from_graph_path(path, connected_subgraph)
109-
if _node_represents_a_spdx_element(connected_subgraph, node):
110-
create_attribution_and_link_with_resource(
111-
external_attributions,
112-
resources_to_attributions,
113-
file_path,
114-
node,
115-
connected_subgraph,
99+
source_file_path = _get_file_path(connected_subgraph, source, source)
100+
rootnode = Resource(path=PurePath(source_file_path))
101+
resources.append(rootnode)
102+
for node_label in connected_subgraph.nodes():
103+
node = connected_subgraph.nodes[node_label]
104+
file_path: str = _get_file_path(connected_subgraph, source, node_label)
105+
new_resource = Resource(path=file_path, type=_get_resource_type(node))
106+
if _node_represents_a_spdx_element(connected_subgraph, node_label):
107+
attribution = create_attribution(node)
108+
attribution_to_id[attribution] = (
109+
get_attribution_id(node["element"]) or node_label
116110
)
117-
111+
new_resource.attributions.append(attribution)
118112
else:
119-
attribution_breakpoints.append(file_path)
113+
attribution_breakpoints.append("/" + file_path)
114+
rootnode.add_resource(new_resource)
120115

121-
opossum_information = OpossumInformation(
116+
scan_results = ScanResults(
122117
metadata=metadata,
123-
resources=resources.convert_to_file_resource(),
124-
external_attributions=external_attributions,
125-
resources_to_attributions=resources_to_attributions,
118+
resources=resources,
119+
attribution_to_id=attribution_to_id,
126120
attribution_breakpoints=attribution_breakpoints,
127121
external_attribution_sources=external_attribution_sources,
128122
)
129-
return opossum_information
123+
return Opossum(scan_results=scan_results)
130124

131125

132-
def create_attribution_and_link_with_resource(
133-
external_attributions: dict[OpossumPackageIdentifier, OpossumPackage],
134-
resources_to_attributions: dict[OpossumPackageIdentifier, list[str]],
135-
file_path: str,
136-
node: str,
137-
tree: DiGraph,
138-
) -> None:
139-
node_element = tree.nodes[node]["element"]
126+
def create_attribution(
127+
node: dict[str, Any],
128+
) -> OpossumPackage:
129+
node_element = node["element"]
140130
if isinstance(node_element, Package):
141-
external_attributions[node_element.spdx_id] = create_package_attribution(
142-
package=node_element
143-
)
144-
resources_to_attributions[file_path] = [
145-
node_element.spdx_id,
146-
]
131+
return create_package_attribution(package=node_element)
147132
elif isinstance(node_element, File):
148-
external_attributions[node_element.spdx_id] = create_file_attribution(
149-
node_element
150-
)
151-
resources_to_attributions[file_path] = [
152-
node_element.spdx_id,
153-
]
133+
return create_file_attribution(node_element)
154134
elif isinstance(node_element, Snippet):
155-
external_attributions[node_element.spdx_id] = create_snippet_attribution(
156-
node_element
157-
)
158-
resources_to_attributions[file_path] = [
159-
node_element.spdx_id,
160-
]
135+
return create_snippet_attribution(node_element)
161136
elif isinstance(node_element, CreationInfo):
162-
external_attributions[node_element.spdx_id] = create_document_attribution(
163-
node_element
164-
)
165-
resources_to_attributions[file_path] = [node_element.spdx_id]
166-
137+
return create_document_attribution(node_element)
167138
else:
168-
external_attributions[node] = OpossumPackage(
169-
source=SourceInfo(name=tree.nodes[node]["label"])
170-
)
171-
resources_to_attributions[file_path] = [node]
139+
return OpossumPackage(source=SourceInfo(name=node["label"]))
140+
141+
142+
def get_attribution_id(element: Any) -> str | None:
143+
if isinstance(element, Package | File | Snippet | CreationInfo):
144+
return element.spdx_id
145+
return None
172146

173147

174148
def create_metadata(tree: DiGraph) -> Metadata:

‎src/opossum_lib/spdx/helper_methods.py

+10-30
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
# SPDX-License-Identifier: Apache-2.0
44
from typing import Any
55

6-
from networkx import DiGraph, weakly_connected_components
6+
from networkx import DiGraph, shortest_path, weakly_connected_components
77
from spdx_tools.spdx.constants import DOCUMENT_SPDX_ID
88
from spdx_tools.spdx.model import File, Package, Snippet
99

10-
from opossum_lib.opossum.opossum_file import ResourceType
10+
from opossum_lib.opossum_model import ResourceType
1111

1212

1313
def _get_source_for_graph_traversal(connected_subgraph: DiGraph) -> str | None:
@@ -41,38 +41,18 @@ def _weakly_connected_component_sub_graphs(graph: DiGraph) -> list[DiGraph]:
4141
return connected_sub_graphs
4242

4343

44+
def _get_file_path(graph: DiGraph, source: str, to: str) -> str:
45+
path = shortest_path(graph, source, to)
46+
return _create_file_path_from_graph_path(path, graph)
47+
48+
4449
def _create_file_path_from_graph_path(path: list[str], graph: DiGraph) -> str:
45-
base_path = "/" + "/".join(
46-
[_replace_prefix(graph.nodes[node]["label"]) for node in path]
47-
)
50+
base_path = "/".join([_replace_prefix(graph.nodes[node]["label"]) for node in path])
4851
if list(graph.successors(path[-1])):
4952
base_path += "/"
5053
return base_path
5154

5255

53-
def _replace_node_ids_with_labels_and_add_resource_type(
54-
path: list[str], graph: DiGraph
55-
) -> list[tuple[str, ResourceType]]:
56-
resulting_path = []
57-
path_with_label_and_resource_type = [
58-
(
59-
_replace_prefix(graph.nodes[node]["label"]),
60-
_get_resource_type(graph.nodes[node]),
61-
)
62-
for node in path
63-
]
64-
for element_or_path, resource_type in path_with_label_and_resource_type:
65-
resulting_path.extend(
66-
[
67-
(element, resource_type)
68-
for element in element_or_path.split("/")
69-
if element
70-
]
71-
)
72-
73-
return resulting_path
74-
75-
7656
def _replace_prefix(label: str) -> str:
7757
"""
7858
Some spdx element names start with "./" or "/", to avoid paths like "/./" or
@@ -85,11 +65,11 @@ def _replace_prefix(label: str) -> str:
8565
return label
8666

8767

88-
def _get_resource_type(node_attributes: dict[str, Any]) -> ResourceType:
68+
def _get_resource_type(node_attributes: dict[str, Any]) -> ResourceType | None:
8969
element = node_attributes.get("element")
9070
if isinstance(element, Package):
9171
return ResourceType.FOLDER
9272
elif isinstance(element, Snippet | File):
9373
return ResourceType.FILE
9474
else:
95-
return ResourceType.OTHER
75+
return None

‎tests/data/expected_opossum.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,10 @@
109109
}
110110
},
111111
"resourcesToAttributions":{
112-
"/SPDX Lite Document/":[
112+
"/SPDX Lite Document":[
113113
"SPDXRef-DOCUMENT"
114114
],
115-
"/SPDX Lite Document/DESCRIBES/Package A/":[
115+
"/SPDX Lite Document/DESCRIBES/Package A":[
116116
"SPDXRef-Package-A"
117117
],
118118
"/SPDX Lite Document/DESCRIBES/Package B":[
@@ -124,7 +124,7 @@
124124
"/SPDX Lite Document/DESCRIBES/Package A/CONTAINS/File-C":[
125125
"SPDXRef-File-C"
126126
],
127-
"/SPDX Lite Document/DESCRIBES/Package A/COPY_OF/Package C/":[
127+
"/SPDX Lite Document/DESCRIBES/Package A/COPY_OF/Package C":[
128128
"SPDXRef-Package-C"
129129
],
130130
"/SPDX Lite Document/DESCRIBES/Package A/COPY_OF/Package C/CONTAINS/File-B":[

‎tests/test_spdx/test_attribution_creation.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def test_create_package_attribution() -> None:
5454
write_package(package, package_data)
5555
package_attribution = create_package_attribution(package)
5656

57-
assert package_attribution == OpossumPackage(
57+
assert package_attribution.to_opossum_file_format() == OpossumPackage(
5858
source=SourceInfo(name=SPDX_PACKAGE_IDENTIFIER),
5959
comment=package_data.getvalue(),
6060
package_name=package.name,
@@ -79,7 +79,7 @@ def test_create_file_attribution() -> None:
7979
write_file(file, file_data)
8080
file_attribution = create_file_attribution(file)
8181

82-
assert file_attribution == OpossumPackage(
82+
assert file_attribution.to_opossum_file_format() == OpossumPackage(
8383
source=SourceInfo(name=SPDX_FILE_IDENTIFIER),
8484
comment=file_data.getvalue(),
8585
package_name=file.name,
@@ -102,7 +102,7 @@ def test_create_snippet_attribution() -> None:
102102
write_snippet(snippet, snippet_data)
103103
snippet_attribution = create_snippet_attribution(snippet)
104104

105-
assert snippet_attribution == OpossumPackage(
105+
assert snippet_attribution.to_opossum_file_format() == OpossumPackage(
106106
source=SourceInfo(name=SPDX_SNIPPET_IDENTIFIER),
107107
comment=snippet_data.getvalue(),
108108
package_name=snippet.name,
@@ -124,7 +124,7 @@ def test_create_document_attribution() -> None:
124124
write_creation_info(creation_info, creation_info_data)
125125
document_attribution = create_document_attribution(creation_info)
126126

127-
assert document_attribution == OpossumPackage(
127+
assert document_attribution.to_opossum_file_format() == OpossumPackage(
128128
source=SourceInfo(name=DOCUMENT_SPDX_ID),
129129
package_name=creation_info.name,
130130
license_name=creation_info.data_license,

‎tests/test_spdx/test_extract_opossum_information_from_spdx.py

+12-12
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
SPDX_PACKAGE_IDENTIFIER,
2020
SPDX_SNIPPET_IDENTIFIER,
2121
)
22-
from opossum_lib.spdx.convert_to_opossum import convert_tree_to_opossum_information
22+
from opossum_lib.spdx.convert_to_opossum import convert_tree_to_opossum
2323
from opossum_lib.spdx.graph_generation import generate_graph_from_spdx
2424
from opossum_lib.spdx.tree_generation import generate_tree_from_graph
2525
from tests.test_spdx.helper_methods import (
@@ -52,12 +52,12 @@ def test_different_paths_graph() -> None:
5252
],
5353
)
5454
assert opossum_information.resources_to_attributions == {
55-
"/SPDX Lite Document/": ["SPDXRef-DOCUMENT"],
56-
"/SPDX Lite Document/DESCRIBES/Example package A/": ["SPDXRef-Package-A"],
55+
"/SPDX Lite Document": ["SPDXRef-DOCUMENT"],
56+
"/SPDX Lite Document/DESCRIBES/Example package A": ["SPDXRef-Package-A"],
5757
"/SPDX Lite Document/DESCRIBES/Example package A/CONTAINS/Example file": [
5858
"SPDXRef-File"
5959
],
60-
"/SPDX Lite Document/DESCRIBES/Example package B/": ["SPDXRef-Package-B"],
60+
"/SPDX Lite Document/DESCRIBES/Example package B": ["SPDXRef-Package-B"],
6161
"/SPDX Lite Document/DESCRIBES/Example package B/CONTAINS/Example file": [
6262
"SPDXRef-File"
6363
],
@@ -119,12 +119,12 @@ def test_unconnected_paths_graph() -> None:
119119
)
120120

121121
assert opossum_information.resources_to_attributions == {
122-
"/SPDX Lite Document/": ["SPDXRef-DOCUMENT"],
123-
"/SPDX Lite Document/DESCRIBES/Example package A/": ["SPDXRef-Package-A"],
122+
"/SPDX Lite Document": ["SPDXRef-DOCUMENT"],
123+
"/SPDX Lite Document/DESCRIBES/Example package A": ["SPDXRef-Package-A"],
124124
"/SPDX Lite Document/DESCRIBES/Example package A/CONTAINS/Example file": [
125125
"SPDXRef-File"
126126
],
127-
"/SPDX Lite Document/DESCRIBES/Example package B/": ["SPDXRef-Package-B"],
127+
"/SPDX Lite Document/DESCRIBES/Example package B": ["SPDXRef-Package-B"],
128128
"/SPDX Lite Document/DESCRIBES/Example package B/CONTAINS/Example file": [
129129
"SPDXRef-File"
130130
],
@@ -180,10 +180,10 @@ def test_different_roots_graph() -> None:
180180
)
181181

182182
assert opossum_information.resources_to_attributions == {
183-
"/File-B/": ["SPDXRef-File-B"],
183+
"/File-B": ["SPDXRef-File-B"],
184184
"/File-B/DESCRIBES/Package-B": ["SPDXRef-Package-B"],
185-
"/Document/": ["SPDXRef-DOCUMENT"],
186-
"/Document/DESCRIBES/Package-A/": ["SPDXRef-Package-A"],
185+
"/Document": ["SPDXRef-DOCUMENT"],
186+
"/Document/DESCRIBES/Package-A": ["SPDXRef-Package-A"],
187187
"/Document/DESCRIBES/Package-A/CONTAINS/File-A": ["SPDXRef-File-A"],
188188
"/Document/DESCRIBES/Package-B": ["SPDXRef-Package-B"],
189189
}
@@ -276,5 +276,5 @@ def _get_opossum_information_from_file(file_name: str) -> OpossumInformation:
276276
def _get_opossum_information_from_document(document: Document) -> OpossumInformation:
277277
graph = generate_graph_from_spdx(document)
278278
tree = generate_tree_from_graph(graph)
279-
opossum_information = convert_tree_to_opossum_information(tree)
280-
return opossum_information
279+
opossum_information = convert_tree_to_opossum(tree)
280+
return opossum_information.to_opossum_file_format().input_file

‎tests/test_spdx/test_helper_methods.py

+1-25
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
# SPDX-FileCopyrightText: TNG Technology Consulting GmbH <https://www.tngtech.com>
22
#
33
# SPDX-License-Identifier: Apache-2.0
4-
from unittest import TestCase
54

65
import pytest
76
from networkx import DiGraph
87

9-
from opossum_lib.opossum.opossum_file import ResourceType
108
from opossum_lib.spdx.helper_methods import (
119
_create_file_path_from_graph_path,
12-
_replace_node_ids_with_labels_and_add_resource_type,
1310
)
1411

1512

@@ -22,28 +19,7 @@ def test_create_file_path_from_graph_path(node_label: str) -> None:
2219

2320
file_path = _create_file_path_from_graph_path(path, graph)
2421

25-
assert file_path == "/root/node/with/path/leaf"
26-
27-
28-
@pytest.mark.parametrize(
29-
"node_label", ["node/with/path", "./node/with/path", "/node/with/path"]
30-
)
31-
def test_replace_node_ids_with_labels(node_label: str) -> None:
32-
graph = _create_simple_graph(node_label)
33-
path = ["root", "node", "leaf"]
34-
35-
file_path = _replace_node_ids_with_labels_and_add_resource_type(path, graph)
36-
37-
TestCase().assertCountEqual(
38-
file_path,
39-
[
40-
("root", ResourceType.OTHER),
41-
("node", ResourceType.OTHER),
42-
("with", ResourceType.OTHER),
43-
("path", ResourceType.OTHER),
44-
("leaf", ResourceType.OTHER),
45-
],
46-
)
22+
assert file_path == "root/node/with/path/leaf"
4723

4824

4925
def _create_simple_graph(node_label: str) -> DiGraph:

0 commit comments

Comments
 (0)
Please sign in to comment.