Module continuous_delivery_scripts.spdx_report.spdx_package
Definition of an SPDX Package.
Expand source code
#
# Copyright (C) 2020-2026 Arm Limited or its affiliates and Contributors. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
"""Definition of an SPDX Package."""
from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING, List, Optional
from continuous_delivery_scripts.spdx_report.spdx_file import SpdxFile
from continuous_delivery_scripts.spdx_report.spdx_helpers import (
determine_spdx_value,
list_project_files_for_licensing,
)
from continuous_delivery_scripts.utils.definitions import UNKNOWN
from continuous_delivery_scripts.utils.python.package_helpers import PackageMetadata
from continuous_delivery_scripts.utils.third_party_licences import (
UNKNOWN_LICENCE,
cleanse_licence_expression,
is_licence_accepted,
determine_licence_compound,
)
if TYPE_CHECKING:
from spdx.package import Package
@dataclass(frozen=True, order=True)
class PackageInfo:
"""Definition of a Python package.
Attributes:
metadata: metadata about a package from files generated from setup.py.
root_dir: project root directory.
source_dir: directory where package's sources are.
uuid: unique identifier of the package.
"""
metadata: PackageMetadata
root_dir: Path
source_dir: Path
uuid: str
def _set_package_copyright(file: SpdxFile, package: "Package") -> None:
"""Sets the copyright field of a package based on file copyright."""
if file.copyright:
package.cr_text = determine_spdx_value(file.copyright)
class SpdxPackage:
"""SPDX package.
See https://spdx.org/spdx-specification-21-web-version#h.4i7ojhp
"""
def __init__(self, package_info: PackageInfo, is_dependency: bool = False) -> None:
"""Constructor."""
self._is_dependency = is_dependency
self._package_info = package_info
self._file_list: Optional[List[Path]] = None
self._actual_licence: Optional[str] = None
self._main_licence: Optional[str] = None
@property
def files(self) -> Optional[List[Path]]:
"""Gets package's files.
Returns:
list of file path or None if a dependency.
"""
if self._is_dependency:
return None
if not self._file_list:
self._file_list = [p for p in list_project_files_for_licensing(self._package_info.source_dir)]
return self._file_list
@property
def id(self) -> str:
"""Gets Package's identifier.
Returns:
An ID
"""
return self.name if self._is_dependency else self._package_info.uuid
@property
def is_dependency(self) -> bool:
"""States whether the package is a dependency or not."""
return self._is_dependency
@property
def name(self) -> str:
"""Gets Package's name.
See https://spdx.org/spdx-specification-21-web-version#h.2xcytpi
Returns:
corresponding string
"""
return str(self._package_info.metadata.name)
@property
def version(self) -> str:
"""Gets package's version.
Returns:
package version
"""
return str(self._package_info.metadata.version)
@property
def main_licence(self) -> str:
"""Gets the package's official licence.
Returns:
project's licence
"""
if not self._main_licence:
package_licence = self._package_info.metadata.licence
self._main_licence = (
cleanse_licence_expression(package_licence) if package_licence else UNKNOWN_LICENCE.identifier
)
return self._main_licence
@property
def is_main_licence_accepted(self) -> bool:
"""States whether the main licence of the package is part of the accepted licence list."""
return bool(is_licence_accepted(self.main_licence))
@property
def licence(self) -> str:
"""Gets the actual package's licence based on the files it contains.
Returns:
project's licence
"""
if not self._actual_licence:
files = self.get_spdx_files()
self._actual_licence = (
determine_licence_compound(self.main_licence, [f.licence for f in files])
if files
else self.main_licence
)
return self._actual_licence
@property
def is_licence_accepted(self) -> bool:
"""States whether the actual package's licence of the package is part of the accepted licence list."""
return bool(is_licence_accepted(self.licence))
@property
def author(self) -> str:
"""Gets the document's author.
Returns:
document's author
"""
return str(self._package_info.metadata.author)
@property
def author_email(self) -> str:
"""Gets the document author's email.
Returns:
document author's email
"""
return str(self._package_info.metadata.author_email)
@property
def url(self) -> str:
"""Gets the package source URL.
Returns:
the package homepage
"""
return str(self._package_info.metadata.url)
@property
def description(self) -> str:
"""Gets the package description.
Returns:
some description
"""
return str(self._package_info.metadata.description)
def get_spdx_files(self) -> Optional[List[SpdxFile]]:
"""Gets package's files SPDX description.
Returns:
list of file descriptions or None if a dependency.
"""
if not self.files:
return None
return [SpdxFile(p, self._package_info.root_dir, self.main_licence) for p in self.files]
def generate_spdx_package(self) -> "Package":
"""Generates the SPDX package.
Example of a SPDX package:
PackageName: eduVPN
DataFormat: SPDXRef-1
PackageSupplier: Organization: The Commons Conservancy eduVPN Programme
PackageHomePage: https://eduvpn.org
PackageLicenseDeclared: GPL-3.0+
PackageCopyrightText: 2017, The Commons Conservancy eduVPN Programme
PackageSummary: <text>EduVPN is designed to allow users to connect
securely and encrypted to the Internet from any standard device.
</text>
PackageComment: <text>The package includes the following libraries; see
Relationship information.
</text>
Created: 2017-06-06T09:00:00Z
PackageDownloadLocation: git://github.com/eduVPN/reponame
PackageDownloadLocation: git+https://github.com/eduVPN/reponame.git
PackageDownloadLocation: git+ssh://github.com/eduVPN/reponame.git
Creator: Person: Jane Doe
Returns:
the corresponding package
"""
from spdx.checksum import Algorithm
from spdx.creationinfo import Person
from spdx.document import License
from spdx.package import Package
from spdx.utils import NoAssert
package = Package(
name=determine_spdx_value(self.name),
spdx_id=f"SPDXRef-{self.id}",
download_location=determine_spdx_value(None),
version=determine_spdx_value(self.version),
file_name=determine_spdx_value(self.name),
supplier=None,
originator=Person(
determine_spdx_value(self.author),
determine_spdx_value(self.author_email),
),
)
package.check_sum = Algorithm("SHA1", str(NoAssert()))
package.cr_text = NoAssert()
package.homepage = determine_spdx_value(self.url)
package.license_declared = License.from_identifier(str(determine_spdx_value(self.main_licence)))
package.conc_lics = License.from_identifier(str(determine_spdx_value(self.licence)))
package.summary = determine_spdx_value(self.description)
package.description = NoAssert()
files = self.get_spdx_files()
if files:
package.files_analyzed = True
for file in files:
package.add_file(file.generate_spdx_file())
package.add_lics_from_file(License.from_identifier(str(determine_spdx_value(file.licence))))
_set_package_copyright(file, package)
package.verif_code = determine_spdx_value(package.calc_verif_code())
else:
# Has to generate a dummy file because of the following rule in SDK:
# - Package must have at least one file
dummy_file = SpdxFile(Path(UNKNOWN), self._package_info.root_dir, self.main_licence)
package.verif_code = NoAssert()
package.add_file(dummy_file.generate_spdx_file())
package.add_lics_from_file(License.from_identifier(str(determine_spdx_value(dummy_file.licence))))
return package
Classes
class PackageInfo (metadata: PackageMetadata, root_dir: pathlib.Path, source_dir: pathlib.Path, uuid: str)-
Definition of a Python package.
Attributes
metadata- metadata about a package from files generated from setup.py.
root_dir- project root directory.
source_dir- directory where package's sources are.
uuid- unique identifier of the package.
Expand source code
@dataclass(frozen=True, order=True) class PackageInfo: """Definition of a Python package. Attributes: metadata: metadata about a package from files generated from setup.py. root_dir: project root directory. source_dir: directory where package's sources are. uuid: unique identifier of the package. """ metadata: PackageMetadata root_dir: Path source_dir: Path uuid: strClass variables
var metadata : PackageMetadatavar root_dir : pathlib.Pathvar source_dir : pathlib.Pathvar uuid : str
class SpdxPackage (package_info: PackageInfo, is_dependency: bool = False)-
Expand source code
class SpdxPackage: """SPDX package. See https://spdx.org/spdx-specification-21-web-version#h.4i7ojhp """ def __init__(self, package_info: PackageInfo, is_dependency: bool = False) -> None: """Constructor.""" self._is_dependency = is_dependency self._package_info = package_info self._file_list: Optional[List[Path]] = None self._actual_licence: Optional[str] = None self._main_licence: Optional[str] = None @property def files(self) -> Optional[List[Path]]: """Gets package's files. Returns: list of file path or None if a dependency. """ if self._is_dependency: return None if not self._file_list: self._file_list = [p for p in list_project_files_for_licensing(self._package_info.source_dir)] return self._file_list @property def id(self) -> str: """Gets Package's identifier. Returns: An ID """ return self.name if self._is_dependency else self._package_info.uuid @property def is_dependency(self) -> bool: """States whether the package is a dependency or not.""" return self._is_dependency @property def name(self) -> str: """Gets Package's name. See https://spdx.org/spdx-specification-21-web-version#h.2xcytpi Returns: corresponding string """ return str(self._package_info.metadata.name) @property def version(self) -> str: """Gets package's version. Returns: package version """ return str(self._package_info.metadata.version) @property def main_licence(self) -> str: """Gets the package's official licence. Returns: project's licence """ if not self._main_licence: package_licence = self._package_info.metadata.licence self._main_licence = ( cleanse_licence_expression(package_licence) if package_licence else UNKNOWN_LICENCE.identifier ) return self._main_licence @property def is_main_licence_accepted(self) -> bool: """States whether the main licence of the package is part of the accepted licence list.""" return bool(is_licence_accepted(self.main_licence)) @property def licence(self) -> str: """Gets the actual package's licence based on the files it contains. Returns: project's licence """ if not self._actual_licence: files = self.get_spdx_files() self._actual_licence = ( determine_licence_compound(self.main_licence, [f.licence for f in files]) if files else self.main_licence ) return self._actual_licence @property def is_licence_accepted(self) -> bool: """States whether the actual package's licence of the package is part of the accepted licence list.""" return bool(is_licence_accepted(self.licence)) @property def author(self) -> str: """Gets the document's author. Returns: document's author """ return str(self._package_info.metadata.author) @property def author_email(self) -> str: """Gets the document author's email. Returns: document author's email """ return str(self._package_info.metadata.author_email) @property def url(self) -> str: """Gets the package source URL. Returns: the package homepage """ return str(self._package_info.metadata.url) @property def description(self) -> str: """Gets the package description. Returns: some description """ return str(self._package_info.metadata.description) def get_spdx_files(self) -> Optional[List[SpdxFile]]: """Gets package's files SPDX description. Returns: list of file descriptions or None if a dependency. """ if not self.files: return None return [SpdxFile(p, self._package_info.root_dir, self.main_licence) for p in self.files] def generate_spdx_package(self) -> "Package": """Generates the SPDX package. Example of a SPDX package: PackageName: eduVPN DataFormat: SPDXRef-1 PackageSupplier: Organization: The Commons Conservancy eduVPN Programme PackageHomePage: https://eduvpn.org PackageLicenseDeclared: GPL-3.0+ PackageCopyrightText: 2017, The Commons Conservancy eduVPN Programme PackageSummary: <text>EduVPN is designed to allow users to connect securely and encrypted to the Internet from any standard device. </text> PackageComment: <text>The package includes the following libraries; see Relationship information. </text> Created: 2017-06-06T09:00:00Z PackageDownloadLocation: git://github.com/eduVPN/reponame PackageDownloadLocation: git+https://github.com/eduVPN/reponame.git PackageDownloadLocation: git+ssh://github.com/eduVPN/reponame.git Creator: Person: Jane Doe Returns: the corresponding package """ from spdx.checksum import Algorithm from spdx.creationinfo import Person from spdx.document import License from spdx.package import Package from spdx.utils import NoAssert package = Package( name=determine_spdx_value(self.name), spdx_id=f"SPDXRef-{self.id}", download_location=determine_spdx_value(None), version=determine_spdx_value(self.version), file_name=determine_spdx_value(self.name), supplier=None, originator=Person( determine_spdx_value(self.author), determine_spdx_value(self.author_email), ), ) package.check_sum = Algorithm("SHA1", str(NoAssert())) package.cr_text = NoAssert() package.homepage = determine_spdx_value(self.url) package.license_declared = License.from_identifier(str(determine_spdx_value(self.main_licence))) package.conc_lics = License.from_identifier(str(determine_spdx_value(self.licence))) package.summary = determine_spdx_value(self.description) package.description = NoAssert() files = self.get_spdx_files() if files: package.files_analyzed = True for file in files: package.add_file(file.generate_spdx_file()) package.add_lics_from_file(License.from_identifier(str(determine_spdx_value(file.licence)))) _set_package_copyright(file, package) package.verif_code = determine_spdx_value(package.calc_verif_code()) else: # Has to generate a dummy file because of the following rule in SDK: # - Package must have at least one file dummy_file = SpdxFile(Path(UNKNOWN), self._package_info.root_dir, self.main_licence) package.verif_code = NoAssert() package.add_file(dummy_file.generate_spdx_file()) package.add_lics_from_file(License.from_identifier(str(determine_spdx_value(dummy_file.licence)))) return packageInstance variables
-
Gets the document's author.
Returns
document's author
Expand source code
@property def author(self) -> str: """Gets the document's author. Returns: document's author """ return str(self._package_info.metadata.author) -
Gets the document author's email.
Returns
document author's email
Expand source code
@property def author_email(self) -> str: """Gets the document author's email. Returns: document author's email """ return str(self._package_info.metadata.author_email) var description : str-
Gets the package description.
Returns
some description
Expand source code
@property def description(self) -> str: """Gets the package description. Returns: some description """ return str(self._package_info.metadata.description) var files : Optional[List[pathlib.Path]]-
Gets package's files.
Returns
list of file path or None if a dependency.
Expand source code
@property def files(self) -> Optional[List[Path]]: """Gets package's files. Returns: list of file path or None if a dependency. """ if self._is_dependency: return None if not self._file_list: self._file_list = [p for p in list_project_files_for_licensing(self._package_info.source_dir)] return self._file_list var id : str-
Gets Package's identifier.
Returns
An ID
Expand source code
@property def id(self) -> str: """Gets Package's identifier. Returns: An ID """ return self.name if self._is_dependency else self._package_info.uuid var is_dependency : bool-
States whether the package is a dependency or not.
Expand source code
@property def is_dependency(self) -> bool: """States whether the package is a dependency or not.""" return self._is_dependency var is_licence_accepted : bool-
States whether the actual package's licence of the package is part of the accepted licence list.
Expand source code
@property def is_licence_accepted(self) -> bool: """States whether the actual package's licence of the package is part of the accepted licence list.""" return bool(is_licence_accepted(self.licence)) var is_main_licence_accepted : bool-
States whether the main licence of the package is part of the accepted licence list.
Expand source code
@property def is_main_licence_accepted(self) -> bool: """States whether the main licence of the package is part of the accepted licence list.""" return bool(is_licence_accepted(self.main_licence)) var licence : str-
Gets the actual package's licence based on the files it contains.
Returns
project's licence
Expand source code
@property def licence(self) -> str: """Gets the actual package's licence based on the files it contains. Returns: project's licence """ if not self._actual_licence: files = self.get_spdx_files() self._actual_licence = ( determine_licence_compound(self.main_licence, [f.licence for f in files]) if files else self.main_licence ) return self._actual_licence var main_licence : str-
Gets the package's official licence.
Returns
project's licence
Expand source code
@property def main_licence(self) -> str: """Gets the package's official licence. Returns: project's licence """ if not self._main_licence: package_licence = self._package_info.metadata.licence self._main_licence = ( cleanse_licence_expression(package_licence) if package_licence else UNKNOWN_LICENCE.identifier ) return self._main_licence var name : str-
Gets Package's name.
See https://spdx.org/spdx-specification-21-web-version#h.2xcytpi
Returns
corresponding string
Expand source code
@property def name(self) -> str: """Gets Package's name. See https://spdx.org/spdx-specification-21-web-version#h.2xcytpi Returns: corresponding string """ return str(self._package_info.metadata.name) var url : str-
Gets the package source URL.
Returns
the package homepage
Expand source code
@property def url(self) -> str: """Gets the package source URL. Returns: the package homepage """ return str(self._package_info.metadata.url) var version : str-
Gets package's version.
Returns
package version
Expand source code
@property def version(self) -> str: """Gets package's version. Returns: package version """ return str(self._package_info.metadata.version)
Methods
def generate_spdx_package(self) ‑> Package-
Generates the SPDX package.
Example of a SPDX package: PackageName: eduVPN DataFormat: SPDXRef-1 PackageSupplier: Organization: The Commons Conservancy eduVPN Programme PackageHomePage: https://eduvpn.org PackageLicenseDeclared: GPL-3.0+ PackageCopyrightText: 2017, The Commons Conservancy eduVPN Programme PackageSummary:
EduVPN is designed to allow users to connect securely and encrypted to the Internet from any standard device. PackageComment:The package includes the following libraries; see Relationship information. Created: 2017-06-06T09:00:00Z PackageDownloadLocation: git://github.com/eduVPN/reponame PackageDownloadLocation: git+https://github.com/eduVPN/reponame.git PackageDownloadLocation: git+ssh://github.com/eduVPN/reponame.git Creator: Person: Jane DoeReturns
the corresponding package
Expand source code
def generate_spdx_package(self) -> "Package": """Generates the SPDX package. Example of a SPDX package: PackageName: eduVPN DataFormat: SPDXRef-1 PackageSupplier: Organization: The Commons Conservancy eduVPN Programme PackageHomePage: https://eduvpn.org PackageLicenseDeclared: GPL-3.0+ PackageCopyrightText: 2017, The Commons Conservancy eduVPN Programme PackageSummary: <text>EduVPN is designed to allow users to connect securely and encrypted to the Internet from any standard device. </text> PackageComment: <text>The package includes the following libraries; see Relationship information. </text> Created: 2017-06-06T09:00:00Z PackageDownloadLocation: git://github.com/eduVPN/reponame PackageDownloadLocation: git+https://github.com/eduVPN/reponame.git PackageDownloadLocation: git+ssh://github.com/eduVPN/reponame.git Creator: Person: Jane Doe Returns: the corresponding package """ from spdx.checksum import Algorithm from spdx.creationinfo import Person from spdx.document import License from spdx.package import Package from spdx.utils import NoAssert package = Package( name=determine_spdx_value(self.name), spdx_id=f"SPDXRef-{self.id}", download_location=determine_spdx_value(None), version=determine_spdx_value(self.version), file_name=determine_spdx_value(self.name), supplier=None, originator=Person( determine_spdx_value(self.author), determine_spdx_value(self.author_email), ), ) package.check_sum = Algorithm("SHA1", str(NoAssert())) package.cr_text = NoAssert() package.homepage = determine_spdx_value(self.url) package.license_declared = License.from_identifier(str(determine_spdx_value(self.main_licence))) package.conc_lics = License.from_identifier(str(determine_spdx_value(self.licence))) package.summary = determine_spdx_value(self.description) package.description = NoAssert() files = self.get_spdx_files() if files: package.files_analyzed = True for file in files: package.add_file(file.generate_spdx_file()) package.add_lics_from_file(License.from_identifier(str(determine_spdx_value(file.licence)))) _set_package_copyright(file, package) package.verif_code = determine_spdx_value(package.calc_verif_code()) else: # Has to generate a dummy file because of the following rule in SDK: # - Package must have at least one file dummy_file = SpdxFile(Path(UNKNOWN), self._package_info.root_dir, self.main_licence) package.verif_code = NoAssert() package.add_file(dummy_file.generate_spdx_file()) package.add_lics_from_file(License.from_identifier(str(determine_spdx_value(dummy_file.licence)))) return package def get_spdx_files(self) ‑> Optional[List[SpdxFile]]-
Gets package's files SPDX description.
Returns
list of file descriptions or None if a dependency.
Expand source code
def get_spdx_files(self) -> Optional[List[SpdxFile]]: """Gets package's files SPDX description. Returns: list of file descriptions or None if a dependency. """ if not self.files: return None return [SpdxFile(p, self._package_info.root_dir, self.main_licence) for p in self.files]
-