Skip to content

Instantly share code, notes, and snippets.

@bact
Last active February 23, 2026 10:41
Show Gist options
  • Select an option

  • Save bact/7227ad858500c2097a25344a4af015d6 to your computer and use it in GitHub Desktop.

Select an option

Save bact/7227ad858500c2097a25344a4af015d6 to your computer and use it in GitHub Desktop.
spdx-python-model-tutorial.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"authorship_tag": "ABX9TyMWSxluqwm98LiTXdzCLMqI",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/bact/7227ad858500c2097a25344a4af015d6/spdx-python-model-tutorial.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "code",
"source": [
"# SPDX-FileCopyrightText: 2025-present Arthit Suriyawongkul <suriyawa@tcd.ie>\n",
"# SPDX-FileType: SOURCE\n",
"# SPDX-License-Identifier: Apache-2.0"
],
"metadata": {
"id": "fTKOUcN_esq8"
},
"execution_count": 1,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"# Using SPDX 3 in Python with spdx-python-model\n",
"\n",
"- This tutorial demonstrates how to use the [spdx-python-model][] library to:\n",
" - read the software bill of materials (SBOM) data in the [System Package Data Exchange (SPDX)][spdx] format from an [SPDX 3 JSON][spdx-3-json] file;\n",
" - access SPDX objects and their relationships; and\n",
" - create a simple SPDX object (also demonstrating the built-in data validation of the library)\n",
"\n",
"- The SBOM 3 JSON file example is taken from [AI Example 2][ai-example-2] of the spdx-examples repository.\n",
"- This documentation is a contribution to the [NTIA Conformance Checker][ntia-conformance-checker] as part of the [Google Summer of Code 2025 program][gsoc-2025].\n",
"\n",
"by [@bact][bact] - Arthit Suriyawongkul\n",
"\n",
"## Change log\n",
"\n",
"- First published: 7 July 2025\n",
"- Updated: 23 February 2026 - add a section on SPDX object creation and validation\n",
"\n",
"[spdx]: https://spdx.dev/use/specifications/\n",
"[spdx-python-model]: https://github.com/spdx/spdx-python-model\n",
"[spdx-3-json]: https://github.com/spdx/spdx-spec/blob/develop/docs/serializations.md#serialization-in-spdx-3-json\n",
"[ai-example-2]: https://github.com/spdx/spdx-examples/tree/master/ai/example02/spdx3.0\n",
"[ntia-conformance-checker]: https://github.com/spdx/ntia-conformance-checker\n",
"[gsoc-2025]: https://summerofcode.withgoogle.com/programs/2025/projects/CeR3hQTq\n",
"[bact]: https://github.com/bact/"
],
"metadata": {
"id": "09M0uv5_Bk7B"
}
},
{
"cell_type": "markdown",
"source": [
"## Install the spdx-python-model library"
],
"metadata": {
"id": "_ZRVMOBvQDuO"
}
},
{
"cell_type": "code",
"source": [
"!pip install spdx-python-model"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "JsENPjgwHlHc",
"outputId": "1da52411-c9fa-4926-87b6-a849b3f20d2c"
},
"execution_count": 2,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Requirement already satisfied: spdx-python-model in /usr/local/lib/python3.12/dist-packages (0.0.4)\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"## Import and check library version"
],
"metadata": {
"id": "0rhToD4DQQ0-"
}
},
{
"cell_type": "code",
"source": [
"from spdx_python_model import VERSION, bindings\n",
"\n",
"print(f\"spdx-python-model library version: {VERSION}\")\n",
"\n",
"print(\"Available SPDX bindings in spdx_python_model module:\")\n",
"for name in dir(bindings):\n",
" if not name.startswith(\"__\"):\n",
" print(name)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "977gguFuH1Nt",
"outputId": "26cfcc76-9364-486f-9ef2-1559581facb3"
},
"execution_count": 3,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"spdx-python-model library version: 0.0.4\n",
"Available SPDX bindings in spdx_python_model module:\n",
"v3_0_1\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"To use a Python binding, import a specific version of the model.\n",
"\n",
"For example, to import a binding for SPDX model version 3.0.1, import `v3_0_1`."
],
"metadata": {
"id": "yuweD-jan0A8"
}
},
{
"cell_type": "code",
"source": [
"from spdx_python_model import v3_0_1"
],
"metadata": {
"id": "MW10UJY2n7Ot"
},
"execution_count": 4,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Or you may like to assign it to a versionless alias,\n",
"so you can update the version easily later.\n",
"\n",
"For example, this will set the import alias as `spdx3`."
],
"metadata": {
"id": "v7E3Ys6BoBKQ"
}
},
{
"cell_type": "code",
"source": [
"from spdx_python_model import v3_0_1 as spdx3"
],
"metadata": {
"id": "bgKEojPtn_yC"
},
"execution_count": 5,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"If a new version (e.g., v3_0_2) is released, you only need to update the import line and can keep the rest of your code intact.\n",
"\n",
"For example, `from spdx_python_model import v3_0_2 as spdx3`."
],
"metadata": {
"id": "PC_h1XmSpIF3"
}
},
{
"cell_type": "markdown",
"source": [
"Once imported, we can now access SPDX 3 model.\n",
"\n",
"Let's print some \"named individuals\" from the model."
],
"metadata": {
"id": "9bWRkIYvpkzO"
}
},
{
"cell_type": "code",
"source": [
"list(spdx3.NAMED_INDIVIDUALS)[:8]"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "jh4qYAep0d-L",
"outputId": "00cccf0d-a666-44e1-f062-6f80a637ed27"
},
"execution_count": 6,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"['https://spdx.org/rdf/3.0.1/terms/Core/RelationshipType/delegatedTo',\n",
" 'https://spdx.org/rdf/3.0.1/terms/Core/ExternalRefType/buildMeta',\n",
" 'https://spdx.org/rdf/3.0.1/terms/Dataset/DatasetType/sensor',\n",
" 'https://spdx.org/rdf/3.0.1/terms/Core/ProfileIdentifierType/core',\n",
" 'https://spdx.org/rdf/3.0.1/terms/Core/ExternalIdentifierType/other',\n",
" 'https://spdx.org/rdf/3.0.1/terms/Security/ExploitCatalogType/kev',\n",
" 'https://spdx.org/rdf/3.0.1/terms/Core/ExternalRefType/issueTracker',\n",
" 'https://spdx.org/rdf/3.0.1/terms/Core/ExternalRefType/other']"
]
},
"metadata": {},
"execution_count": 6
}
]
},
{
"cell_type": "markdown",
"source": [
"Some of these could be entries in SPDX 3 vocabularies (enum)."
],
"metadata": {
"id": "x6C0wvWRqDIP"
}
},
{
"cell_type": "markdown",
"source": [
"## Read SPDX data from SPDX 3 JSON file"
],
"metadata": {
"id": "LeTPh0lsQZBw"
}
},
{
"cell_type": "markdown",
"source": [
"The library offers a built-in JSON-LD deserializer.\n",
"To use it, provide the path to your SPDX 3 JSON file and an instance of SHACLObjectSet.\n",
"The deserializer will read the JSON file and populate the SHACLObjectSet.\n",
"\n",
"SHACLObjectSet holds SHACLObjects, which are the primary data structures for\n",
"interaction within this library. It also includes helpful utilities, such as\n",
"a generator (iterator) for accessing objects by their type.\n",
"\n",
"In this example, we will first download the JSON file from the `spdx-examples` repository."
],
"metadata": {
"id": "wak3vYKObfXI"
}
},
{
"cell_type": "code",
"source": [
"from urllib.request import urlretrieve\n",
"\n",
"url = \"https://raw.githubusercontent.com/spdx/spdx-examples/refs/heads/master/ai/example02/spdx3.0/sbom.spdx.json\"\n",
"filename = \"sbom.spdx.json\"\n",
"filepath, _ = urlretrieve(url, filename) # download from url; get the filepath\n",
"\n",
"object_set = spdx3.SHACLObjectSet()\n",
"\n",
"with open(filepath, \"r\", encoding=\"utf-8\") as f:\n",
" spdx3.JSONLDDeserializer().read(f, object_set)\n",
"\n",
"print(object_set)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "JQ9lTkNdIckN",
"outputId": "500d4741-0ccd-4982-b7b6-36ff24674063"
},
"execution_count": 7,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"<spdx_python_model.bindings.v3_0_1.SHACLObjectSet object at 0x7d437200bbf0>\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"## Some internal works of SHACLObjectSet"
],
"metadata": {
"id": "8llGik8aQdct"
}
},
{
"cell_type": "markdown",
"source": [
"Internally SHACLObjectSet keeps objects in its `objects` set."
],
"metadata": {
"id": "jbEuuu53b0Eb"
}
},
{
"cell_type": "code",
"source": [
"# see what is inside\n",
"print(list(object_set.objects)[:3])"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "y1z84-MK6cnx",
"outputId": "196aa8bf-7eba-4978-8600-686d1d00f8dd"
},
"execution_count": 8,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"[<spdx_python_model.bindings.v3_0_1.software_File object at 0x7d4371e40800>, <spdx_python_model.bindings.v3_0_1.ai_AIPackage object at 0x7d4371e41040>, <spdx_python_model.bindings.v3_0_1.Relationship object at 0x7d4371f9d040>]\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"Internally, for faster lookup, SHACLObjectSet maintains two indexes:\n",
"`obj_by_id` and `obj_by_type`.\n",
"\n",
"`obj_by_id` is a dictionary where:\n",
"- Keys are the `spdxId` property values of the objects.\n",
"- Values are the corresponding SHACLObject instances.\n",
"\n",
"`obj_by_type` is a dictionary where:\n",
"- Keys are the `type` property values of the objects.\n",
" (e.g., \"SpdxDocument\", \"software_Package\", \"simplelicensing_LicenseExpression\").\n",
" Note: Core Profile types do not require a prefix, while other Profiles do (e.g., \"software_\").\n",
"- Values are sets of tuples. Each tuple contains:\n",
" 1. A boolean indicating if the SHACLObject in this tuple is an exact type match (True)\n",
" or a subclass match (False); and\n",
" 2. The SHACLObject instance itself."
],
"metadata": {
"id": "4sqtE2vVcDnx"
}
},
{
"cell_type": "code",
"source": [
"any_license_infos = object_set.obj_by_type[\"simplelicensing_AnyLicenseInfo\"]\n",
"print(any_license_infos)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "xb-edy3n4eRh",
"outputId": "13f1a84c-a8e2-4e0b-9bb5-412af366e1e7"
},
"execution_count": 9,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"{(False, <spdx_python_model.bindings.v3_0_1.simplelicensing_LicenseExpression object at 0x7d4371e41f10>)}\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"Let's begin with a quick check.\n",
"\n",
"For example, we can see how many SpdxDocument objects are present\n",
"and if it is according to the specification.\n",
"\n",
"See: https://spdx.github.io/spdx-spec/v3.0.1/serializations/#serialization-information"
],
"metadata": {
"id": "S17Cuqt5cgiY"
}
},
{
"cell_type": "code",
"source": [
"spdx_documents = list(object_set.obj_by_type[\"SpdxDocument\"])\n",
"\n",
"if len(spdx_documents) > 1:\n",
" print(\"Warning: A serialization must not contain more than one SpdxDocument.\")\n",
"else:\n",
" print(\"Looks good.\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "S-Q78uecJ-m9",
"outputId": "326cc716-07ba-4036-94ad-75e5155d9a2e"
},
"execution_count": 10,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Looks good.\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"# Get all objects of type \"Relationship\", including its subclasses\n",
"relationships = object_set.obj_by_type[\"Relationship\"]\n",
"print(\"Relationship found:\", len(relationships))\n",
"for o in relationships:\n",
" print(o)\n",
"print()\n",
"\n",
"# Get all objects of type \"LifecycleScopedRelationship\", including its subclasses\n",
"lifecycle_scoped_relationships = object_set.obj_by_type[\"LifecycleScopedRelationship\"]\n",
"print(\"LifecycleScopedRelationship found:\", len(lifecycle_scoped_relationships))\n",
"for o in lifecycle_scoped_relationships:\n",
" print(o)\n",
"print()\n",
"\n",
"# Get all objects of type \"Softare/Package\", including its subclasses\n",
"packages = object_set.obj_by_type[\"software_Package\"]\n",
"print(\"software_Package found:\", len(packages))\n",
"for o in packages:\n",
" print(o)\n",
"\n",
"print()"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "1tiaPV-FKmZh",
"outputId": "40727506-9b03-4de2-d8db-5ee28afd3c41"
},
"execution_count": 11,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Relationship found: 15\n",
"(True, <spdx_python_model.bindings.v3_0_1.Relationship object at 0x7d4371e0c7d0>)\n",
"(True, <spdx_python_model.bindings.v3_0_1.Relationship object at 0x7d4371e0d5e0>)\n",
"(True, <spdx_python_model.bindings.v3_0_1.Relationship object at 0x7d4371e0c590>)\n",
"(True, <spdx_python_model.bindings.v3_0_1.Relationship object at 0x7d4371e0d850>)\n",
"(False, <spdx_python_model.bindings.v3_0_1.LifecycleScopedRelationship object at 0x7d4371e0cb60>)\n",
"(True, <spdx_python_model.bindings.v3_0_1.Relationship object at 0x7d4371e0c920>)\n",
"(True, <spdx_python_model.bindings.v3_0_1.Relationship object at 0x7d4371e0c6b0>)\n",
"(True, <spdx_python_model.bindings.v3_0_1.Relationship object at 0x7d4371e0d730>)\n",
"(True, <spdx_python_model.bindings.v3_0_1.Relationship object at 0x7d4371f9d040>)\n",
"(True, <spdx_python_model.bindings.v3_0_1.Relationship object at 0x7d4371e0cc80>)\n",
"(False, <spdx_python_model.bindings.v3_0_1.LifecycleScopedRelationship object at 0x7d4371e0d3a0>)\n",
"(True, <spdx_python_model.bindings.v3_0_1.Relationship object at 0x7d4371e0d4f0>)\n",
"(True, <spdx_python_model.bindings.v3_0_1.Relationship object at 0x7d4371e0ca40>)\n",
"(True, <spdx_python_model.bindings.v3_0_1.Relationship object at 0x7d4371f15d60>)\n",
"(True, <spdx_python_model.bindings.v3_0_1.Relationship object at 0x7d4371e0c470>)\n",
"\n",
"LifecycleScopedRelationship found: 2\n",
"(True, <spdx_python_model.bindings.v3_0_1.LifecycleScopedRelationship object at 0x7d4371e0d3a0>)\n",
"(True, <spdx_python_model.bindings.v3_0_1.LifecycleScopedRelationship object at 0x7d4371e0cb60>)\n",
"\n",
"software_Package found: 2\n",
"(False, <spdx_python_model.bindings.v3_0_1.dataset_DatasetPackage object at 0x7d4371e40e30>)\n",
"(False, <spdx_python_model.bindings.v3_0_1.ai_AIPackage object at 0x7d4371e41040>)\n",
"\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"## A proper way to get access to SPDX objects"
],
"metadata": {
"id": "qY3_-XIGQoKT"
}
},
{
"cell_type": "markdown",
"source": [
"While direct access to `obj_by_id` and `obj_by_type` is possible,\n",
"it is generally recommended to use the provided access methods for better\n",
"encapsulation. These methods include `find_by_id` and `foreach_type`.\n",
"\n",
"SHACLObjectSet offers the `find_by_id` method to retrieve a specific object\n",
"using its unique `spdxId`."
],
"metadata": {
"id": "uqNOwXXAczeW"
}
},
{
"cell_type": "code",
"source": [
"obj = object_set.find_by_id(\"https://spdx.org/spdxdocs/Sbom1-b1b4ff24-5ada-4c22-a9c9-7bd7121978ff\")\n",
"print(obj)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "c-28HXbkudie",
"outputId": "04a9d060-4c2a-4c1a-fe21-24add8a5dfb7"
},
"execution_count": 12,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"software_Sbom(@id='https://spdx.org/spdxdocs/Sbom1-b1b4ff24-5ada-4c22-a9c9-7bd7121978ff')\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"SHACLObjectSet also provides `foreach_type`, a generator method that\n",
"iterates over all objects of a specified type.\n",
"\n",
"By default, `foreach_type` will also return instances of subclasses."
],
"metadata": {
"id": "wyvA-i5fdB80"
}
},
{
"cell_type": "code",
"source": [
"for obj in object_set.foreach_type(\"Relationship\"):\n",
" print(obj)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "Qjz-D9iz_p8n",
"outputId": "8b5e70bf-d6ec-4c1b-baa7-4badb8fd1671"
},
"execution_count": 13,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/contains3-c03bf106-fc40-4bd3-93a6-bac2235bb71b')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/hasDocumentation1-1dcbd2c2-f6aa-4059-95b6-8574e598e619')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/hasConcludedLicense3-c3e6ff66-b9da-4529-bd8b-dc75a13baad3')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/testedOn1-01efd83b-b615-4099-a9b7-e0c7db47fdd8')\n",
"LifecycleScopedRelationship(@id='https://spdx.org/spdxdocs/Relationship/generates1-01bd0839-cb33-4446-9cae-b01c9148d221')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/contains8-c087fb75-9ca8-4952-974d-87cb207375a6')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/contains1-c01b64da-fda5-4338-9425-da853192e79a')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/trainedOn1-01b4b32c-7854-4c31-9bf0-95f2e5805b66')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/hasDeclaredLicense1-d1741c4c-4c18-459a-b9f9-e24f018e39f1')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/hasDataFile1-016903ab-f618-4aaf-87f7-f260e37874f1')\n",
"LifecycleScopedRelationship(@id='https://spdx.org/spdxdocs/Relationship/generates2-02ec04c7-b248-4472-b017-da7c04145ec2')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/hasDependencyManifest1-1deb949d-5874-4b8e-a779-188e3ce4c420')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/contains9-c09ce78c-fa22-4110-9232-e246ea989999')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/hasConcludedLicense1-c18ca8f8-32f1-4682-a136-51c32a5d555a')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/hasDeclaredLicense3-d3e53884-69b0-404d-b297-e2180d2724be')\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"Notice the presence of `LifecycleScopedRelationship`, which is a subclass of `Relationship`."
],
"metadata": {
"id": "24pEngfddGw1"
}
},
{
"cell_type": "markdown",
"source": [
"Set `match_subclass` argument to False, to exclude subclasses.\n"
],
"metadata": {
"id": "6YbhPUpqdSZ9"
}
},
{
"cell_type": "code",
"source": [
"for obj in object_set.foreach_type(\"Relationship\", match_subclass=False):\n",
" print(obj)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "CLSKgtC7yRmt",
"outputId": "83f7618b-4068-4026-f65b-85c34efc3db9"
},
"execution_count": 14,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/contains3-c03bf106-fc40-4bd3-93a6-bac2235bb71b')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/hasDocumentation1-1dcbd2c2-f6aa-4059-95b6-8574e598e619')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/hasConcludedLicense3-c3e6ff66-b9da-4529-bd8b-dc75a13baad3')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/testedOn1-01efd83b-b615-4099-a9b7-e0c7db47fdd8')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/contains8-c087fb75-9ca8-4952-974d-87cb207375a6')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/contains1-c01b64da-fda5-4338-9425-da853192e79a')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/trainedOn1-01b4b32c-7854-4c31-9bf0-95f2e5805b66')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/hasDeclaredLicense1-d1741c4c-4c18-459a-b9f9-e24f018e39f1')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/hasDataFile1-016903ab-f618-4aaf-87f7-f260e37874f1')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/hasDependencyManifest1-1deb949d-5874-4b8e-a779-188e3ce4c420')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/contains9-c09ce78c-fa22-4110-9232-e246ea989999')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/hasConcludedLicense1-c18ca8f8-32f1-4682-a136-51c32a5d555a')\n",
"Relationship(@id='https://spdx.org/spdxdocs/Relationship/hasDeclaredLicense3-d3e53884-69b0-404d-b297-e2180d2724be')\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"Notice the absence of `LifecycleScopedRelationship` this time."
],
"metadata": {
"id": "_uwUlUordW9R"
}
},
{
"cell_type": "markdown",
"source": [
"Because `foreach_type` is a generator, you can use it like this as well:\n",
"\n",
"\n"
],
"metadata": {
"id": "pDSVRsAXdcqM"
}
},
{
"cell_type": "code",
"source": [
"objs = set(object_set.foreach_type(\"Relationship\", match_subclass=False))\n",
"print(len(objs))"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "1uVFmeYQAcSC",
"outputId": "d99ccefa-2b1e-4ab2-9768-48fddd1509b3"
},
"execution_count": 15,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"13\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"## Accessing SPDX properties"
],
"metadata": {
"id": "0GpZV9m1QzI1"
}
},
{
"cell_type": "markdown",
"source": [
"SPDX objects and their properties can be accessed directly from Python attributes.\n",
"\n",
"For example, this SpdxDocument `doc` has an attribute `creationInfo`\n",
"and the value of `creationInfo` is a CreationInfo object.\n",
"We can directly access `specVersion` attribute of the CreationInfo object like this."
],
"metadata": {
"id": "vnpIoFf1dli8"
}
},
{
"cell_type": "code",
"source": [
"doc = list(object_set.foreach_type(\"SpdxDocument\", match_subclass=False))[0]\n",
"print(\"SPDX Spec Version:\", doc.creationInfo.specVersion)\n",
"\n",
"root_element = doc.rootElement[0] # assume we have only one root element\n",
"print(\"Root element ID:\", root_element.spdxId)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "pXl50kABJXnw",
"outputId": "1579a77e-16ed-4657-981b-1f3b04efe70f"
},
"execution_count": 16,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"SPDX Spec Version: 3.0.1\n",
"Root element ID: https://spdx.org/spdxdocs/Sbom1-b1b4ff24-5ada-4c22-a9c9-7bd7121978ff\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"The SPDX specification defines the classes and properties of SPDX models.\n",
"\n",
"For example, the SpdxDocument class is detailed at: https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Classes/SpdxDocument/"
],
"metadata": {
"id": "VgA7RhdiPZjl"
}
},
{
"cell_type": "markdown",
"source": [
"## Put things together\n",
"\n",
"We can now perform basic object access.\n",
"Let's try to visualize an SPDX SBOM.\n",
"\n",
"The example below will iterate through all relationships and print relationship types between SPDX objects."
],
"metadata": {
"id": "rNdl2dqiPht8"
}
},
{
"cell_type": "code",
"source": [
"for rel in object_set.foreach_type(\"Relationship\"):\n",
" from_ = getattr(rel, \"from_\") # spdx-python-model uses \"from_\" instead of \"from\" to avoid the Python keyword\n",
" to = getattr(rel, \"to\")\n",
" rel_type = getattr(rel, \"relationshipType\").split(\"/\")[-1] # print only the type name, not the full URL\n",
"\n",
" print( \"┏\" + \"━\"*50 + \"┅\")\n",
" print(f\"┃ {from_.__class__.__name__}\")\n",
" print(f\"┃ - name: {from_.name}\")\n",
" print(f\"┃ - spdxId: {from_.spdxId}\")\n",
" print( \"┗\" + \"━\"*50 + \"┅\")\n",
"\n",
" print( \" │\")\n",
" print(f\" {rel_type}\")\n",
" print( \" ↓\")\n",
"\n",
" for o in to:\n",
" print( \" ┏\" + \"━\"*50 + \"┅\")\n",
" print(f\" ┃ {o.__class__.__name__}\")\n",
" print(f\" ┃ - name: {o.name}\")\n",
" print(f\" ┃ - spdxId: {o.spdxId}\")\n",
" print( \" ┗\" + \"━\"*50 + \"┅\")\n",
"\n",
" print()"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "pAgJZnDJU4Jf",
"outputId": "fe6375da-33bb-436e-9388-d1e5a11c2eea"
},
"execution_count": 17,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"┃ software_File\n",
"┃ - name: data\n",
"┃ - spdxId: https://spdx.org/spdxdocs/File1-d1f75466-39c8-4d2b-8ec9-79f67e8a20d7\n",
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" │\n",
" contains\n",
" ↓\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: test.txt\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File16-f16c17ad-d95d-40d6-860f-1bacde2dbfd9\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: train.txt\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File17-f17f40d2-0540-4d2b-9f23-1b58d7ab4652\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: valid.txt\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File18-f18be0d7-d16f-4658-91f3-5e5cf364edfa\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"\n",
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"┃ ai_AIPackage\n",
"┃ - name: Sentiment Demo: A Simple AI Application and its AI BOM Example\n",
"┃ - spdxId: https://spdx.org/spdxdocs/AIPackage1-a1fae2b4-2c1a-4f21-9001-1bdc5ac9e481\n",
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" │\n",
" hasDocumentation\n",
" ↓\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: techdocs\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File3-d36bc4df-b88e-4e0c-8aeb-ec446dbae90f\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: LICENSE\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File9-f09b8718-e9ab-4d89-97b5-940988fdf050\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: README.md\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File13-f13b1a21-3183-4eb8-a4c2-c4c0e2f55d50\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"\n",
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"┃ ai_AIPackage\n",
"┃ - name: Sentiment Demo: A Simple AI Application and its AI BOM Example\n",
"┃ - spdxId: https://spdx.org/spdxdocs/AIPackage1-a1fae2b4-2c1a-4f21-9001-1bdc5ac9e481\n",
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" │\n",
" hasConcludedLicense\n",
" ↓\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ simplelicensing_LicenseExpression\n",
" ┃ - name: None\n",
" ┃ - spdxId: https://spdx.org/licenses/CC0-1.0\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"\n",
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"┃ software_File\n",
"┃ - name: model.bin\n",
"┃ - spdxId: https://spdx.org/spdxdocs/File10-f10f9f75-3a23-465e-a80d-3a4fec75b220\n",
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" │\n",
" testedOn\n",
" ↓\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: test.txt\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File16-f16c17ad-d95d-40d6-860f-1bacde2dbfd9\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"\n",
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"┃ software_File\n",
"┃ - name: preprocess.py\n",
"┃ - spdxId: https://spdx.org/spdxdocs/File12-f12effed-9289-4fc3-986a-5fd83ff6724c\n",
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" │\n",
" generates\n",
" ↓\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: test.txt\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File16-f16c17ad-d95d-40d6-860f-1bacde2dbfd9\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: train.txt\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File17-f17f40d2-0540-4d2b-9f23-1b58d7ab4652\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: valid.txt\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File18-f18be0d7-d16f-4658-91f3-5e5cf364edfa\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"\n",
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"┃ ai_AIPackage\n",
"┃ - name: Sentiment Demo: A Simple AI Application and its AI BOM Example\n",
"┃ - spdxId: https://spdx.org/spdxdocs/AIPackage1-a1fae2b4-2c1a-4f21-9001-1bdc5ac9e481\n",
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" │\n",
" contains\n",
" ↓\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: techdocs\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File3-d36bc4df-b88e-4e0c-8aeb-ec446dbae90f\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: evaluate.py\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File8-f08b4fa4-2658-421b-80e9-e9ab116102de\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: LICENSE\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File9-f09b8718-e9ab-4d89-97b5-940988fdf050\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: model.bin\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File10-f10f9f75-3a23-465e-a80d-3a4fec75b220\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: predict.py\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File11-f11e140e-67ef-48db-bb4a-7fcb3775406f\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: preprocess.py\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File12-f12effed-9289-4fc3-986a-5fd83ff6724c\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: README.md\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File13-f13b1a21-3183-4eb8-a4c2-c4c0e2f55d50\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: requirements.txt\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File14-f14b0f00-1396-4986-8ca9-5d59b9362826\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: train.py\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File15-f15b29a6-9634-4813-aa99-9986891a0711\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"\n",
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"┃ dataset_DatasetPackage\n",
"┃ - name: Data after preprocessing\n",
"┃ - spdxId: https://spdx.org/spdxdocs/DatasetPackage1-d1f693d0-b420-4b6a-b9f2-ec2097bac863\n",
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" │\n",
" contains\n",
" ↓\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: data\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File1-d1f75466-39c8-4d2b-8ec9-79f67e8a20d7\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"\n",
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"┃ software_File\n",
"┃ - name: model.bin\n",
"┃ - spdxId: https://spdx.org/spdxdocs/File10-f10f9f75-3a23-465e-a80d-3a4fec75b220\n",
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" │\n",
" trainedOn\n",
" ↓\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: train.txt\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File17-f17f40d2-0540-4d2b-9f23-1b58d7ab4652\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: valid.txt\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File18-f18be0d7-d16f-4658-91f3-5e5cf364edfa\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"\n",
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"┃ dataset_DatasetPackage\n",
"┃ - name: Data after preprocessing\n",
"┃ - spdxId: https://spdx.org/spdxdocs/DatasetPackage1-d1f693d0-b420-4b6a-b9f2-ec2097bac863\n",
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" │\n",
" hasDeclaredLicense\n",
" ↓\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ simplelicensing_LicenseExpression\n",
" ┃ - name: None\n",
" ┃ - spdxId: https://spdx.org/licenses/CC0-1.0\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"\n",
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"┃ software_File\n",
"┃ - name: predict.py\n",
"┃ - spdxId: https://spdx.org/spdxdocs/File11-f11e140e-67ef-48db-bb4a-7fcb3775406f\n",
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" │\n",
" hasDataFile\n",
" ↓\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: model.bin\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File10-f10f9f75-3a23-465e-a80d-3a4fec75b220\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"\n",
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"┃ software_File\n",
"┃ - name: train.py\n",
"┃ - spdxId: https://spdx.org/spdxdocs/File15-f15b29a6-9634-4813-aa99-9986891a0711\n",
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" │\n",
" generates\n",
" ↓\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: model.bin\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File10-f10f9f75-3a23-465e-a80d-3a4fec75b220\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"\n",
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"┃ ai_AIPackage\n",
"┃ - name: Sentiment Demo: A Simple AI Application and its AI BOM Example\n",
"┃ - spdxId: https://spdx.org/spdxdocs/AIPackage1-a1fae2b4-2c1a-4f21-9001-1bdc5ac9e481\n",
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" │\n",
" hasDependencyManifest\n",
" ↓\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: requirements.txt\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File14-f14b0f00-1396-4986-8ca9-5d59b9362826\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"\n",
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"┃ software_File\n",
"┃ - name: techdocs\n",
"┃ - spdxId: https://spdx.org/spdxdocs/File3-d36bc4df-b88e-4e0c-8aeb-ec446dbae90f\n",
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" │\n",
" contains\n",
" ↓\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: dataprepare.md\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File31-f31f6dc0-f9ce-4efa-9ccd-47dee50041d3\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ software_File\n",
" ┃ - name: instructions.md\n",
" ┃ - spdxId: https://spdx.org/spdxdocs/File32-f32f0784-038e-488d-a7bf-7d172dfe8b8a\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"\n",
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"┃ dataset_DatasetPackage\n",
"┃ - name: Data after preprocessing\n",
"┃ - spdxId: https://spdx.org/spdxdocs/DatasetPackage1-d1f693d0-b420-4b6a-b9f2-ec2097bac863\n",
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" │\n",
" hasConcludedLicense\n",
" ↓\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ simplelicensing_LicenseExpression\n",
" ┃ - name: None\n",
" ┃ - spdxId: https://spdx.org/licenses/CC0-1.0\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"\n",
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"┃ ai_AIPackage\n",
"┃ - name: Sentiment Demo: A Simple AI Application and its AI BOM Example\n",
"┃ - spdxId: https://spdx.org/spdxdocs/AIPackage1-a1fae2b4-2c1a-4f21-9001-1bdc5ac9e481\n",
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" │\n",
" hasDeclaredLicense\n",
" ↓\n",
" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
" ┃ simplelicensing_LicenseExpression\n",
" ┃ - name: None\n",
" ┃ - spdxId: https://spdx.org/licenses/CC0-1.0\n",
" ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┅\n",
"\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"## SPDX object creation and built-in validation\n",
"\n",
"To create an SPDX object,\n",
"simply use the standard constructor as you would with any native Python class."
],
"metadata": {
"id": "D2PljHcL5iH_"
}
},
{
"cell_type": "code",
"source": [
"package = spdx3.ai_AIPackage()\n",
"package"
],
"metadata": {
"id": "9hjARbwm6DUd",
"outputId": "30c49bae-92bf-40ef-a57d-9d3dc3014205",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"execution_count": 18,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"<spdx_python_model.bindings.v3_0_1.ai_AIPackage at 0x7d4371e0f530>"
]
},
"metadata": {},
"execution_count": 18
}
]
},
{
"cell_type": "markdown",
"source": [
"The spdx-python-model library features built-in SHACL-based validation derived\n",
"directly from the [SPDX 3 model][spdx-3-model].\n",
"This validation ensures data integrity by rejecting invalid types at runtime.\n",
"\n",
"For example, the `domain` property of [`/AI/AIPackage`][ai-package] has a\n",
"[cardinality] of 0..* (minimum count: 0, maximum count: unbounded).\n",
"This requires the value to be a list;\n",
"if a different type is provided, the library will raise a `TypeError`.\n",
"\n",
"[spdx-3-model]: https://spdx.org/rdf/3.0.1/spdx-model.ttl\n",
"[ai-package]: https://spdx.github.io/spdx-spec/v3.0.1/model/AI/Classes/AIPackage/\n",
"[cardinality]: https://spdx.github.io/spdx-spec/v3.0.1/conformance/"
],
"metadata": {
"id": "68j1H6Fw6xtf"
}
},
{
"cell_type": "code",
"source": [
"package.ai_domain = \"nlp\""
],
"metadata": {
"id": "cAzCx9tV6wI-",
"outputId": "4f012acc-bc16-44b3-8d94-f72d134db84a",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 300
}
},
"execution_count": 19,
"outputs": [
{
"output_type": "error",
"ename": "TypeError",
"evalue": "Value must be one of type: list, ListProxy. Got str",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m/tmp/ipython-input-179762956.py\u001b[0m in \u001b[0;36m<cell line: 0>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mpackage\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mai_domain\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"nlp\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m/usr/local/lib/python3.12/dist-packages/spdx_python_model/bindings/v3_0_1.py\u001b[0m in \u001b[0;36m__setattr__\u001b[0;34m(self, name, value)\u001b[0m\n\u001b[1;32m 714\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 715\u001b[0m \u001b[0miri\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_OBJ_IRIS\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 716\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0miri\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 717\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mKeyError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 718\u001b[0m raise AttributeError(\n",
"\u001b[0;32m/usr/local/lib/python3.12/dist-packages/spdx_python_model/bindings/v3_0_1.py\u001b[0m in \u001b[0;36m__setitem__\u001b[0;34m(self, iri, value)\u001b[0m\n\u001b[1;32m 796\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 797\u001b[0m \u001b[0mprop\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpyname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__get_prop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0miri\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 798\u001b[0;31m \u001b[0mprop\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalidate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 799\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0miri\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_OBJ_DEPRECATED\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 800\u001b[0m warnings.warn(\n",
"\u001b[0;32m/usr/local/lib/python3.12/dist-packages/spdx_python_model/bindings/v3_0_1.py\u001b[0m in \u001b[0;36mvalidate\u001b[0;34m(self, value)\u001b[0m\n\u001b[1;32m 473\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 474\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mvalidate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 475\u001b[0;31m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalidate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 476\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 477\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/usr/local/lib/python3.12/dist-packages/spdx_python_model/bindings/v3_0_1.py\u001b[0m in \u001b[0;36mvalidate\u001b[0;34m(self, value)\u001b[0m\n\u001b[1;32m 67\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 68\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mvalidate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 69\u001b[0;31m \u001b[0mcheck_type\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mVALID_TYPES\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 70\u001b[0m if self.pattern is not None and not re.search(\n\u001b[1;32m 71\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpattern\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto_string\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/usr/local/lib/python3.12/dist-packages/spdx_python_model/bindings/v3_0_1.py\u001b[0m in \u001b[0;36mcheck_type\u001b[0;34m(obj, types)\u001b[0m\n\u001b[1;32m 44\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtypes\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtypes\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mlist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 46\u001b[0;31m raise TypeError(\n\u001b[0m\u001b[1;32m 47\u001b[0m \u001b[0;34mf\"Value must be one of type: {', '.join(t.__name__ for t in types)}. Got {type(obj).__name__}\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 48\u001b[0m )\n",
"\u001b[0;31mTypeError\u001b[0m: Value must be one of type: list, ListProxy. Got str"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"Even if there is only a single entry, you must provide it as a single-element\n",
"list to satisfy the property's cardinality:"
],
"metadata": {
"id": "WWdZwqP9EfnP"
}
},
{
"cell_type": "code",
"source": [
"package.ai_domain = [\"nlp\"]\n",
"package.ai_domain"
],
"metadata": {
"id": "H6LeOd3_D8rQ",
"outputId": "5d3fd9bc-2ea7-4518-c9af-4d2314f81ba1",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"execution_count": 20,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"['nlp']"
]
},
"metadata": {},
"execution_count": 20
}
]
},
{
"cell_type": "markdown",
"source": [
"Refer to the [SPDX specification][spdx-spec]\n",
"for detailed information on property types and cardinality.\n",
"\n",
"[spdx-spec]: https://spdx.github.io/spdx-spec/"
],
"metadata": {
"id": "quqpF4E0E6fv"
}
},
{
"cell_type": "markdown",
"source": [
"## That's it\n",
"\n",
"That's it for now! Hope you enjoyed the tutorial.\n",
"\n",
"Special thanks to Joshua Watt for the spdx-python-model library, making SPDX 3 more accessible for Python developers.\n",
"\n",
"Find more examples of how to use the library:\n",
"\n",
"- `spdx-python-model` tests: https://github.com/spdx/spdx-python-model/blob/main/tests/test_import.py\n",
"- `shacl2code` (which generates spdx-python-model's Python binding) tests: https://github.com/JPEWdev/shacl2code/blob/main/tests/test_python.py\n"
],
"metadata": {
"id": "lqg60qPeLaI3"
}
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment