Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
94a1050
delete unused dependencies und update README.md
meretp Jan 26, 2023
febe022
[issue-407] add writer for rdf, start with simple graph
meretp Jan 23, 2023
68e1805
[issue-407] add writer for creation information
meretp Jan 24, 2023
6b0129a
[issue-407] add writer for annotations
meretp Jan 24, 2023
794c056
[issue-407] connect annotation with spdx element that is annotated
meretp Jan 24, 2023
9b53dcd
[issue-407] add annotation_type as resource not Literal
meretp Jan 24, 2023
1d63be9
[issue-407] add relationship writer
meretp Jan 24, 2023
9a3f0de
[issue-407] add relationship writer to document writer
meretp Jan 24, 2023
7aa5c7d
[issue-407] add checksum writer
meretp Jan 24, 2023
9df6d6b
[issue-407] add file writer
meretp Jan 24, 2023
9f07f66
[issue-407] fix annotation test
meretp Jan 24, 2023
dd92310
[issue-407] also use add_value_if_exists in creation information
meretp Jan 25, 2023
b61750b
[issue-407] turn spdx_namespace into a variable
meretp Jan 25, 2023
f93f660
[issue-407, refactor] rename function
meretp Jan 25, 2023
d03bdf9
[issue-407] add helper functions to handle SpdxNoAssertion and SpdxNone
meretp Jan 25, 2023
d2534c7
[issue-407] add package writer
meretp Jan 25, 2023
bf70966
[issue-407] use helper method to transform enum names correctly
meretp Jan 25, 2023
679471a
[issue-407] add transformation from ChecksumAlgorithm to rdf string
meretp Jan 25, 2023
453b135
[issue-407] add snippet writer
meretp Jan 25, 2023
51ee09b
[issue-407] add extracted licensing info writer
meretp Jan 25, 2023
2d9ad16
[issue-407] add external document ref writer
meretp Jan 26, 2023
0992053
[issue-407] bind DOAP namespace to "doap" prefix
meretp Jan 26, 2023
dcfe408
[issue-407] fix handling of spdx id
meretp Jan 26, 2023
a1090ab
[issue-407] add rdf writer to CLI
meretp Jan 26, 2023
339ae08
[issue-407] fix writing of reference_type in ExternalPackageRef
meretp Jan 26, 2023
937be2f
[issue-407, refactor] rename
meretp Jan 26, 2023
9720d2c
[issue-407] add ranges writer
meretp Jan 26, 2023
72e7448
[issue-407] fix
meretp Jan 26, 2023
bc8f820
[issue-407] add rdflib as optional dependency
meretp Jan 26, 2023
af1e3a0
[issue-407] fix pipeline
meretp Jan 26, 2023
4a96c92
[issue-407] fix writing of data license
meretp Jan 27, 2023
8f6b243
[issue-407] fix tests after rebase
meretp Jan 27, 2023
cb56a58
[issue-407] add license expression writer
meretp Jan 30, 2023
70457f9
[issue-407] delete unused imports
meretp Jan 30, 2023
33ea172
[issue-407] refactor
meretp Jan 30, 2023
c676827
[issue-407] delete debug output from development
meretp Jan 30, 2023
8e4113e
[issue-407, review] make global constants uppercase
meretp Jan 31, 2023
7c7c05a
[issue-407, review] reformat and use positive logic
meretp Jan 31, 2023
bfb8ee6
[issue-407, review] also test line_range and set datatype for offset …
meretp Jan 31, 2023
65b20d9
[issue-407, review] include all parameters in test
meretp Jan 31, 2023
34dfe29
[issue-407, review] rename parameter for clarification
meretp Jan 31, 2023
5ac24af
[issue-407, review] add tests for helper function and add error handling
meretp Jan 31, 2023
64caf43
[issue-407, review] rename
meretp Jan 31, 2023
975e115
[issue-407, review] refactor: change order of arguments and rename so…
meretp Jan 31, 2023
d86e712
[issue-407, review] make comment in pyproject.toml more comprehensible
meretp Jan 31, 2023
3e21033
[issue-407, review] rename methods
meretp Jan 31, 2023
506fafd
[issue-407, review] use fixture values in assert statements
meretp Jan 31, 2023
bde80ff
[issue-407, refactor] introduce module "rdfschema" to use common spdx…
meretp Jan 31, 2023
22ee52f
[issue-407] add license and reference namespace
meretp Jan 31, 2023
9d88414
[issue-407, review] inline triple
meretp Feb 1, 2023
3eaf91c
[issue-407, review] precise tests by using RDF.type as predicate and …
meretp Feb 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/install_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
python -m build -nwx .
python -m pip install --upgrade ./dist/*.whl
python -m pip install pytest
python -m pip install rdflib
shell: bash
- name: Run tests
run: pytest
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,12 @@ instead of `bin`.

# Dependencies

* PLY: https://pypi.python.org/pypi/ply/ used for parsing.
* rdflib: https://pypi.python.org/pypi/rdflib/ for handling RDF.
* PyYAML: https://pypi.org/project/PyYAML/ for handling YAML.
* xmltodict: https://pypi.org/project/xmltodict/ for handling XML.
* click: https://pypi.org/project/click/ for creating the CLI interface.
* rdflib: https://pypi.python.org/pypi/rdflib/ for handling RDF.
* typeguard: https://pypi.org/project/typeguard/ for using typehints.
* uritools: https://pypi.org/project/uritools/ for validation of URIs.

# Support

Expand Down
19 changes: 11 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"

[project]
name = "spdx-tools"
authors = [{name = "Ahmed H. Ismail", email = "[email protected]"}]
authors = [{ name = "Ahmed H. Ismail", email = "[email protected]" }]
maintainers = [
{name = "Philippe Ombredanne", email = "[email protected]"},
{name = "SPDX group at the Linux Foundation and others"},
{ name = "Philippe Ombredanne", email = "[email protected]" },
{ name = "SPDX group at the Linux Foundation and others" },
]
license = {text = "Apache-2.0"}
license = { text = "Apache-2.0" }
description = "SPDX parser and tools."
readme = "README.md"
classifiers = [
Expand All @@ -22,13 +22,14 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
]
urls = {Homepage = "https://github.com/spdx/tools-python"}
urls = { Homepage = "https://github.com/spdx/tools-python" }
requires-python = ">=3.7"
dependencies = ["ply", "rdflib", "click", "pyyaml", "xmltodict", "typeguard", "uritools", "license_expression"]
dependencies = ["click", "pyyaml", "xmltodict", "rdflib", "typeguard", "uritools", "license_expression"]
dynamic = ["version"]

[project.optional-dependencies]
test = ["pytest"]
test = ["pytest", "rdflib"]
rdf = ["rdflib"]

[project.scripts]
pyspdxtools = "spdx.clitools.pyspdxtools:main"
Expand All @@ -40,8 +41,10 @@ include-package-data = true
[tool.setuptools.packages.find]
where = ["src"]

# the default git describe resolves to the tag `python3.6` because the current release tag is not on main
# by adding "v" the command resolves to the alpha release of 0.7.0 which leads to the desired name spdx-tools-0.7.0
[tool.setuptools_scm]
git_describe_command = ["git", "describe", "--dirty", "--tags", "--long", "--match", "v[0-9]*"] # `python3.6` tag falsely matches to the default one, clrearly a bug in setuptools_scm
git_describe_command = ["git", "describe", "--dirty", "--tags", "--long", "--match", "v[0-9]*"]

[tool.aliases]
release = "clean --all sdist --formats=gztar bdist_wheel"
3 changes: 3 additions & 0 deletions src/spdx/model/actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ def to_serialized_string(self) -> str:
"""
optional_email = f" ({self.email})" if self.email else ""
return "".join([f"{self.actor_type.name.title()}:", f" {self.name}", optional_email])

def __str__(self):
return self.to_serialized_string()
3 changes: 2 additions & 1 deletion src/spdx/model/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ class ExternalPackageRefCategory(Enum):
CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES: Dict[ExternalPackageRefCategory, List[str]] = {
ExternalPackageRefCategory.SECURITY : ["cpe22Type", "cpe23Type", "advisory", "fix", "url", "swid"],
ExternalPackageRefCategory.PACKAGE_MANAGER : ["maven-central", "npm", "nuget", "bower", "purl"],
ExternalPackageRefCategory.PERSISTENT_ID : ["swh", "gitoid"]
ExternalPackageRefCategory.PERSISTENT_ID : ["swh", "gitoid"],
ExternalPackageRefCategory.OTHER: []
}


Expand Down
Empty file added src/spdx/rdfschema/__init__.py
Empty file.
16 changes: 16 additions & 0 deletions src/spdx/rdfschema/namespace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright (c) 2023 spdx contributors
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from rdflib import Namespace

SPDX_NAMESPACE = Namespace("http://spdx.org/rdf/terms#")
POINTER_NAMESPACE = Namespace("http://www.w3.org/2009/pointers#")
REFERENCE_NAMESPACE = Namespace("http://spdx.org/rdf/references/")
LICENSE_NAMESPACE = Namespace("http://spdx.org/licenses/")
Empty file added src/spdx/writer/rdf/__init__.py
Empty file.
34 changes: 34 additions & 0 deletions src/spdx/writer/rdf/annotation_writer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright (c) 2023 spdx contributors
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Dict

from rdflib import Graph, Literal, RDFS, URIRef, RDF, BNode

from spdx.datetime_conversions import datetime_to_iso_string
from spdx.model.annotation import Annotation
from spdx.writer.casing_tools import snake_case_to_camel_case
from spdx.writer.rdf.writer_utils import add_namespace_to_spdx_id
from spdx.rdfschema.namespace import SPDX_NAMESPACE


def add_annotation_to_graph(annotation: Annotation, graph: Graph, doc_namespace: str,
external_doc_ref_to_namespace: Dict[str, str]):
annotation_resource = URIRef(add_namespace_to_spdx_id(annotation.spdx_id, doc_namespace, external_doc_ref_to_namespace))
annotation_node = BNode()
graph.add((annotation_node, RDF.type, SPDX_NAMESPACE.Annotation))
graph.add((annotation_node, SPDX_NAMESPACE.annotationType,
SPDX_NAMESPACE[f"annotationType_{snake_case_to_camel_case(annotation.annotation_type.name)}"]))
graph.add((annotation_node, SPDX_NAMESPACE.annotator, Literal(annotation.annotator.to_serialized_string())))
graph.add(
(annotation_node, SPDX_NAMESPACE.annotationDate, Literal(datetime_to_iso_string(annotation.annotation_date))))
graph.add((annotation_node, RDFS.comment, Literal(annotation.annotation_comment)))

graph.add((annotation_resource, SPDX_NAMESPACE.annotation, annotation_node))
31 changes: 31 additions & 0 deletions src/spdx/writer/rdf/checksum_writer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright (c) 2023 spdx contributors
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from rdflib import Graph, URIRef, BNode, RDF, Literal

from spdx.model.checksum import Checksum, ChecksumAlgorithm
from spdx.rdfschema.namespace import SPDX_NAMESPACE


def add_checksum_to_graph(checksum: Checksum, graph: Graph, parent: URIRef):
checksum_node = BNode()
graph.add((checksum_node, RDF.type, SPDX_NAMESPACE.Checksum))
graph.add((checksum_node, SPDX_NAMESPACE.algorithm, algorithm_to_rdf_string(checksum.algorithm)))
graph.add((checksum_node, SPDX_NAMESPACE.checksumValue, Literal(checksum.value)))

graph.add((parent, SPDX_NAMESPACE.checksum, checksum_node))

def algorithm_to_rdf_string(algorithm: ChecksumAlgorithm) -> URIRef:
if "BLAKE2B" in algorithm.name:
algorithm_rdf_string = algorithm.name.replace("_","").lower()
else:
algorithm_rdf_string = algorithm.name.lower()

return SPDX_NAMESPACE[f"checksumAlgorithm_{algorithm_rdf_string}"]
45 changes: 45 additions & 0 deletions src/spdx/writer/rdf/creation_info_writer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright (c) 2023 spdx contributors
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from rdflib import Graph, BNode, RDF, Literal, RDFS, URIRef

from spdx.datetime_conversions import datetime_to_iso_string
from spdx.model.document import CreationInfo
from spdx.writer.rdf.external_document_ref_writer import add_external_document_ref_to_graph
from spdx.writer.rdf.writer_utils import add_optional_literal
from spdx.rdfschema.namespace import SPDX_NAMESPACE, LICENSE_NAMESPACE


def add_creation_info_to_graph(creation_info: CreationInfo, graph: Graph):
doc_node = URIRef(f"{creation_info.document_namespace}#{creation_info.spdx_id}")
graph.add((doc_node, RDF.type, SPDX_NAMESPACE.SpdxDocument))
graph.add((doc_node, SPDX_NAMESPACE.specVersion, Literal(creation_info.spdx_version)))
graph.add((doc_node, SPDX_NAMESPACE.dataLicense, LICENSE_NAMESPACE[creation_info.data_license]))
graph.add((doc_node, SPDX_NAMESPACE.name, Literal(creation_info.name)))
add_optional_literal(creation_info.document_comment, graph, doc_node, RDFS.comment)

creation_info_node = BNode()
graph.add((creation_info_node, RDF.type, SPDX_NAMESPACE.CreationInfo))

graph.add((creation_info_node, SPDX_NAMESPACE.created, Literal(datetime_to_iso_string(creation_info.created))))

for creator in creation_info.creators:
graph.add((creation_info_node, SPDX_NAMESPACE.creator, Literal(creator.to_serialized_string())))

add_optional_literal(creation_info.license_list_version, graph, creation_info_node,
SPDX_NAMESPACE.licenseListVersion)
add_optional_literal(creation_info.creator_comment, graph, creation_info_node, RDFS.comment)

graph.add((doc_node, SPDX_NAMESPACE.creationInfo, creation_info_node))

for external_document_ref in creation_info.external_document_refs:
add_external_document_ref_to_graph(external_document_ref, graph, doc_node, creation_info.document_namespace)

return doc_node
25 changes: 25 additions & 0 deletions src/spdx/writer/rdf/external_document_ref_writer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright (c) 2023 spdx contributors
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from rdflib import Graph, URIRef, RDF

from spdx.model.external_document_ref import ExternalDocumentRef
from spdx.writer.rdf.checksum_writer import add_checksum_to_graph
from spdx.rdfschema.namespace import SPDX_NAMESPACE


def add_external_document_ref_to_graph(external_document_ref: ExternalDocumentRef, graph: Graph, doc_node: URIRef,
doc_namespace: str):
external_document_ref_resource = URIRef(f"{doc_namespace}#{external_document_ref.document_ref_id}")
graph.add((external_document_ref_resource, RDF.type, SPDX_NAMESPACE.ExternalDocumentRef))
graph.add((external_document_ref_resource, SPDX_NAMESPACE.spdxDocument, URIRef(external_document_ref.document_uri)))
add_checksum_to_graph(external_document_ref.checksum, graph, external_document_ref_resource)

graph.add((doc_node, SPDX_NAMESPACE.externalDocumentRef, external_document_ref_resource))
35 changes: 35 additions & 0 deletions src/spdx/writer/rdf/extracted_licensing_info_writer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright (c) 2023 spdx contributors
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from rdflib import Graph, URIRef, RDF, BNode, RDFS, Literal
from spdx.writer.rdf.writer_utils import add_optional_literal, add_literal_or_no_assertion
from spdx.rdfschema.namespace import SPDX_NAMESPACE

from spdx.model.extracted_licensing_info import ExtractedLicensingInfo


def add_extracted_licensing_info_to_graph(extracted_licensing_info: ExtractedLicensingInfo, graph: Graph, doc_node,
doc_namespace: str):
if extracted_licensing_info.license_id:
extracted_licensing_info_resource = URIRef(f"{doc_namespace}#{extracted_licensing_info.license_id}")
graph.add((extracted_licensing_info_resource, RDF.type, SPDX_NAMESPACE.ExtractedLicensingInfo))
else:
extracted_licensing_info_resource = BNode()
add_optional_literal(extracted_licensing_info.license_id, graph, extracted_licensing_info_resource,
SPDX_NAMESPACE.licenseId)
add_optional_literal(extracted_licensing_info.extracted_text, graph, extracted_licensing_info_resource,
SPDX_NAMESPACE.extractedText)
add_literal_or_no_assertion(extracted_licensing_info.license_name, graph, extracted_licensing_info_resource,
SPDX_NAMESPACE.name)
for cross_reference in extracted_licensing_info.cross_references:
graph.add((extracted_licensing_info_resource, RDFS.seeAlso, Literal(cross_reference)))
add_optional_literal(extracted_licensing_info.comment, graph, extracted_licensing_info_resource, RDFS.comment)

graph.add((doc_node, SPDX_NAMESPACE.hasExtractedLicensingInfo, extracted_licensing_info_resource))
47 changes: 47 additions & 0 deletions src/spdx/writer/rdf/file_writer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright (c) 2023 spdx contributors
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Dict

from rdflib import Graph, URIRef, Literal, RDF, RDFS

from spdx.model.file import File
from spdx.writer.casing_tools import snake_case_to_camel_case
from spdx.writer.rdf.checksum_writer import add_checksum_to_graph
from spdx.writer.rdf.license_expression_writer import add_license_expression_or_none_or_no_assertion
from spdx.writer.rdf.writer_utils import add_optional_literal, add_namespace_to_spdx_id
from spdx.rdfschema.namespace import SPDX_NAMESPACE


def add_file_to_graph(file: File, graph: Graph, doc_namespace: str,
external_doc_ref_to_namespace: Dict[str, str]):
file_resource = URIRef(add_namespace_to_spdx_id(file.spdx_id, doc_namespace, external_doc_ref_to_namespace))
graph.add((file_resource, RDF.type, SPDX_NAMESPACE.File))
graph.add((file_resource, SPDX_NAMESPACE.fileName, Literal(file.name)))
for file_type in file.file_type:
graph.add((file_resource, SPDX_NAMESPACE.fileType,
SPDX_NAMESPACE[f"fileType_{snake_case_to_camel_case(file_type.name)}"]))

for checksum in file.checksums:
add_checksum_to_graph(checksum, graph, file_resource)

add_license_expression_or_none_or_no_assertion(file.license_concluded, graph, file_resource,
SPDX_NAMESPACE.licenseConcluded, doc_namespace)
add_license_expression_or_none_or_no_assertion(file.license_info_in_file, graph, file_resource,
SPDX_NAMESPACE.licenseInfoInFile, doc_namespace)

add_optional_literal(file.license_comment, graph, file_resource, SPDX_NAMESPACE.licenseComments)
add_optional_literal(file.copyright_text, graph, file_resource, SPDX_NAMESPACE.copyrightText)
add_optional_literal(file.comment, graph, file_resource, RDFS.comment)
add_optional_literal(file.notice, graph, file_resource, SPDX_NAMESPACE.noticeText)
for contributor in file.contributors:
graph.add((file_resource, SPDX_NAMESPACE.fileContributor, Literal(contributor)))
for attribution_text in file.attribution_texts:
graph.add((file_resource, SPDX_NAMESPACE.attributionText, Literal(attribution_text)))
Loading