Skip to content

Commit e0cdb6b

Browse files
committed
feat: change SPDX to the new internal opossum_model
* minor changes to attribution_generation.py and helper_methods.py * convert_to_opossum.py now uses opossum_model.Resource which simplifies the logic * this caused some tests to change: - (minor) some tests require a conversion to opossum file format - resources_to_attribution used to have trailing "/" for folders which are no longer generated. This should have no impact on the validity of the .opossum file
1 parent 10b16d6 commit e0cdb6b

7 files changed

+90
-159
lines changed

src/opossum_lib/spdx/attribution_generation.py

+11-11
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,
@@ -34,13 +34,13 @@ def create_package_attribution(package: Package) -> OpossumPackage:
3434
source = SourceInfo(name=SPDX_PACKAGE_IDENTIFIER)
3535
package_attribution = OpossumPackage(
3636
source=source,
37-
packageName=package.name,
37+
package_name=package.name,
3838
url=str(package.download_location),
39-
packageVersion=package.version,
40-
packagePURLAppendix=_get_purl(package),
39+
package_version=package.version,
40+
package_purl_appendix=_get_purl(package),
4141
copyright=str(package.copyright_text),
4242
comment=package_data.getvalue(),
43-
licenseName=str(package.license_concluded),
43+
license_name=str(package.license_concluded),
4444
)
4545

4646
return package_attribution
@@ -52,10 +52,10 @@ def create_file_attribution(file: File) -> OpossumPackage:
5252
source = SourceInfo(name=SPDX_FILE_IDENTIFIER)
5353
file_attribution = OpossumPackage(
5454
source=source,
55-
packageName=file.name.split("/")[-1],
55+
package_name=file.name.split("/")[-1],
5656
copyright=str(file.copyright_text),
5757
comment=file_data.getvalue(),
58-
licenseName=str(file.license_concluded),
58+
license_name=str(file.license_concluded),
5959
)
6060
return file_attribution
6161

@@ -66,10 +66,10 @@ def create_snippet_attribution(snippet: Snippet) -> OpossumPackage:
6666
source = SourceInfo(name=SPDX_SNIPPET_IDENTIFIER)
6767
snippet_attribution = OpossumPackage(
6868
source=source,
69-
packageName=snippet.name,
69+
package_name=snippet.name,
7070
copyright=str(snippet.copyright_text),
7171
comment=snippet_data.getvalue(),
72-
licenseName=str(snippet.license_concluded),
72+
license_name=str(snippet.license_concluded),
7373
)
7474

7575
return snippet_attribution
@@ -83,8 +83,8 @@ def create_document_attribution(
8383
source = SourceInfo(name=creation_info.spdx_id)
8484
document_attribution = OpossumPackage(
8585
source=source,
86-
packageName=creation_info.name,
87-
licenseName=creation_info.data_license,
86+
package_name=creation_info.name,
87+
license_name=creation_info.data_license,
8888
comment=creation_info_data.getvalue(),
8989
)
9090

src/opossum_lib/spdx/convert_to_opossum.py

+49-74
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
import logging
66
import sys
77
import uuid
8+
from typing import Any
89

9-
from networkx import DiGraph, shortest_path
10+
from networkx import DiGraph
1011
from spdx_tools.spdx.model.document import CreationInfo
1112
from spdx_tools.spdx.model.document import Document as SpdxDocument
1213
from spdx_tools.spdx.model.file import File
@@ -16,17 +17,16 @@
1617
from spdx_tools.spdx.parser.parse_anything import parse_file
1718
from spdx_tools.spdx.validation.document_validator import validate_full_spdx_document
1819

19-
from opossum_lib.opossum.opossum_file import (
20+
from opossum_lib.opossum.opossum_file_content import OpossumFileContent
21+
from opossum_lib.opossum_model import (
2022
ExternalAttributionSource,
2123
Metadata,
22-
OpossumInformation,
24+
Opossum,
2325
OpossumPackage,
24-
OpossumPackageIdentifier,
2526
Resource,
26-
ResourceType,
27+
ScanResults,
2728
SourceInfo,
2829
)
29-
from opossum_lib.opossum.opossum_file_content import OpossumFileContent
3030
from opossum_lib.spdx.attribution_generation import (
3131
create_document_attribution,
3232
create_file_attribution,
@@ -40,10 +40,10 @@
4040
)
4141
from opossum_lib.spdx.graph_generation import generate_graph_from_spdx
4242
from opossum_lib.spdx.helper_methods import (
43-
_create_file_path_from_graph_path,
43+
_get_file_path,
44+
_get_resource_type,
4445
_get_source_for_graph_traversal,
4546
_node_represents_a_spdx_element,
46-
_replace_node_ids_with_labels_and_add_resource_type,
4747
_weakly_connected_component_sub_graphs,
4848
)
4949
from opossum_lib.spdx.tree_generation import generate_tree_from_graph
@@ -69,15 +69,15 @@ def convert_spdx_to_opossum_information(filename: str) -> OpossumFileContent:
6969
)
7070
graph = generate_graph_from_spdx(document)
7171
tree = generate_tree_from_graph(graph)
72-
opossum_information = convert_tree_to_opossum_information(tree)
73-
return OpossumFileContent(opossum_information)
72+
return convert_tree_to_opossum(tree).to_opossum_file_format()
7473

7574

76-
def convert_tree_to_opossum_information(tree: DiGraph) -> OpossumInformation:
75+
def convert_tree_to_opossum(tree: DiGraph) -> Opossum:
7776
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()
77+
resources = [] # Resource(type=ResourceType.TOP_LEVEL)
78+
# resources_to_attributions: dict[str, list[str]] = dict()
79+
# external_attributions: dict[str, OpossumPackage] = dict()
80+
attribution_to_id: dict[OpossumPackage, str] = {}
8181
attribution_breakpoints = []
8282
external_attribution_sources = {
8383
SPDX_FILE_IDENTIFIER: ExternalAttributionSource(
@@ -97,78 +97,53 @@ def convert_tree_to_opossum_information(tree: DiGraph) -> OpossumInformation:
9797
raise RuntimeError(
9898
"A tree should always have a node without incoming edge."
9999
)
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,
100+
source_file_path = _get_file_path(connected_subgraph, source, source)
101+
rootnode = Resource(path=source_file_path)
102+
resources.append(rootnode)
103+
for node_label in connected_subgraph.nodes():
104+
node = connected_subgraph.nodes[node_label]
105+
file_path: str = _get_file_path(connected_subgraph, source, node_label)
106+
new_resource = Resource(path=file_path, type=_get_resource_type(node))
107+
if _node_represents_a_spdx_element(connected_subgraph, node_label):
108+
attribution = create_attribution(node)
109+
attribution_to_id[attribution] = (
110+
get_attribution_id(node["element"]) or node_label
116111
)
117-
112+
new_resource.attributions.append(attribution)
118113
else:
119-
attribution_breakpoints.append(file_path)
114+
attribution_breakpoints.append("/" + file_path)
115+
rootnode.add_resource(new_resource)
120116

121-
opossum_information = OpossumInformation(
117+
scan_results = ScanResults(
122118
metadata=metadata,
123-
resources=resources.convert_to_file_resource(),
124-
external_attributions=external_attributions,
125-
resources_to_attributions=resources_to_attributions,
126-
attributionBreakpoints=attribution_breakpoints,
127-
externalAttributionSources=external_attribution_sources,
119+
resources=resources,
120+
attribution_to_id=attribution_to_id,
121+
attribution_breakpoints=attribution_breakpoints,
122+
external_attribution_sources=external_attribution_sources,
128123
)
129-
return opossum_information
124+
return Opossum(scan_results=scan_results)
130125

131126

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"]
127+
def create_attribution(
128+
node: dict[str, Any],
129+
) -> OpossumPackage:
130+
node_element = node["element"]
140131
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-
]
132+
return create_package_attribution(package=node_element)
147133
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-
]
134+
return create_file_attribution(node_element)
154135
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-
]
136+
return create_snippet_attribution(node_element)
161137
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-
138+
return create_document_attribution(node_element)
167139
else:
168-
external_attributions[node] = OpossumPackage(
169-
source=SourceInfo(name=tree.nodes[node]["label"])
170-
)
171-
resources_to_attributions[file_path] = [node]
140+
return OpossumPackage(source=SourceInfo(name=node["label"]))
141+
142+
143+
def get_attribution_id(element: Any) -> str | None:
144+
if isinstance(element, Package | File | Snippet | CreationInfo):
145+
return element.spdx_id
146+
return None
172147

173148

174149
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
packageName=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
packageName=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
packageName=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
packageName=creation_info.name,
130130
licenseName=creation_info.data_license,

0 commit comments

Comments
 (0)