Skip to content

Commit

Permalink
Merge pull request #186 from opossum-tool/feat-migrate-to-pydantic-ba…
Browse files Browse the repository at this point in the history
…semodel

refactor: migrate opossum_file to pydantic basemodel
  • Loading branch information
Hellgartner authored Jan 16, 2025
2 parents d9288b1 + 5adb197 commit d221019
Show file tree
Hide file tree
Showing 10 changed files with 70 additions and 65 deletions.
2 changes: 1 addition & 1 deletion src/opossum_lib/opossum/merger.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def expand_opossum_package_identifier(


def _merge_resources(resources: list[Resource]) -> Resource:
merged_resource = Resource(ResourceType.TOP_LEVEL)
merged_resource = Resource(type=ResourceType.TOP_LEVEL)
for resource in resources:
for path in resource.get_paths_of_all_leaf_nodes_with_types():
merged_resource = merged_resource.add_path(path)
Expand Down
25 changes: 12 additions & 13 deletions src/opossum_lib/opossum/opossum_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@
from typing import Literal, cast

from pydantic import BaseModel, ConfigDict, model_serializer
from pydantic.dataclasses import dataclass

type OpossumPackageIdentifier = str
type ResourcePath = str
type ResourceInFile = dict[str, ResourceInFile] | int


@dataclass(frozen=True)
class OpossumInformation:
class OpossumInformation(BaseModel):
model_config = ConfigDict(frozen=True)
metadata: Metadata
resources: ResourceInFile
externalAttributions: dict[OpossumPackageIdentifier, OpossumPackage]
Expand Down Expand Up @@ -47,15 +46,15 @@ class FrequentLicense(BaseModel):
defaultText: str


@dataclass(frozen=True)
class SourceInfo:
class SourceInfo(BaseModel):
model_config = ConfigDict(frozen=True)
name: str
documentConfidence: int | float | None = 0
additionalName: str | None = None


@dataclass(frozen=True)
class OpossumPackage:
class OpossumPackage(BaseModel):
model_config = ConfigDict(frozen=True)
source: SourceInfo
attributionConfidence: int | None = None
comment: str | None = None
Expand Down Expand Up @@ -95,8 +94,8 @@ class ResourceType(Enum):
OTHER = auto()


@dataclass(frozen=True)
class Resource:
class Resource(BaseModel):
model_config = ConfigDict(frozen=True)
type: ResourceType
children: dict[str, Resource] = field(default_factory=dict)

Expand Down Expand Up @@ -138,7 +137,7 @@ def drop_element(
)

else:
resource = Resource(ResourceType.TOP_LEVEL)
resource = Resource(type=ResourceType.TOP_LEVEL)
paths_in_resource.remove(path_to_element_to_drop)
paths_in_resource.append(path_to_element_to_drop[:-1])

Expand Down Expand Up @@ -182,8 +181,8 @@ def convert_to_file_resource(self) -> ResourceInFile:
return self.to_dict()


@dataclass(frozen=True)
class ExternalAttributionSource:
class ExternalAttributionSource(BaseModel):
model_config = ConfigDict(frozen=True)
name: str
priority: int
isRelevantForPreferred: bool | None = None
Expand All @@ -200,7 +199,7 @@ def _build_resource_tree(resource: ResourceInFile) -> Resource:


def convert_resource_in_file_to_resource(resource: ResourceInFile) -> Resource:
root_node = Resource(ResourceType.TOP_LEVEL)
root_node = Resource(type=ResourceType.TOP_LEVEL)

if isinstance(resource, dict):
dict_resource = cast(dict[str, ResourceInFile], resource)
Expand Down
4 changes: 2 additions & 2 deletions src/opossum_lib/scancode/resource_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def get_attribution_info(file: File) -> list[OpossumPackage]:
if file.type == FileType.DIRECTORY:
return []
copyright = "\n".join(c.copyright for c in file.copyrights)
source_info = SourceInfo(SCANCODE_SOURCE_NAME)
source_info = SourceInfo(name=SCANCODE_SOURCE_NAME)

attribution_infos = []
for license_detection in file.license_detections:
Expand All @@ -79,7 +79,7 @@ def get_attribution_info(file: File) -> list[OpossumPackage]:
attribution_confidence = int(max_score)

package = OpossumPackage(
source_info,
source=source_info,
licenseName=license_name,
attributionConfidence=attribution_confidence,
copyright=copyright,
Expand Down
8 changes: 4 additions & 4 deletions src/opossum_lib/spdx/attribution_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def _get_purl(package: Package) -> str | None:
def create_package_attribution(package: Package) -> OpossumPackage:
package_data = StringIO()
write_package(package, package_data)
source = SourceInfo(SPDX_PACKAGE_IDENTIFIER)
source = SourceInfo(name=SPDX_PACKAGE_IDENTIFIER)
package_attribution = OpossumPackage(
source=source,
packageName=package.name,
Expand All @@ -49,7 +49,7 @@ def create_package_attribution(package: Package) -> OpossumPackage:
def create_file_attribution(file: File) -> OpossumPackage:
file_data = StringIO()
write_file(file, file_data)
source = SourceInfo(SPDX_FILE_IDENTIFIER)
source = SourceInfo(name=SPDX_FILE_IDENTIFIER)
file_attribution = OpossumPackage(
source=source,
packageName=file.name.split("/")[-1],
Expand All @@ -63,7 +63,7 @@ def create_file_attribution(file: File) -> OpossumPackage:
def create_snippet_attribution(snippet: Snippet) -> OpossumPackage:
snippet_data = StringIO()
write_snippet(snippet, snippet_data)
source = SourceInfo(SPDX_SNIPPET_IDENTIFIER)
source = SourceInfo(name=SPDX_SNIPPET_IDENTIFIER)
snippet_attribution = OpossumPackage(
source=source,
packageName=snippet.name,
Expand All @@ -80,7 +80,7 @@ def create_document_attribution(
) -> OpossumPackage:
creation_info_data = StringIO()
write_creation_info(creation_info, creation_info_data)
source = SourceInfo(creation_info.spdx_id)
source = SourceInfo(name=creation_info.spdx_id)
document_attribution = OpossumPackage(
source=source,
packageName=creation_info.name,
Expand Down
10 changes: 6 additions & 4 deletions src/opossum_lib/spdx/convert_to_opossum.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,14 @@ def convert_tree_to_opossum_information(tree: DiGraph) -> OpossumInformation:
external_attributions: dict[str, OpossumPackage] = dict()
attribution_breakpoints = []
external_attribution_sources = {
SPDX_FILE_IDENTIFIER: ExternalAttributionSource(SPDX_FILE_IDENTIFIER, 500),
SPDX_FILE_IDENTIFIER: ExternalAttributionSource(
name=SPDX_FILE_IDENTIFIER, priority=500
),
SPDX_PACKAGE_IDENTIFIER: ExternalAttributionSource(
SPDX_PACKAGE_IDENTIFIER, 500
name=SPDX_PACKAGE_IDENTIFIER, priority=500
),
SPDX_SNIPPET_IDENTIFIER: ExternalAttributionSource(
SPDX_SNIPPET_IDENTIFIER, 500
name=SPDX_SNIPPET_IDENTIFIER, priority=500
),
}

Expand Down Expand Up @@ -164,7 +166,7 @@ def create_attribution_and_link_with_resource(

else:
external_attributions[node] = OpossumPackage(
source=SourceInfo(tree.nodes[node]["label"])
source=SourceInfo(name=tree.nodes[node]["label"])
)
resources_to_attributions[file_path] = [node]

Expand Down
50 changes: 26 additions & 24 deletions tests/test_opossum/test_merge.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,27 @@


def test_merge_opossum_information() -> None:
opossum_package = OpossumPackage(source=SourceInfo("source"))
opossum_package = OpossumPackage(source=SourceInfo(name="source"))
opossum_information = OpossumInformation(
Metadata(
metadata=Metadata(
projectId="project-id",
fileCreationDate="30-05-2023",
projectTitle="test data",
),
{"A": {"B": {}}},
{"SPDXRef-Package": opossum_package},
{"/A/B/": ["SPDXRef-Package"]},
resources={"A": {"B": {}}},
externalAttributions={"SPDXRef-Package": opossum_package},
resourcesToAttributions={"/A/B/": ["SPDXRef-Package"]},
)

opossum_information_2 = OpossumInformation(
Metadata(
metadata=Metadata(
projectId="test-data-id",
fileCreationDate="29-05-2023",
projectTitle="second test data",
),
{"A": {"D": {"C": 1}}},
{"SPDXRef-File": opossum_package},
{"/A/D/C": ["SPDXRef-File"]},
resources={"A": {"D": {"C": 1}}},
externalAttributions={"SPDXRef-File": opossum_package},
resourcesToAttributions={"/A/D/C": ["SPDXRef-File"]},
)

merged_information = merge_opossum_information(
Expand Down Expand Up @@ -80,7 +80,7 @@ def test_merge_resources() -> None:
[("A", ResourceType.FOLDER), ("D", ResourceType.FILE)],
]

resource = Resource(ResourceType.TOP_LEVEL)
resource = Resource(type=ResourceType.TOP_LEVEL)
for path in list_of_paths_with_resource_types:
resource = resource.add_path(path)

Expand All @@ -98,30 +98,32 @@ def test_merge_resources() -> None:
("E", ResourceType.FOLDER),
],
]
resource2 = Resource(ResourceType.TOP_LEVEL)
resource2 = Resource(type=ResourceType.TOP_LEVEL)
for path in list_of_paths_with_resource_type:
resource2 = resource2.add_path(path)

resources = [resource, resource2]
merged_resource = _merge_resources(resources)

assert merged_resource == Resource(
ResourceType.TOP_LEVEL,
{
type=ResourceType.TOP_LEVEL,
children={
"A": Resource(
ResourceType.FOLDER,
{
type=ResourceType.FOLDER,
children={
"B": Resource(
ResourceType.FOLDER, {"C": Resource(ResourceType.FILE, {})}
type=ResourceType.FOLDER,
children={"C": Resource(type=ResourceType.FILE)},
),
"D": Resource(ResourceType.FILE, {}),
"D": Resource(type=ResourceType.FILE),
},
),
"C": Resource(
ResourceType.FOLDER,
{
type=ResourceType.FOLDER,
children={
"D": Resource(
ResourceType.FOLDER, {"E": Resource(ResourceType.FOLDER, {})}
type=ResourceType.FOLDER,
children={"E": Resource(type=ResourceType.FOLDER)},
)
},
),
Expand Down Expand Up @@ -176,18 +178,18 @@ def test_merge_dicts_without_duplicates_type_error(
source_info: SourceInfo,
) -> None:
dicts = [
{"A": OpossumPackage(source_info, comment="test package 1")},
{"A": OpossumPackage(source_info, comment="test package 2")},
{"A": OpossumPackage(source=source_info, comment="test package 1")},
{"A": OpossumPackage(source=source_info, comment="test package 2")},
]
with pytest.raises(TypeError):
_merge_dicts_without_duplicates(dicts)


def test_expand_opossum_package_identifier() -> None:
opossum_package = OpossumPackage(SourceInfo("source-info"))
opossum_package = OpossumPackage(source=SourceInfo(name="source-info"))
opossum_information_expanded = expand_opossum_package_identifier(
OpossumInformation(
Metadata(
metadata=Metadata(
projectId="project-id",
fileCreationDate="2022-03-02",
projectTitle="project title",
Expand Down
16 changes: 8 additions & 8 deletions tests/test_opossum/test_opossum_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def test_resource_to_dict_with_file_as_leaf() -> None:
[("A", ResourceType.FOLDER), ("B", ResourceType.FILE)],
[("A", ResourceType.FOLDER), ("D", ResourceType.FILE)],
]
resource = Resource(ResourceType.TOP_LEVEL)
resource = Resource(type=ResourceType.TOP_LEVEL)

for path in list_of_paths:
resource = resource.add_path(path)
Expand All @@ -41,7 +41,7 @@ def test_resource_to_dict_with_package_as_leaf() -> None:
[("A", ResourceType.FOLDER), ("B", ResourceType.FILE)],
[("A", ResourceType.FOLDER), ("D", ResourceType.FOLDER)],
]
resource = Resource(ResourceType.TOP_LEVEL)
resource = Resource(type=ResourceType.TOP_LEVEL)

for path in list_of_paths:
resource = resource.add_path(path)
Expand All @@ -66,7 +66,7 @@ def test_resource_get_path() -> None:
("F", ResourceType.OTHER),
],
]
resource = Resource(ResourceType.TOP_LEVEL)
resource = Resource(type=ResourceType.TOP_LEVEL)

for path in list_of_paths:
resource = resource.add_path(path)
Expand All @@ -92,7 +92,7 @@ def test_resource_get_path() -> None:
def test_resource_add_path_throws_err_if_leaf_element_exists_with_different_type() -> (
None
):
resource = Resource(ResourceType.TOP_LEVEL)
resource = Resource(type=ResourceType.TOP_LEVEL)
resource = resource.add_path(
[
("A", ResourceType.FOLDER),
Expand All @@ -112,7 +112,7 @@ def test_resource_add_path_throws_err_if_leaf_element_exists_with_different_type


def test_resource_add_path_throws_err_if_element_exists_with_different_type() -> None:
resource = Resource(ResourceType.TOP_LEVEL)
resource = Resource(type=ResourceType.TOP_LEVEL)
resource = resource.add_path(
[
("A", ResourceType.FOLDER),
Expand All @@ -133,7 +133,7 @@ def test_resource_add_path_throws_err_if_element_exists_with_different_type() ->


def test_resource_drop_element_error() -> None:
resource = Resource(ResourceType.TOP_LEVEL)
resource = Resource(type=ResourceType.TOP_LEVEL)
resource = resource.add_path(
[
("A", ResourceType.FOLDER),
Expand All @@ -154,7 +154,7 @@ def test_resource_drop_element_error() -> None:


def test_resource_drop_element_error_not_leaf() -> None:
resource = Resource(ResourceType.TOP_LEVEL)
resource = Resource(type=ResourceType.TOP_LEVEL)
resource = resource.add_path(
[
("A", ResourceType.FOLDER),
Expand All @@ -175,7 +175,7 @@ def test_resource_drop_element_error_not_leaf() -> None:


def test_resource_drop_element() -> None:
resource = Resource(ResourceType.TOP_LEVEL)
resource = Resource(type=ResourceType.TOP_LEVEL)
resource = resource.add_path(
[
("A", ResourceType.FOLDER),
Expand Down
4 changes: 2 additions & 2 deletions tests/test_scancode/test_resource_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,13 @@ def test_get_attribution_info_file_multiple() -> None:
)
attributions = get_attribution_info(file)
expected1 = OpossumPackage(
source=SourceInfo(SCANCODE_SOURCE_NAME),
source=SourceInfo(name=SCANCODE_SOURCE_NAME),
licenseName="Apache-2.0",
copyright="Me\nMyself\nI",
attributionConfidence=95,
)
expected2 = OpossumPackage(
source=SourceInfo(SCANCODE_SOURCE_NAME),
source=SourceInfo(name=SCANCODE_SOURCE_NAME),
licenseName="MIT",
copyright="Me\nMyself\nI",
attributionConfidence=50,
Expand Down
Loading

0 comments on commit d221019

Please sign in to comment.