# SPDX-FileCopyrightText: 2025-present Arthit Suriyawongkul <suriyawa@tcd.ie>
# SPDX-FileType: SOURCE
# SPDX-License-Identifier: Apache-2.0
Using SPDX 3 in Python with spdx-python-model¶
- This tutorial demonstrates how to use the spdx-python-model library to:
- read the software bill of materials (SBOM) data in the System Package Data Exchange (SPDX) format from an SPDX 3 JSON file;
- access SPDX objects and their relationships; and
- create a simple SPDX object (also demonstrating the built-in data validation of the library)
- The SBOM 3 JSON file examples are taken from AI Example 2 and Software Example 14 of the spdx-examples repository.
Install the spdx-python-model library¶
%pip install spdx-python-model
Requirement already satisfied: spdx-python-model in /Users/art/.pyenv/versions/3.10.20/envs/spdx-python-model310/lib/python3.10/site-packages (0.0.6) Note: you may need to restart the kernel to use updated packages.
Import and check library version¶
import pkgutil
from spdx_python_model import __version__, bindings
print(f"spdx-python-model library version: {__version__}")
print("Available bindings:")
for module_info in pkgutil.iter_modules(bindings.__path__):
print(module_info.name)
spdx-python-model library version: 0.0.6 Available bindings: v3_0_1 v3_1
To work with SPDX data, import the binding for the SPDX version you need.
For example, to import the binding for SPDX 3.0.1, import v3_0_1.
from spdx_python_model import v3_0_1
Or you may like to assign it to a versionless alias,
so you can update the version easily later.
For example, from spdx_python_model import v3_0_1 as spdx3.
When a new version (e.g., 3.1) is released, you only need to update the
import line and can keep the rest of your code intact.
For example, from spdx_python_model import v3_1 as spdx3.
The library also provides a way to automatically select the correct binding from a file's content, without needing to specify the version manually — we will cover that in the next section.
Once imported, we can now use the binding to access SPDX 3 classes, properties, and vocabulary terms.
Let's print some "named individuals" from the binding.
list(v3_0_1.NAMED_INDIVIDUALS)[:8]
['https://spdx.org/rdf/3.0.1/terms/Dataset/DatasetType/timeseries', 'https://spdx.org/rdf/3.0.1/terms/Core/AnnotationType/other', 'https://spdx.org/rdf/3.0.1/terms/Core/ExternalRefType/privacyAssessment', 'https://spdx.org/rdf/3.0.1/terms/AI/SafetyRiskAssessmentType/high', 'https://spdx.org/rdf/3.0.1/terms/Core/ExternalRefType/funding', 'https://spdx.org/rdf/3.0.1/terms/Core/RelationshipType/coordinatedBy', 'https://spdx.org/rdf/3.0.1/terms/Core/RelationshipType/doesNotAffect', 'https://spdx.org/rdf/3.0.1/terms/Core/RelationshipType/other']
Some of these could be entries in SPDX 3 vocabularies (enum).
Read SPDX data from SPDX 3 JSON file¶
The library offers a load() function that reads an SPDX 3 JSON file and
automatically selects the correct Python binding based on the @context URL
in the file.
It returns a tuple of (model, object_set) where:
modelis the version-specific binding (e.g.,v3_0_1) matching the file's context; andobject_setis aSHACLObjectSetpopulated with the objects from the file.
SHACLObjectSet holds SHACLObjects, which are the primary data structures
for interaction within this library. It also includes helpful utilities,
such as a generator (iterator) for accessing objects by their type.
In this example, we will first download the JSON file from the spdx-examples
repository.
from urllib.request import urlretrieve
from pathlib import Path
import spdx_python_model
# url = "https://raw.githubusercontent.com/spdx/spdx-examples/refs/heads/master/ai/example02/spdx3.0/sbom.spdx3.json"
url = "https://raw.githubusercontent.com/spdx/spdx-examples/refs/heads/master/software/example14/spdx3.0/examplemaven-0.0.1-enriched.spdx3.json"
filename = "sbom.spdx.json"
filepath, _ = urlretrieve(url, filename) # download from url; get the filepath
spdx3, object_set = spdx_python_model.load(Path(filepath))
print(object_set)
<spdx_python_model.bindings.v3_0_1.model.SHACLObjectSet object at 0x10a678460>
load() returns a (model, object_set) tuple.
Here, model is the version-specific Python binding (e.g., v3_0_1)
automatically selected based on the @context URL in the file.
We assign it to spdx3 as the versionless alias introduced earlier.
The object_set is a SHACLObjectSet populated with all the SPDX objects
from the file, which we will explore in the next section.
Some internal works of SHACLObjectSet¶
Internally SHACLObjectSet keeps objects in its objects set.
# see what is inside
print(list(object_set.objects)[:3])
[<spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a615000>, <spdx_python_model.bindings.v3_0_1.model.software_Package object at 0x10a207010>, <spdx_python_model.bindings.v3_0_1.model.Person object at 0x10a652830>]
Internally, for faster lookup, SHACLObjectSet maintains two indexes:
obj_by_id and obj_by_type.
obj_by_id is a dictionary where:
- Keys are the
spdxIdproperty values of the objects. - Values are the corresponding SHACLObject instances.
obj_by_type is a dictionary where:
- Keys are the
typeproperty values of the objects. (e.g., "SpdxDocument", "software_Package", "simplelicensing_LicenseExpression").- Note: Core namespace does not require a prefix, while other namespaces do (e.g., "software_"). This follows convention for SPDX 3 JSON serialization.
- Values are sets of tuples. Each tuple contains:
- A boolean indicating if the SHACLObject in this tuple is an exact type match (True) or a subclass match (False); and
- The SHACLObject instance itself.
any_license_infos = object_set.obj_by_type["simplelicensing_AnyLicenseInfo"]
print(any_license_infos)
{(False, <spdx_python_model.bindings.v3_0_1.model.simplelicensing_LicenseExpression object at 0x10a5b31a0>), (False, <spdx_python_model.bindings.v3_0_1.model.simplelicensing_LicenseExpression object at 0x10a5b2d40>), (False, <spdx_python_model.bindings.v3_0_1.model.simplelicensing_LicenseExpression object at 0x10a5b2a20>), (False, <spdx_python_model.bindings.v3_0_1.model.simplelicensing_LicenseExpression object at 0x10a5b2c00>)}
Let's begin with a quick check.
For example, we can see how many SpdxDocument objects are present and if it is according to the specification.
See: https://spdx.github.io/spdx-spec/v3.0.1/serializations/#serialization-information
spdx_documents = list(object_set.obj_by_type["SpdxDocument"])
if len(spdx_documents) > 1:
print("Warning: A serialization must not contain more than one SpdxDocument.")
else:
print("Looks good.")
Looks good.
# Get all objects of type "Relationship", including its subclasses
relationships = object_set.obj_by_type["Relationship"]
print("Relationship found:", len(relationships))
for o in relationships:
print(o)
print()
# Get all objects of type "LifecycleScopedRelationship", including its subclasses
lifecycle_scoped_relationships = object_set.obj_by_type["LifecycleScopedRelationship"]
print("LifecycleScopedRelationship found:", len(lifecycle_scoped_relationships))
for o in lifecycle_scoped_relationships:
print(o)
print()
# Get all objects of type "Softare/Package", including its subclasses
packages = object_set.obj_by_type["software_Package"]
print("software_Package found:", len(packages))
for o in packages:
print(o)
print()
Relationship found: 28 (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a614040>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a614b80>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a6156c0>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a614c40>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a615780>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a614d00>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a615840>) (False, <spdx_python_model.bindings.v3_0_1.model.LifecycleScopedRelationship object at 0x10a615cc0>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a614dc0>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a615900>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a614e80>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a6159c0>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a614f40>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a615a80>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a615000>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a615600>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a615b40>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a614580>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a4c3640>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a6150c0>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a615180>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a614700>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a615240>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a615300>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a6153c0>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a615480>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a615540>) (True, <spdx_python_model.bindings.v3_0_1.model.Relationship object at 0x10a614ac0>) LifecycleScopedRelationship found: 1 (True, <spdx_python_model.bindings.v3_0_1.model.LifecycleScopedRelationship object at 0x10a615cc0>) software_Package found: 6 (True, <spdx_python_model.bindings.v3_0_1.model.software_Package object at 0x10a207120>) (True, <spdx_python_model.bindings.v3_0_1.model.software_Package object at 0x10a206ce0>) (True, <spdx_python_model.bindings.v3_0_1.model.software_Package object at 0x10a207010>) (True, <spdx_python_model.bindings.v3_0_1.model.software_Package object at 0x10a207340>) (True, <spdx_python_model.bindings.v3_0_1.model.software_Package object at 0x10a206f00>) (True, <spdx_python_model.bindings.v3_0_1.model.software_Package object at 0x10a206df0>)
A proper way to get access to SPDX objects¶
While direct access to obj_by_id and obj_by_type is possible,
it is generally recommended to use the provided access methods for better
encapsulation. These methods include find_by_id and foreach_type.
SHACLObjectSet offers the find_by_id method to retrieve a specific object
using its unique spdxId.
obj = object_set.find_by_id("https://spdx.org/spdxdocs/Sbom1-b1b4ff24-5ada-4c22-a9c9-7bd7121978ff")
print(obj)
None
SHACLObjectSet also provides foreach_type, a generator method that
iterates over all objects of a specified type.
By default, foreach_type will also return instances of subclasses.
for obj in object_set.foreach_type("Relationship"):
print(obj)
Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd5') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd11') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd32') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd13') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd33') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd14') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd36') LifecycleScopedRelationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd45') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd15') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd38') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd16') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd39') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd18') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd46') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd20') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd31') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd48') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd7') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd3') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd21') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd23') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd8') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd24') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd25') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd27') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd28') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd29') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd10')
Notice the presence of LifecycleScopedRelationship, which is a subclass of Relationship.
Set match_subclass argument to False, to exclude subclasses.
for obj in object_set.foreach_type("Relationship", match_subclass=False):
print(obj)
Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd5') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd11') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd32') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd13') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd33') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd14') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd36') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd15') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd38') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd16') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd39') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd18') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd46') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd20') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd31') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd48') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd7') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd3') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd21') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd23') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd8') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd24') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd25') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd27') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd28') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd29') Relationship(@id='http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd10')
Notice the absence of LifecycleScopedRelationship this time.
Because foreach_type is a generator, you can use it like this as well:
objs = set(object_set.foreach_type("Relationship", match_subclass=False))
print(len(objs))
27
Accessing SPDX properties¶
SPDX objects and their properties can be accessed directly from Python attributes.
For example, this SpdxDocument doc has an attribute creationInfo
and the value of creationInfo is a CreationInfo object.
We can directly access specVersion attribute of the CreationInfo object like this.
doc = list(object_set.foreach_type("SpdxDocument", match_subclass=False))[0]
print("SPDX Spec Version:", doc.creationInfo.specVersion)
root_element = doc.rootElement[0] # assume we have only one root element
print("Root element ID:", root_element.spdxId)
SPDX Spec Version: 3.0.1 Root element ID: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd4
The SPDX specification defines the classes and properties of SPDX models.
For example, the SpdxDocument class is detailed at: https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Classes/SpdxDocument/
Put things together¶
We can now perform basic object access. Let's try to visualize an SPDX 3 SBOM.
The example below will iterate through all relationships and print relationship types between SPDX objects.
def print_object_info(obj, prefix=""):
"""helper function to print a box containing object info.
obj can be either SHACLObject or just spdxId (string)."""
print(f"{prefix}┏" + "━"*50 + "┅")
if isinstance(obj, spdx3.SHACLObject):
print(f"{prefix}┃ {obj.__class__.__name__}")
print(f"{prefix}┃ - name: {getattr(obj, 'name', 'N/A')}")
print(f"{prefix}┃ - spdxId: {obj.spdxId}")
else: # assume it's an spdxId for an external object
print(f"{prefix}┃ (Unresolved spdxId)")
print(f"{prefix}┃ - spdxId: {obj}")
print(f"{prefix}┗" + "━"*50 + "┅")
for rel in object_set.foreach_type("Relationship"):
# spdx-python-model uses "from_" instead of "from" to avoid the Python keyword
from_obj = getattr(rel, "from_")
to_objs = getattr(rel, "to")
# print only the type name, not the full URL, for conciseness
rel_type = getattr(rel, "relationshipType").split("/")[-1]
print_object_info(from_obj)
print( " │")
print(f" {rel_type}")
print( " ↓")
for o in to_objs:
print_object_info(o, prefix=" ")
print()
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: examplemaven
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd4
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
contains
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_File
┃ - name: ./src/main/java/org/spdx/examplemaven/App.java
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd6
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: examplemaven
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd4
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
contains
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_File
┃ - name: ./src/test/java/org/spdx/examplemaven/AppTest.java
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd12
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: Apache Log4j SLF4J Binding
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd30
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasDeclaredLicense
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ simplelicensing_LicenseExpression
┃ - name: None
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd19
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: examplemaven
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd4
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasTestCase
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_File
┃ - name: ./src/test/java/org/spdx/examplemaven/AppTest.java
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd12
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: examplemaven
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd4
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasConcludedLicense
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ simplelicensing_LicenseExpression
┃ - name: None
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd9
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_File
┃ - name: ./src/test/java/org/spdx/examplemaven/AppTest.java
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd12
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasConcludedLicense
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ simplelicensing_LicenseExpression
┃ - name: None
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd9
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: examplemaven
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd4
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasDistributionArtifact
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_File
┃ - name: examplemaven-0.0.1.jar
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd35
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: examplemaven
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd4
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
dependsOn
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: JUnit
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd44
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_File
┃ - name: ./src/test/java/org/spdx/examplemaven/AppTest.java
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd12
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasDeclaredLicense
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ simplelicensing_LicenseExpression
┃ - name: None
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd9
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: examplemaven
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd4
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasDeclaredLicense
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ simplelicensing_LicenseExpression
┃ - name: None
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd9
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: Apache Log4j Core
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd17
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasDynamicLink
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: examplemaven
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd4
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ (Unresolved spdxId)
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1#SPDXRef-DOCUMENT
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
amendedBy
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ SpdxDocument
┃ - name: examplemaven
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/document0
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: Apache Log4j Core
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd17
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasConcludedLicense
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ simplelicensing_LicenseExpression
┃ - name: None
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd19
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: JUnit
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd44
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasConcludedLicense
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ simplelicensing_LicenseExpression
┃ - name: None
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd19
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: Apache Log4j Core
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd17
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasDeclaredLicense
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ simplelicensing_LicenseExpression
┃ - name: None
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd19
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: Apache Log4j SLF4J Binding
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd30
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasConcludedLicense
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ simplelicensing_LicenseExpression
┃ - name: None
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd19
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: JUnit
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd44
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasDeclaredLicense
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ simplelicensing_LicenseExpression
┃ - name: None
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd49
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_File
┃ - name: ./src/main/java/org/spdx/examplemaven/App.java
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd6
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
generates
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: examplemaven
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd4
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ SpdxDocument
┃ - name: examplemaven
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/document0
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
describes
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: examplemaven
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd4
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: Apache Log4j API
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd22
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasDynamicLink
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: examplemaven
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd4
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: Apache Log4j API
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd22
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasConcludedLicense
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ simplelicensing_LicenseExpression
┃ - name: None
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd19
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_File
┃ - name: ./src/main/java/org/spdx/examplemaven/App.java
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd6
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasConcludedLicense
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ simplelicensing_LicenseExpression
┃ - name: None
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd9
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: Apache Log4j API
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd22
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasDeclaredLicense
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ simplelicensing_LicenseExpression
┃ - name: None
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd19
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: SLF4J API Module
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd26
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasDynamicLink
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: examplemaven
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd4
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: SLF4J API Module
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd26
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasConcludedLicense
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ simplelicensing_LicenseExpression
┃ - name: None
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd19
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: SLF4J API Module
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd26
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasDeclaredLicense
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ simplelicensing_LicenseExpression
┃ - name: None
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd19
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: Apache Log4j SLF4J Binding
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd30
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasDynamicLink
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_Package
┃ - name: examplemaven
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd4
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ software_File
┃ - name: ./src/main/java/org/spdx/examplemaven/App.java
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd6
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
│
hasDeclaredLicense
↓
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
┃ simplelicensing_LicenseExpression
┃ - name: None
┃ - spdxId: http://spdx.org/documents/examplemaven-0.0.1/enriched-specv3/SPDXRef-gnrtd9
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅
SPDX object creation and built-in validation¶
To create an SPDX object, simply use the standard constructor as you would with any native Python class.
package = spdx3.ai_AIPackage()
package
<spdx_python_model.bindings.v3_0_1.model.ai_AIPackage at 0x10a6acb30>
The spdx-python-model library features built-in SHACL-based validation derived directly from the SPDX 3 model. This validation ensures data integrity by rejecting invalid types at runtime.
For example, the domain property of /AI/AIPackage has a
cardinality of 0..* (minimum count: 0, maximum count: unbounded).
This requires the value to be a list;
if a different type is provided, the library will raise a TypeError.
package.ai_domain = "nlp"
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[18], line 1 ----> 1 package.ai_domain = "nlp" File ~/projects/spdx-python-model/src/spdx_python_model/bindings/v3_0_1/model.py:1075, in SHACLObject.__setattr__(self, name, value) 1072 object.__setattr__(self, name, value) 1073 return -> 1075 self.__set(self.__get_attr(name), value) File ~/projects/spdx-python-model/src/spdx_python_model/bindings/v3_0_1/model.py:1048, in SHACLObject.__set(self, p, value) 1043 if not is_blank_node(value) and not is_IRI(value): 1044 raise ValueError( 1045 f"{self.__class__.__name__} ({id(self)}) Has invalid Property '{p.iri}' {value!r}. Must be a blank node or IRI" 1046 ) -> 1048 p.prop.validate(value) 1049 if p.deprecated: 1050 warnings.warn( 1051 f"{self.__class__.__name__}.{p.pyname} is deprecated", 1052 DeprecationWarning, 1053 ) File ~/projects/spdx-python-model/src/spdx_python_model/bindings/v3_0_1/model.py:570, in ListProp.validate(self, value) 569 def validate(self, value: Any) -> None: --> 570 super().validate(value) 572 for i in value: 573 self.prop.validate(i) File ~/projects/spdx-python-model/src/spdx_python_model/bindings/v3_0_1/model.py:78, in Property.validate(self, value) 77 def validate(self, value: Any) -> None: ---> 78 check_type(value, self.VALID_TYPES) 79 if self.pattern is not None and not re.search( 80 self.pattern, self.to_string(value) 81 ): 82 raise ValueError( 83 f"Value is not correctly formatted. Got '{self.to_string(value)}'" 84 ) File ~/projects/spdx-python-model/src/spdx_python_model/bindings/v3_0_1/model.py:53, in check_type(obj, types) 51 if not isinstance(obj, types): 52 if isinstance(types, (list, tuple)): ---> 53 raise TypeError( 54 f"Value must be one of type: {', '.join(t.__name__ for t in types)}. Got {type(obj).__name__}" 55 ) 56 raise TypeError( 57 f"Value must be of type {types.__name__}. Got {type(obj).__name__}" 58 ) TypeError: Value must be one of type: list, ListProxy. Got str
Even if there is only a single entry, you must provide it as a single-element list to satisfy the property's cardinality:
package.ai_domain = ["nlp"]
package.ai_domain
['nlp']
Refer to the SPDX specification for detailed information on property types and cardinality.
That's it¶
That's it for now! Hope you enjoyed the tutorial.
Find more examples of how to use the library:
spdx-python-modeltests: https://github.com/spdx/spdx-python-model/blob/main/tests/test_import.pyshacl2code(which generates spdx-python-model's Python binding) tests: https://github.com/JPEWdev/shacl2code/blob/main/tests/test_python.py
There are many ways to get involved with the SPDX community. If you have questions about using SPDX, feel free to join one of our online meetings.
Change log¶
- Update: 24 June 2026 - update code to spdx-python-model 0.0.6; included as part of spdx-python-model documentation
- Update: 21 May 2026 - fix handling of unresolved references (spdxId) (reported by @vargenau and suggested a fix by @benjarobin - thank you both!)
- Update: 23 February 2026 - add a section on SPDX object creation and validation
- First published: 7 July 2025 - as part of @bact Arthit Suriyawongkul's Google Summer of Code 2025 program contribution to NTIA Conformance Checker (mentors: Gary O'Neall, John Speed Meyers, and Joshua Watt).