diff --git a/.github/workflows/check_codestyle.yml b/.github/workflows/check_codestyle.yml
index 08b5e125f..327f2f54a 100644
--- a/.github/workflows/check_codestyle.yml
+++ b/.github/workflows/check_codestyle.yml
@@ -20,7 +20,7 @@ jobs:
strategy:
matrix:
os: [ ubuntu-latest, macos-latest, windows-latest ]
- python-version: [ "3.8", "3.9", "3.10", "3.11" ]
+ python-version: [ "3.14" ]
steps:
- uses: actions/checkout@v3
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 095f9ec7a..46dc4157a 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -7,6 +7,7 @@ on:
push:
branches:
- main
+ workflow_dispatch:
permissions:
contents: read
@@ -19,7 +20,7 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v4
with:
- python-version: '3.11'
+ python-version: '3.14'
- name: Install dependencies
run: |
sudo apt-get install graphviz-dev
@@ -28,7 +29,7 @@ jobs:
- name: Generate docs
run: pdoc spdx_tools -o docs/
- name: Upload docs as artifact
- uses: actions/upload-pages-artifact@v1
+ uses: actions/upload-pages-artifact@v3
with:
path: docs/
@@ -44,4 +45,4 @@ jobs:
steps:
- id: deployment
name: Deploy docs to GitHub pages
- uses: actions/deploy-pages@v2
+ uses: actions/deploy-pages@v4
diff --git a/.github/workflows/install_and_test.yml b/.github/workflows/install_and_test.yml
index 2149dd267..d661745be 100644
--- a/.github/workflows/install_and_test.yml
+++ b/.github/workflows/install_and_test.yml
@@ -17,7 +17,7 @@ jobs:
strategy:
matrix:
os: [ ubuntu-latest, macos-latest, windows-latest ]
- python-version: [ "3.8", "3.9", "3.10", "3.11" ]
+ python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ]
steps:
- uses: actions/checkout@v3
diff --git a/.github/workflows/integration_test.yml b/.github/workflows/integration_test.yml
index 2a37fa7a6..3a749a3f0 100644
--- a/.github/workflows/integration_test.yml
+++ b/.github/workflows/integration_test.yml
@@ -13,10 +13,10 @@ jobs:
steps:
- uses: actions/checkout@v3
- - name: Set up Python 3.11
+ - name: Set up Python 3.14
uses: actions/setup-python@v4
with:
- python-version: 3.11
+ python-version: 3.14
- name: Installation
run: |
python -m pip install --upgrade pip
diff --git a/.github/workflows/prepare_release.yml b/.github/workflows/prepare_release.yml
index cb2f5a67f..15439f41f 100644
--- a/.github/workflows/prepare_release.yml
+++ b/.github/workflows/prepare_release.yml
@@ -19,7 +19,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
- python-version: '3.8'
+ python-version: '3.14'
- name: Set up dependencies
run: |
python -m pip install --upgrade pip
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f17ee022c..808f5e134 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,28 @@
# Changelog
+## v0.8.4 (2026-01-16)
+
+### New features and changes
+
+* change supported Python versions:
+ * add support for Python 3.14
+ * dropped support for Python 3.7, 3.8, 3.9 (which all have reached their end-of-life)
+* fixes
+ * download location URIs are not case sensitive anymore
+ * removed some control characters from JSON SPDX
+ * remove usages of `__file__` to make the project zip-safe
+* fixes to `README.md` and `process.md`
+
+### Contributors
+
+This release was made possible by the following contributors. Thank you very much!
+
+* @zbleness
+* @clabbenius
+* Oliver Benjamin @oli-ben
+* Arthit Suriyawongkul @bact
+* Armin Tänzer @armintaenzertng
+
## v0.8.3 (2024-09-27)
### New features and changes
@@ -141,12 +164,12 @@ Starting a Changelog.
* Dropped Python 2 support. Python >= 3.6 is now required.
* Added `pyspdxtools_convertor` and `pyspdxtools_parser` CLI scripts. See [the readme](README.md) for usage instructions.
-* Updated the tools to support SPDX versions up to 2.3 and to conform with the specification. Apart from many bugfixes
+* Updated the tools to support SPDX versions up to 2.3 and to conform with the specification. Apart from many bugfixes
and new properties, some of the more significant changes include:
- * Support for multiple packages per document
- * Support for multiple checksums for packages and files
- * Support for files outside a package
-* **Note**: Validation was updated to follow the 2.3 specification. Since there is currently no support for
+ * Support for multiple packages per document
+ * Support for multiple checksums for packages and files
+ * Support for files outside a package
+* **Note**: Validation was updated to follow the 2.3 specification. Since there is currently no support for
version-specific handling, some details may be handled incorrectly for documents using lower
versions. The changes are mostly restricted to properties becoming optional and new property values becoming
available, and should be of limited impact. See https://spdx.github.io/spdx-spec/v2.3/diffs-from-previous-editions/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4c4a04341..88638e70e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -15,7 +15,7 @@ intention prior to creating a patch.
## Development process
-We use the GitHub flow that is described here: https://guides.github.com/introduction/flow/
+We use the GitHub flow that is described here:
Here's the process to make changes to the codebase:
@@ -30,15 +30,19 @@ Here's the process to make changes to the codebase:
and optionally follow the further steps described to sync your fork and the original repository.
4. Create a new branch in your fork and set up environment:
+
```sh
git checkout -b fix-or-improve-something
python -m venv ./venv
./venv/bin/activate
pip install -e ".[development]"
```
- Note: By using the group `[development]` for the installation, all dependencies (including optional ones) will be
- installed. This way we make sure that all tests are executed.
+
+ Note: By using the group `[development]` for the installation, all dependencies (including optional ones) will be
+ installed. This way we make sure that all tests are executed.
+
5. Make some changes and commit them to the branch:
+
```sh
git commit --signoff -m 'description of my changes'
```
@@ -49,27 +53,33 @@ Here's the process to make changes to the codebase:
of [the Developer Certificate of Origin](https://developercertificate.org/). Git has utilities for signing off on
commits: `git commit -s` or `--signoff` signs a current commit, and `git rebase --signoff `
retroactively signs a range of past commits.
+
6. Test your changes:
+
```sh
pytest -vvs # in the repo root
```
-7. Check your code style. When opening a pull request, your changes will automatically be checked with `isort`, `black`
- and `flake8` to make sure your changes fit with the rest of the code style.
+7. Check your code style. When opening a pull request, your changes will automatically be checked with `isort`, `black`
+ and `flake8` to make sure your changes fit with the rest of the code style.
+
```sh
# run the following commands in the repo root
- isort src tests
+ isort src tests
black src tests
- flake8 src tests
+ flake8 src tests
```
- `black` and `isort` will automatically format the code and sort the imports. The configuration for these linters
+
+ `black` and `isort` will automatically format the code and sort the imports. The configuration for these linters
can be found in the `pyproject.toml`. `flake8` lists all problems found which then need to be resolved manually.
The configuration for the linter can be found in the `.flake8` file.
8. Push the branch to your fork on GitHub:
+
```sh
git push origin fix-or-improve-something
```
+
9. Make a pull request on GitHub.
10. Continue making more changes and commits on the branch, with `git commit --signoff` and `git push`.
11. When done, write a comment on the PR asking for a code review.
@@ -77,6 +87,7 @@ Here's the process to make changes to the codebase:
possible, or with `squash`.
13. The temporary branch on GitHub should be deleted (there is a button for deleting it).
14. Delete the local branch as well:
+
```sh
git checkout master
git pull -p
@@ -84,11 +95,11 @@ Here's the process to make changes to the codebase:
git branch -d fix-or-improve-something
```
-# How to run tests
+## How to run tests
The tests framework is using pytest:
-```
+```sh
pip install pytest
pytest -vvs
```
diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md
index f8c7e274b..6a9bcb2a5 100644
--- a/DOCUMENTATION.md
+++ b/DOCUMENTATION.md
@@ -1,12 +1,15 @@
# Code architecture documentation
## Package Overview
+
Beneath the top-level package `spdx_tools` you will find three sub-packages:
+
- `spdx`, which contains the code to create, parse, write and validate SPDX documents of versions 2.2 and 2.3
-- `spdx3`, which will contain the same feature set for versions 3.x once they are released
+- `spdx3`, which will contain the same feature set for versions 3.x
- `common`, which contains code that is shared between the different versions, such as type-checking and `spdx_licensing`.
## `spdx`
+
The `spdx` package contains the code dealing with SPDX-2 documents.
The subpackages serve the purpose to divide the code into logically independent chunks. Shared code can be found in the top-level modules here.
`model`, `parser`, `validation` and `writer` constitute the four main components of this library and are further described below.
@@ -14,9 +17,11 @@ The subpackages serve the purpose to divide the code into logically independent
`jsonschema` and `rdfschema` contain code specific to the corresponding serialization format.
### `model`
+
The internal data model closely follows the [official SPDX-2.3 specification](https://spdx.github.io/spdx-spec/v2.3/).
Entrypoint to the model is the `Document` class, which has the following attributes:
+
- `creation_info`: a single instance of the `CreationInfo` class
- `packages`: a list of `Package` objects
- `files`: a list of `File` objects
@@ -35,6 +40,7 @@ A custom extension of the `@dataclass` annotation is used that is called `@datac
Apart from all the usual `dataclass` functionality, this implements fields of a class as properties with their own getter and setter methods.
This is used in particular to implement type checking when properties are set.
Source of truth for these checks are the attribute definitions at the start of the respective class that must specify the correct type hint.
+
The `beartype` library is used to check type conformity (`typeguard` was used in the past but has been replaced since due to performance issues).
In case of a type mismatch a `TypeError` is raised. To ensure that all possible type errors are found during the construction of an object,
a custom `__init__()` that calls `check_types_and_set_values()` is part of every class.
@@ -43,26 +49,31 @@ This function tries to set all values provided by the constructor and collects a
For the SPDX values `NONE` and `NOASSERTION` the classes `SpdxNone` and `SpdxNoAssertion` are used, respectively. Both can be instantiated without any arguments.
### `parser`
+
The parsing and writing modules are split into subpackages according to the serialization formats: `json`, `yaml`, `xml`, `tagvalue` and `rdf`.
As the first three share the same tree structure that can be parsed into a dictionary, their shared logic is contained in the `jsonlikedict` package.
One overarching concept of all parsers is the goal of dealing with parsing errors (like faulty types or missing mandatory fields) as long as possible before failing.
Thus, the `SPDXParsingError` that is finally raised collects as much information as possible about all parsing errors that occurred.
#### `tagvalue`
+
Since Tag-Value is an SPDX-specific format, there exist no readily available parsers for it.
-This library implements its own deserialization code using the `ply` library's `lex` module for lexing and the `yacc` module for parsing.
+This library implements its own deserialization code using the `ply` library's `lex` module for lexing and the `yacc` module for parsing.
#### `rdf`
+
The `rdflib` library is used to deserialize RDF graphs from XML format.
-The graph is then being parsed and translated into the internal data model.
+The graph is then being parsed and translated into the internal data model.
#### `json`, `yaml`, `xml`
+
In a first step, all three of JSON, YAML and XML formats are deserialized into a dictionary representing their tree structure.
This is achieved via the `json`, `yaml` and `xmltodict` packages, respectively.
Special note has to be taken in the XML case which does not support lists and numbers.
The logic concerning the translation from these dicts to the internal data model can be found in the `jsonlikedict` package.
### `writer`
+
For serialization purposes, only non-null fields are written out.
All writers expect a valid SPDX document from the internal model as input.
To ensure this is actually the case, the standard behaviour of every writer function is to call validation before the writing process.
@@ -71,18 +82,21 @@ Also by default, all list properties in the model are scanned for duplicates whi
This can be disabled by setting the `drop_duplicates` boolean to false.
#### `tagvalue`
+
The ordering of the tags follows the [example in the official specification](https://github.com/spdx/spdx-spec/blob/development/v2.3.1/examples/SPDXTagExample-v2.3.spdx).
#### `rdf`
+
The RDF graph is constructed from the internal data model and serialized to XML format afterward, using the `rdflib` library.
#### `json`, `yaml`, `xml`
+
As all three of JSON, YAML and XML formats share the same tree structure, the first step is to generate the dictionary representing that tree.
This is achieved by the `DocumentConverter` class in the `jsonschema` package.
Subsequently, the dictionary is serialized using the `json`, `yaml` and `xmltodict` packages, respectively.
-
### `validation`
+
The `validation` package takes care of all nonconformities with the SPDX specification that are not due to incorrect typing.
This mainly includes checks for correctly formatted strings or the actual existence of references SPDXIDs.
Entrypoint is the `document_validator` module with the `validate_full_spdx_document()` function.
@@ -93,6 +107,7 @@ Validation and reference checking of SPDXIDs (and possibly external document ref
For the validation of license expressions we utilise the `license-expression` library's `validate` and `parse` functions, which take care of checking license symbols against the [SPDX license list](https://spdx.org/licenses/).
Invalidities are captured in instances of a custom `ValidationMessage` class. This has two attributes:
+
- `validation_message` is a string that describes the actual problem
- `validation_context` is a `ValidationContext` object that helps to pinpoint the source of the problem by providing the faulty element's SPDXID (if it has one), the parent SPDXID (if that is known), the element's type and finally the full element itself.
It is left open to the implementer which of this information to use in the following evaluation of the validation process.
@@ -101,7 +116,8 @@ Every validation function returns a list of `ValidationMessage` objects, which a
That is, if an empty list is returned, the document is valid.
## `spdx3`
-Due to the SPDX-3 model still being in development, this package is still a work in progress.
+
+This package is still a work in progress.
However, as the basic building blocks of parsing, writing, creation and validation are still important in the new version,
the `spdx3` package is planned to be structured similarly to the `spdx` package.
diff --git a/README.md b/README.md
index 99112ea02..a8e5cafd8 100644
--- a/README.md
+++ b/README.md
@@ -3,11 +3,9 @@
CI status (Linux, macOS and Windows): [![Install and Test][1]][2]
[1]: https://github.com/spdx/tools-python/actions/workflows/install_and_test.yml/badge.svg
-
[2]: https://github.com/spdx/tools-python/actions/workflows/install_and_test.yml
-
-# Breaking changes v0.7 -> v0.8
+## Breaking changes v0.7 -> v0.8
Please be aware that the upcoming 0.8 release has undergone a significant refactoring in preparation for the upcoming
SPDX v3.0 release, leading to breaking changes in the API.
@@ -15,124 +13,143 @@ Please refer to the [migration guide](https://github.com/spdx/tools-python/wiki/
to update your existing code.
The main features of v0.8 are:
+
- full validation of SPDX documents against the v2.2 and v2.3 specification
- support for SPDX's RDF format with all v2.3 features
-- experimental support for the upcoming SPDX v3 specification. Note, however, that support is neither complete nor
- stable at this point, as the spec is still evolving. SPDX3-related code is contained in a separate subpackage "spdx3"
+- experimental support for the upcoming SPDX v3 specification. Note, however, that support is neither complete nor
+ stable at this point, as the spec is still evolving. SPDX3-related code is contained in a separate subpackage "spdx3"
and its use is optional. We do not recommend using it in production code yet.
+Note that v0.8 only supports **writing**, not **reading** SPDX 3.0 documents.
+See [#760](https://github.com/spdx/tools-python/issues/760) for details.
-# Information
+## Information
This library implements SPDX parsers, convertors, validators and handlers in Python.
-- Home: https://github.com/spdx/tools-python
-- Issues: https://github.com/spdx/tools-python/issues
-- PyPI: https://pypi.python.org/pypi/spdx-tools
-- Browse the API: https://spdx.github.io/tools-python
+- Home:
+- Issues:
+- PyPI:
+- Browse the API:
-Important updates regarding this library are shared via the SPDX tech mailing list: https://lists.spdx.org/g/Spdx-tech.
+Important updates regarding this library are shared via
+the SPDX tech mailing list: .
-
-# License
+## License
[Apache-2.0](LICENSE)
-# Features
+## Features
-* API to create and manipulate SPDX v2.2 and v2.3 documents
-* Parse, convert, create and validate SPDX files
-* supported formats: Tag/Value, RDF, JSON, YAML, XML
-* visualize the structure of a SPDX document by creating an `AGraph`. Note: This is an optional feature and requires
-additional installation of optional dependencies
+- API to create and manipulate SPDX v2.2 and v2.3 documents
+- Parse, convert, create and validate SPDX files
+- Supported formats: Tag/Value, RDF, JSON, YAML, XML
+- Visualize the structure of a SPDX document by creating an `AGraph`.
+ Note: This is an optional feature and requires
+ additional installation of optional dependencies
## Experimental support for SPDX 3.0
-* Create v3.0 elements and payloads
-* Convert v2.2/v2.3 documents to v3.0
-* Serialize to JSON-LD
-See [Quickstart to SPDX 3.0](#quickstart-to-spdx-30) below.
-The implementation is based on the descriptive markdown files in the repository https://github.com/spdx/spdx-3-model (latest commit: a5372a3c145dbdfc1381fc1f791c68889aafc7ff).
+- Create v3.0 elements and payloads
+- Convert v2.2/v2.3 documents to v3.0
+- Serialize to JSON-LD
+See [Quickstart to SPDX 3.0](#quickstart-to-spdx-30) below.
+The implementation is based on the descriptive Markdown files in the repository
+
+(commit: a5372a3c145dbdfc1381fc1f791c68889aafc7ff).
+The latest SPDX 3.0 model is available at
+.
-# Installation
+## Installation
As always you should work in a virtualenv (venv). You can install a local clone
-of this repo with `yourenv/bin/pip install .` or install it from PyPI
+of this repo with `yourenv/bin/pip install .` or install it from PyPI
(check for the [newest release](https://pypi.org/project/spdx-tools/#history) and install it like
-`yourenv/bin/pip install spdx-tools==0.8.0a2`). Note that on Windows it would be `Scripts`
+`yourenv/bin/pip install spdx-tools==0.8.3`). Note that on Windows it would be `Scripts`
instead of `bin`.
-# How to use
+## How to use
-## Command-line usage
+### Command-line usage
1. **PARSING/VALIDATING** (for parsing any format):
-* Use `pyspdxtools -i ` where `` is the location of the file. The input format is inferred automatically from the file ending.
+ - Use `pyspdxtools -i ` where `` is the location of the file. The input format is inferred automatically from the file ending.
-* If you are using a source distribution, try running:
- `pyspdxtools -i tests/data/SPDXJSONExample-v2.3.spdx.json`
+ - If you are using a source distribution, try running:
+ `pyspdxtools -i tests/spdx/data/SPDXJSONExample-v2.3.spdx.json`
2. **CONVERTING** (for converting one format to another):
-* Use `pyspdxtools -i -o ` where `` is the location of the file to be converted
- and `` is the location of the output file. The input and output formats are inferred automatically from the file endings.
+ - Use `pyspdxtools -i -o ` where `` is the location of the file to be converted
+ and `` is the location of the output file. The input and output formats are inferred automatically from the file endings.
-* If you are using a source distribution, try running:
- `pyspdxtools -i tests/data/SPDXJSONExample-v2.3.spdx.json -o output.tag`
+ - If you are using a source distribution, try running:
+ `pyspdxtools -i tests/spdx/data/SPDXJSONExample-v2.3.spdx.json -o output.tag`
-* If you want to skip the validation process, provide the `--novalidation` flag, like so:
- `pyspdxtools -i tests/data/SPDXJSONExample-v2.3.spdx.json -o output.tag --novalidation`
+ - If you want to skip the validation process, provide the `--novalidation` flag, like so:
+ `pyspdxtools -i tests/spdx/data/SPDXJSONExample-v2.3.spdx.json -o output.tag --novalidation`
(use this with caution: note that undetected invalid documents may lead to unexpected behavior of the tool)
-
-* For help use `pyspdxtools --help`
+
+ - For help use `pyspdxtools --help`
3. **GRAPH GENERATION** (optional feature)
-* This feature generates a graph representing all elements in the SPDX document and their connections based on the provided
- relationships. The graph can be rendered to a picture. Below is an example for the file `tests/data/SPDXJSONExample-v2.3.spdx.json`:
-
-* Make sure you install the optional dependencies `networkx` and `pygraphviz`. To do so run `pip install ".[graph_generation]"`.
-* Use `pyspdxtools -i --graph -o ` where `` is an output file name with valid format for `pygraphviz` (check
- the documentation [here](https://pygraphviz.github.io/documentation/stable/reference/agraph.html#pygraphviz.AGraph.draw)).
-* If you are using a source distribution, try running
- `pyspdxtools -i tests/data/SPDXJSONExample-v2.3.spdx.json --graph -o SPDXJSONExample-v2.3.spdx.png` to generate
- a png with an overview of the structure of the example file.
-
-## Library usage
+ - This feature generates a graph representing all elements in the SPDX document and their connections based on the provided
+ relationships. The graph can be rendered to a picture. Below is an example for the file `tests/spdx/data/SPDXJSONExample-v2.3.spdx.json`:
+ 
+
+ - Make sure you install the optional dependencies `networkx` and `pygraphviz`. To do so run `pip install ".[graph_generation]"`.
+ - Use `pyspdxtools -i --graph -o ` where `` is an output file name with valid format for `pygraphviz` (check
+ the documentation [here](https://pygraphviz.github.io/documentation/stable/reference/agraph.html#pygraphviz.AGraph.draw)).
+ - If you are using a source distribution, try running
+ `pyspdxtools -i tests/spdx/data/SPDXJSONExample-v2.3.spdx.json --graph -o SPDXJSONExample-v2.3.spdx.png` to generate
+ a png with an overview of the structure of the example file.
+
+### Library usage
+
1. **DATA MODEL**
- * The `spdx_tools.spdx.model` package constitutes the internal SPDX v2.3 data model (v2.2 is simply a subset of this). All relevant classes for SPDX document creation are exposed in the `__init__.py` found [here](src%2Fspdx_tools%2Fspdx%2Fmodel%2F__init__.py).
- * SPDX objects are implemented via `@dataclass_with_properties`, a custom extension of `@dataclass`.
- * Each class starts with a list of its properties and their possible types. When no default value is provided, the property is mandatory and must be set during initialization.
- * Using the type hints, type checking is enforced when initializing a new instance or setting/getting a property on an instance
+
+ - The `spdx_tools.spdx.model` package constitutes the internal SPDX v2.3 data model (v2.2 is simply a subset of this). All relevant classes for SPDX document creation are exposed in the `__init__.py` found [here](src%2Fspdx_tools%2Fspdx%2Fmodel%2F__init__.py).
+ - SPDX objects are implemented via `@dataclass_with_properties`, a custom extension of `@dataclass`.
+ - Each class starts with a list of its properties and their possible types. When no default value is provided, the property is mandatory and must be set during initialization.
+ - Using the type hints, type checking is enforced when initializing a new instance or setting/getting a property on an instance
(wrong types will raise `ConstructorTypeError` or `TypeError`, respectively). This makes it easy to catch invalid properties early and only construct valid documents.
- * Note: in-place manipulations like `list.append(item)` will circumvent the type checking (a `TypeError` will still be raised when reading `list` again). We recommend using `list = list + [item]` instead.
- * The main entry point of an SPDX document is the `Document` class from the [document.py](src%2Fspdx_tools%2Fspdx%2Fmodel%2Fdocument.py) module, which links to all other classes.
- * For license handling, the [license_expression](https://github.com/nexB/license-expression) library is used.
- * Note on `documentDescribes` and `hasFiles`: These fields will be converted to relationships in the internal data model. As they are deprecated, these fields will not be written in the output.
+ - Note: in-place manipulations like `list.append(item)` will circumvent the type checking (a `TypeError` will still be raised when reading `list` again). We recommend using `list = list + [item]` instead.
+ - The main entry point of an SPDX document is the `Document` class from the [document.py](src%2Fspdx_tools%2Fspdx%2Fmodel%2Fdocument.py) module, which links to all other classes.
+ - For license handling, the [license_expression](https://github.com/nexB/license-expression) library is used.
+ - Note on `documentDescribes` and `hasFiles`: These fields will be converted to relationships in the internal data model. As they are deprecated, these fields will not be written in the output.
+
2. **PARSING**
- * Use `parse_file(file_name)` from the `parse_anything.py` module to parse an arbitrary file with one of the supported file endings.
- * Successful parsing will return a `Document` instance. Unsuccessful parsing will raise `SPDXParsingError` with a list of all encountered problems.
+
+ - Use `parse_file(file_name)` from the `parse_anything.py` module to parse an arbitrary file with one of the supported file endings.
+ - Successful parsing will return a `Document` instance. Unsuccessful parsing will raise `SPDXParsingError` with a list of all encountered problems.
+
3. **VALIDATING**
- * Use `validate_full_spdx_document(document)` to validate an instance of the `Document` class.
- * This will return a list of `ValidationMessage` objects, each consisting of a String describing the invalidity and a `ValidationContext` to pinpoint the source of the validation error.
- * Validation depends on the SPDX version of the document. Note that only versions `SPDX-2.2` and `SPDX-2.3` are supported by this tool.
+
+ - Use `validate_full_spdx_document(document)` to validate an instance of the `Document` class.
+ - This will return a list of `ValidationMessage` objects, each consisting of a String describing the invalidity and a `ValidationContext` to pinpoint the source of the validation error.
+ - Validation depends on the SPDX version of the document. Note that only versions `SPDX-2.2` and `SPDX-2.3` are supported by this tool.
+
4. **WRITING**
- * Use `write_file(document, file_name)` from the `write_anything.py` module to write a `Document` instance to the specified file.
+
+ - Use `write_file(document, file_name)` from the `write_anything.py` module to write a `Document` instance to the specified file.
The serialization format is determined from the filename ending.
- * Validation is performed per default prior to the writing process, which is cancelled if the document is invalid. You can skip the validation via `write_file(document, file_name, validate=False)`.
+ - Validation is performed per default prior to the writing process, which is cancelled if the document is invalid. You can skip the validation via `write_file(document, file_name, validate=False)`.
Caution: Only valid documents can be serialized reliably; serialization of invalid documents is not supported.
-## Example
+### Example
+
Here are some examples of possible use cases to quickly get you started with the spdx-tools.
If you want more examples, like how to create an SPDX document from scratch, have a look [at the examples folder](examples).
+
```python
import logging
from license_expression import get_spdx_licensing
-from spdx_tools.spdx.model import (Checksum, ChecksumAlgorithm, File,
+from spdx_tools.spdx.model import (Checksum, ChecksumAlgorithm, File,
FileType, Relationship, RelationshipType)
from spdx_tools.spdx.parser.parse_anything import parse_file
from spdx_tools.spdx.validation.document_validator import validate_full_spdx_document
@@ -147,14 +164,14 @@ document.creation_info.name = "new document name"
# define a file and a DESCRIBES relationship between the file and the document
checksum = Checksum(ChecksumAlgorithm.SHA1, "71c4025dd9897b364f3ebbb42c484ff43d00791c")
-file = File(name="./fileName.py", spdx_id="SPDXRef-File", checksums=[checksum],
- file_types=[FileType.TEXT],
+file = File(name="./fileName.py", spdx_id="SPDXRef-File", checksums=[checksum],
+ file_types=[FileType.TEXT],
license_concluded=get_spdx_licensing().parse("MIT and GPL-2.0"),
license_comment="licenseComment", copyright_text="copyrightText")
relationship = Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-File")
-# add the file and the relationship to the document
+# add the file and the relationship to the document
# (note that we do not use "document.files.append(file)" as that would circumvent the type checking)
document.files = document.files + [file]
document.relationships = document.relationships + [relationship]
@@ -165,51 +182,52 @@ validation_messages = validate_full_spdx_document(document)
for validation_message in validation_messages:
logging.warning(validation_message.validation_message)
-# if there are no validation messages, the document is valid
+# if there are no validation messages, the document is valid
# and we can safely serialize it without validating again
if not validation_messages:
write_file(document, "new_spdx_document.rdf", validate=False)
```
-# Quickstart to SPDX 3.0
+## Quickstart to SPDX 3.0
+
In contrast to SPDX v2, all elements are now subclasses of the central `Element` class.
-This includes packages, files, snippets, relationships, annotations, but also SBOMs, SpdxDocuments, and more.
+This includes packages, files, snippets, relationships, annotations, but also SBOMs, SpdxDocuments, and more.
For serialization purposes, all Elements that are to be serialized into the same file are collected in a `Payload`.
This is just a dictionary that maps each Element's SpdxId to itself.
Use the `write_payload()` functions to serialize a payload.
-There currently are two options:
-* The `spdx_tools.spdx3.writer.json_ld.json_ld_writer` module generates a JSON-LD file of the payload.
-* The `spdx_tools.spdx3.writer.console.payload_writer` module prints a debug output to console. Note that this is not an official part of the SPDX specification and will probably be dropped as soon as a better standard emerges.
+There currently are two options:
+
+- The `spdx_tools.spdx3.writer.json_ld.json_ld_writer` module generates a JSON-LD file of the payload.
+- The `spdx_tools.spdx3.writer.console.payload_writer` module prints a debug output to console. Note that this is not an official part of the SPDX specification and will probably be dropped as soon as a better standard emerges.
You can convert an SPDX v2 document to v3 via the `spdx_tools.spdx3.bump_from_spdx2.spdx_document` module.
The `bump_spdx_document()` function will return a payload containing an `SpdxDocument` Element and one Element for each package, file, snippet, relationship, or annotation contained in the v2 document.
+## Dependencies
-# Dependencies
-
-* PyYAML: https://pypi.org/project/PyYAML/ for handling YAML.
-* xmltodict: https://pypi.org/project/xmltodict/ for handling XML.
-* rdflib: https://pypi.python.org/pypi/rdflib/ for handling RDF.
-* ply: https://pypi.org/project/ply/ for handling tag-value.
-* click: https://pypi.org/project/click/ for creating the CLI interface.
-* beartype: https://pypi.org/project/beartype/ for type checking.
-* uritools: https://pypi.org/project/uritools/ for validation of URIs.
-* license-expression: https://pypi.org/project/license-expression/ for handling SPDX license expressions.
+- PyYAML: for handling YAML.
+- xmltodict: for handling XML.
+- rdflib: for handling RDF.
+- ply: for handling tag-value.
+- click: for creating the CLI interface.
+- beartype: for type checking.
+- uritools: for validation of URIs.
+- license-expression: for handling SPDX license expressions.
-# Support
+## Support
-* Submit issues, questions or feedback at https://github.com/spdx/tools-python/issues
-* Join the chat at https://gitter.im/spdx-org/Lobby
-* Join the discussion on https://lists.spdx.org/g/spdx-tech and
- https://spdx.dev/participate/tech/
+- Submit issues, questions or feedback at
+- Join the chat at
+- Join the discussion on and
+
-# Contributing
+## Contributing
Contributions are very welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md) for instructions on how to contribute to the
codebase.
-# History
+## History
This is the result of an initial GSoC contribution by @[ah450](https://github.com/ah450)
-(or https://github.com/a-h-i) and is maintained by a community of SPDX adopters and enthusiasts.
+(or ) and is maintained by a community of SPDX adopters and enthusiasts.
In order to prepare for the release of SPDX v3.0, the repository has undergone a major refactoring during the time from 11/2022 to 07/2023.
diff --git a/pyproject.toml b/pyproject.toml
index 004e67347..e773d841c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -9,35 +9,57 @@ maintainers = [
{ name = "Philippe Ombredanne", email = "pombredanne@gmail.com" },
{ name = "SPDX group at the Linux Foundation and others" },
]
-license = { text = "Apache-2.0" }
+license = "Apache-2.0"
description = "SPDX parser and tools."
readme = "README.md"
classifiers = [
+ "Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Intended Audience :: System Administrators",
- "License :: OSI Approved :: Apache Software License",
- "Programming Language :: Python :: 3.8",
- "Programming Language :: Python :: 3.9",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
+ "Topic :: Software Development :: Documentation",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ "Topic :: Utilities",
+]
+requires-python = ">=3.10"
+dependencies = [
+ "beartype",
+ "click",
+ "license_expression",
+ "ply",
+ "pyyaml",
+ "rdflib",
+ "semantic_version",
+ "uritools",
+ "xmltodict",
]
-urls = { Homepage = "https://github.com/spdx/tools-python" }
-requires-python = ">=3.8"
-dependencies = ["click", "pyyaml", "xmltodict", "rdflib", "beartype", "uritools", "license_expression", "ply", "semantic_version"]
dynamic = ["version"]
[project.optional-dependencies]
-test = ["pytest", "pyshacl", "tzdata"]
-code_style = ["isort", "black", "flake8"]
-graph_generation = ["pygraphviz", "networkx"]
-development = ["black", "flake8", "isort", "networkx", "pytest", "pyshacl"]
+test = ["pyshacl", "pytest", "tzdata"]
+code_style = ["black", "flake8", "isort"]
+graph_generation = ["networkx", "pygraphviz"]
+development = ["black", "flake8", "isort", "networkx", "pyshacl", "pytest"]
[project.scripts]
pyspdxtools = "spdx_tools.spdx.clitools.pyspdxtools:main"
pyspdxtools3 = "spdx_tools.spdx3.clitools.pyspdxtools3:main"
+[project.urls]
+Homepage = "https://github.com/spdx/tools-python"
+Documentation = "https://spdx.github.io/tools-python/"
+Repository = "https://github.com/spdx/tools-python.git"
+Issues = "https://github.com/spdx/tools-python/issues"
+Changelog = "https://github.com/spdx/tools-python/blob/main/CHANGELOG.md"
+
[tool.setuptools]
-zip-safe = false # because of the uses of __file__: https://github.com/spdx/tools-python/issues/257
include-package-data = true
[tool.setuptools.packages.find]
diff --git a/src/spdx_tools/spdx/parser/json/json_parser.py b/src/spdx_tools/spdx/parser/json/json_parser.py
index 219ccfed2..675ed2560 100644
--- a/src/spdx_tools/spdx/parser/json/json_parser.py
+++ b/src/spdx_tools/spdx/parser/json/json_parser.py
@@ -3,14 +3,33 @@
# SPDX-License-Identifier: Apache-2.0
import json
-from beartype.typing import Dict
+from beartype.typing import Any, Dict
from spdx_tools.spdx.model import Document
from spdx_tools.spdx.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser
+# chars we don't want to see in SBOMs
+CONTROL_CHARS_MAP = {
+ 8: None, # ASCII/UTF-8: backspace
+ 12: None, # ASCII/UTF-8: formfeed
+}
+
+
+def remove_control_chars_from_value(value: Any) -> Any:
+ if isinstance(value, str):
+ return value.translate(CONTROL_CHARS_MAP)
+ elif isinstance(value, list):
+ for i in range(len(value)):
+ value[i] = remove_control_chars_from_value(value[i])
+ return value
+
+
+def remove_json_control_chars_hook(pairs: list) -> dict:
+ return {k: remove_control_chars_from_value(v) for k, v in pairs}
+
def parse_from_file(file_name: str, encoding: str = "utf-8") -> Document:
with open(file_name, encoding=encoding) as file:
- input_doc_as_dict: Dict = json.load(file)
+ input_doc_as_dict: Dict = json.load(file, object_pairs_hook=remove_json_control_chars_hook)
return JsonLikeDictParser().parse(input_doc_as_dict)
diff --git a/src/spdx_tools/spdx/parser/parse_anything.py b/src/spdx_tools/spdx/parser/parse_anything.py
index ff41e2b77..b47dc50dc 100644
--- a/src/spdx_tools/spdx/parser/parse_anything.py
+++ b/src/spdx_tools/spdx/parser/parse_anything.py
@@ -12,6 +12,7 @@
import logging
from spdx_tools.spdx.formats import FileFormat, file_name_to_format
+from spdx_tools.spdx.model import Document
from spdx_tools.spdx.parser.json import json_parser
from spdx_tools.spdx.parser.rdf import rdf_parser
from spdx_tools.spdx.parser.tagvalue import tagvalue_parser
@@ -19,7 +20,7 @@
from spdx_tools.spdx.parser.yaml import yaml_parser
-def parse_file(file_name: str, encoding: str = "utf-8"):
+def parse_file(file_name: str, encoding: str = "utf-8") -> Document:
if encoding != "utf-8":
logging.warning(
"It's recommended to use the UTF-8 encoding for any SPDX file. Consider changing the encoding of the file."
diff --git a/src/spdx_tools/spdx/validation/uri_validators.py b/src/spdx_tools/spdx/validation/uri_validators.py
index c14d196f4..7720c3fb5 100644
--- a/src/spdx_tools/spdx/validation/uri_validators.py
+++ b/src/spdx_tools/spdx/validation/uri_validators.py
@@ -12,23 +12,26 @@
"\\/\\/|ftp:\\/\\/)?([\\w\\-.!~*'()%;:&=+$,]+@)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+){0,100}\\.[a-z]{2,5}"
"(:[0-9]{1,5})?(\\/.*)?"
)
+url_pattern_ignore_case = re.compile(url_pattern, re.IGNORECASE)
+
supported_download_repos: str = "(git|hg|svn|bzr)"
git_pattern = "(git\\+git@[a-zA-Z0-9\\.\\-]+:[a-zA-Z0-9/\\\\.@\\-]+)"
bazaar_pattern = "(bzr\\+lp:[a-zA-Z0-9\\.\\-]+)"
download_location_pattern = (
"^(((" + supported_download_repos + "\\+)?" + url_pattern + ")|" + git_pattern + "|" + bazaar_pattern + ")$"
)
+compiled_pattern = re.compile(download_location_pattern, re.IGNORECASE)
def validate_url(url: str) -> List[str]:
- if not re.match(url_pattern, url):
+ if not url_pattern_ignore_case.match(url):
return [f"must be a valid URL, but is: {url}"]
return []
def validate_download_location(location: str) -> List[str]:
- if not (validate_url(location) == [] or re.match(download_location_pattern, location)):
+ if not (validate_url(location) == [] or compiled_pattern.match(location)):
return [f"must be a valid URL or download location according to the specification, but is: {location}"]
return []
diff --git a/src/spdx_tools/spdx3/writer/console/relationship_writer.py b/src/spdx_tools/spdx3/writer/console/relationship_writer.py
index 1a8b16a3e..9f61e5214 100644
--- a/src/spdx_tools/spdx3/writer/console/relationship_writer.py
+++ b/src/spdx_tools/spdx3/writer/console/relationship_writer.py
@@ -12,5 +12,5 @@ def write_relationship(relationship: Relationship, text_output: TextIO, heading:
if heading:
text_output.write("## Relationship\n")
write_element_properties(relationship, text_output)
- for property_name in relationship.__annotations__.keys():
+ for property_name in Relationship.__annotations__.keys():
write_value(property_name, getattr(relationship, property_name), text_output)
diff --git a/src/spdx_tools/spdx3/writer/json_ld/json_ld_writer.py b/src/spdx_tools/spdx3/writer/json_ld/json_ld_writer.py
index 69a4d763c..3c3e0819a 100644
--- a/src/spdx_tools/spdx3/writer/json_ld/json_ld_writer.py
+++ b/src/spdx_tools/spdx3/writer/json_ld/json_ld_writer.py
@@ -2,20 +2,24 @@
#
# SPDX-License-Identifier: Apache-2.0
import json
-import os
+from importlib import resources
from spdx_tools.spdx3.payload import Payload
-from spdx_tools.spdx3.writer.json_ld.json_ld_converter import convert_payload_to_json_ld_list_of_elements
+from spdx_tools.spdx3.writer.json_ld.json_ld_converter import (
+ convert_payload_to_json_ld_list_of_elements,
+)
def write_payload(payload: Payload, file_name: str):
element_list = convert_payload_to_json_ld_list_of_elements(payload)
# this will be obsolete as soon as the context is publicly available under some URI
- with open(os.path.join(os.path.dirname(__file__), "context.json"), "r") as infile:
+ # Note: 3.0.1 context is now available at
+ # https://spdx.org/rdf/3.0.1/spdx-context.jsonld
+ with resources.files("spdx_tools.spdx3.writer.json_ld").joinpath("context.json").open("r") as infile:
context = json.load(infile)
complete_dict = {"@context": context, "@graph": element_list}
- with open(file_name + ".jsonld", "w") as out:
+ with open(file_name + ".jsonld", "w", encoding="utf-8") as out:
json.dump(complete_dict, out, indent=2)
diff --git a/src/spdx_tools/spdx3/writer/json_ld/owl_to_context.py b/src/spdx_tools/spdx3/writer/json_ld/owl_to_context.py
index f6bc7c35a..cdeb3144e 100644
--- a/src/spdx_tools/spdx3/writer/json_ld/owl_to_context.py
+++ b/src/spdx_tools/spdx3/writer/json_ld/owl_to_context.py
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: Apache-2.0
import json
-import os.path
+from importlib import resources
# current workflow: markdown files + spec_parser -> model.ttl -> convert to json_ld: SPDX_OWL.json ->
# use the function below to generate context.json
@@ -108,8 +108,11 @@ def convert_spdx_owl_to_jsonld_context(spdx_owl: str = "SPDX_OWL.json"):
else:
print(f"unknown node_type: {node_type}")
- with open(os.path.join(os.path.dirname(__file__), "context.json"), "w") as infile:
- json.dump(context_dict, infile)
+ with resources.as_file(
+ resources.files("spdx_tools.spdx3.writer.json_ld").joinpath("context.json")
+ ) as context_path:
+ with open(context_path, "w", encoding="utf-8") as outfile:
+ json.dump(context_dict, outfile)
if __name__ == "__main__":
diff --git a/src/spdx_tools/spdx3/writer/json_ld/process.md b/src/spdx_tools/spdx3/writer/json_ld/process.md
index 7d04d5ccd..02dc7b715 100644
--- a/src/spdx_tools/spdx3/writer/json_ld/process.md
+++ b/src/spdx_tools/spdx3/writer/json_ld/process.md
@@ -1,27 +1,33 @@
-### Workflow
+# Workflow
+
+Official SPDX v3.0 serialization documentation and context file
+are available at:
+
+## Manually generate context file
Process to produce context file and a serialization example:
-1. Run
-```
-spec-parser --gen-md --gen-refs --gen-rdf ../spdx-3-model/model
-```
-- spdx-3-model (commit: 6cb4316, last commit where spec-parser is able to run)
-- spec-parser (main with commits from PR 44, 45)
+1. Run
+
+ ```sh
+ python spec-parser/main.py spdx-3-model/model parser_output
+ ```
-2. Convert the generated `spec-parser/md_generated/model.ttl` to a json-ld file using https://frogcat.github.io/ttl2jsonld/demo/.
-3. Convert owl to context using `convert_spdx_owl_to_jsonld_context("SPDX_OWL.json")`.
-4. Place the generated `context.json` in `spdx_tools/spdx3/writer/jsonld/`.
-5. To generate the jsonld from the testfile run
-```
-pyspdxtools3 -i ./tests/spdx/data/SPDXJSONExample-v2.3.spdx.json -o example_with_context
-```
+ - spdx-3-model (main; where v3.0.1 development happens)
+ - spec-parser (main)
+2. Convert the generated `parser_output/rdf/spdx-model.ttl` to a JSON-LD file
+ using .
+3. Convert OWL to context using `owl_to_context.py`.
+4. Place the generated `context.json` in `src/spdx_tools/spdx3/writer/json_ld/`.
+5. To generate the JSON-LD from the test file, run:
-### Manually
+ ```sh
+ pyspdxtools3 -i ./tests/spdx/data/SPDXJSONExample-v2.3.spdx.json -o example_with_context
+ ```
+## Known limitations
-### Known limitations
- Validation of enums does not work
- Additional keys seem to be ignored in validation
-- inherited properties aren't validated
+- Inherited properties aren't validated
diff --git a/tests/spdx/data/ControlCharacters.spdx.json b/tests/spdx/data/ControlCharacters.spdx.json
new file mode 100644
index 000000000..d79ee2e92
--- /dev/null
+++ b/tests/spdx/data/ControlCharacters.spdx.json
@@ -0,0 +1,46 @@
+{
+ "spdxVersion": "SPDX-2.2",
+ "dataLicense": "CC0-1.0",
+ "SPDXID": "SPDXRef-DOCUMENT",
+ "creationInfo": {
+ "created": "2020-11-24T01:12:27Z",
+ "creators": ["Person: Nisha \b\f K (nishak@vmware.com)"]
+ },
+ "name": "golang-dist",
+ "documentNamespace": "https://swinslow.net/spdx-examples/example7/golang-dist-492dfde4-318b-49f7-b48c-934bfafbde48",
+ "documentDescribes": ["SPDXRef-golang-dist"],
+ "packages": [
+ {
+ "name": "go1.16.4.linux-amd64",
+ "SPDXID": "SPDXRef-golang-dist",
+ "downloadLocation": "https://golang.org/dl/go1.16.4.linux-amd64.tar.gz",
+ "versionInfo": "1.16.4",
+ "filesAnalyzed": false,
+ "checksums": [
+ {
+ "algorithm": "SHA256",
+ "checksumValue": "7154e88f5a8047aad4b80ebace58a059e36e7e2e4eb3b383127a28c711b4ff59"
+ }
+ ],
+ "licenseConcluded": "NOASSERTION",
+ "licenseDeclared": "LicenseRef-Golang-BSD-plus-Patents",
+ "copyrightText": "Copyright (c) 2009 The Go Authors. \b All rights reserved."
+ },
+ {
+ "name": "go",
+ "SPDXID": "SPDXRef-go-compiler",
+ "downloadLocation": "https://golang.org/dl/go1.16.4.linux-amd64.tar.gz",
+ "versionInfo": "1.16.4",
+ "filesAnalyzed": false,
+ "licenseConcluded": "NOASSERTION",
+ "licenseDeclared": "NOASSERTION",
+ "copyrightText": "NOASSERTION"
+ }
+ ],
+ "hasExtractedLicensingInfos": [
+ {
+ "licenseId": "LicenseRef-Golang-BSD-plus-Patents",
+ "extractedText": "Golang BSD plus Patents \"\\\/\b\f\n\r\t"
+ }
+ ]
+}
diff --git a/tests/spdx/parser/jsonlikedict/test_json_parser.py b/tests/spdx/parser/jsonlikedict/test_json_parser.py
new file mode 100644
index 000000000..ab3249d63
--- /dev/null
+++ b/tests/spdx/parser/jsonlikedict/test_json_parser.py
@@ -0,0 +1,11 @@
+import os
+
+from spdx_tools.spdx.parser.json import json_parser
+
+
+def test_parse_control_characters():
+ doc = json_parser.parse_from_file(
+ os.path.join(os.path.dirname(__file__), "../../data/ControlCharacters.spdx.json")
+ )
+ assert doc.creation_info.creators[0].name == "Nisha K"
+ assert doc.extracted_licensing_info[0].extracted_text == 'Golang BSD plus Patents "\\/\n\r\t'
diff --git a/tests/spdx/test_cli.py b/tests/spdx/test_cli.py
index 0019cf3a3..d3b0bd4f3 100644
--- a/tests/spdx/test_cli.py
+++ b/tests/spdx/test_cli.py
@@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: 2023 spdx contributors
#
# SPDX-License-Identifier: Apache-2.0
-import os
+from importlib import resources
import pytest
from click.testing import CliRunner
@@ -12,16 +12,16 @@
@pytest.mark.parametrize(
"options",
[
- ("--infile", os.path.join(os.path.dirname(__file__), "data/SPDXJSONExample-v2.3.spdx.json")),
- ("-i", os.path.join(os.path.dirname(__file__), "data/SPDXJSONExample-v2.3.spdx.json"), "--novalidation"),
+ ("--infile", str(resources.files("tests.spdx.data").joinpath("SPDXJSONExample-v2.3.spdx.json"))),
+ ("-i", str(resources.files("tests.spdx.data").joinpath("SPDXJSONExample-v2.3.spdx.json")), "--novalidation"),
(
"-i",
- os.path.join(os.path.dirname(__file__), "data/SPDXJSONExample-v2.3.spdx.json"),
+ str(resources.files("tests.spdx.data").joinpath("SPDXJSONExample-v2.3.spdx.json")),
"--novalidation",
"--version",
"SPDX-2.3",
),
- ("-i", os.path.join(os.path.dirname(__file__), "data/SPDXJSONExample-v2.3.spdx.json"), "-o", "-"),
+ ("-i", str(resources.files("tests.spdx.data").joinpath("SPDXJSONExample-v2.3.spdx.json")), "-o", "-"),
],
)
def test_cli_with_system_exit_code_0(options):
@@ -37,9 +37,11 @@ def test_cli_with_system_exit_code_0(options):
[
(
"-i",
- os.path.join(
- os.path.dirname(__file__),
- "data/invalid/spdx-trivy-vmware_log-intelligence-fluentd-sha256_086af034f561f343f633be9d9f9e95f65ae6c61b8ddb2c6755ef5bb25b40f53a.json", # noqa: E501
+ str(
+ resources.files("tests.spdx.data.invalid").joinpath(
+ "spdx-trivy-vmware_log-intelligence-fluentd-"
+ "sha256_086af034f561f343f633be9d9f9e95f65ae6c61b8ddb2c6755ef5bb25b40f53a.json"
+ )
),
),
("-i", "non_existent_file.spdx"),
@@ -57,8 +59,8 @@ def test_cli_with_system_exit_code_1(options):
"options",
[
(),
- ("-i", os.path.join(os.path.dirname(__file__), "data/SPDXJSONExample-v2.3.spdx.json"), "--version"),
- ("-i", os.path.join(os.path.dirname(__file__), "data/SPDXJSONExample-v2.3.spdx.json"), "-o"),
+ ("-i", str(resources.files("tests.spdx.data").joinpath("SPDXJSONExample-v2.3.spdx.json")), "--version"),
+ ("-i", str(resources.files("tests.spdx.data").joinpath("SPDXJSONExample-v2.3.spdx.json")), "-o"),
],
)
def test_cli_with_system_exit_code_2(options):
diff --git a/tests/spdx/validation/test_uri_validators.py b/tests/spdx/validation/test_uri_validators.py
index 2d374ee8e..0fb4ec7cb 100644
--- a/tests/spdx/validation/test_uri_validators.py
+++ b/tests/spdx/validation/test_uri_validators.py
@@ -14,6 +14,7 @@
"https://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82...",
"http://some.url",
"http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz",
+ "HTTP://SOME.URL",
],
)
def test_valid_url(input_value):
@@ -79,6 +80,7 @@ def test_invalid_url(input_value):
"bzr+https://bzr.myproject.org/MyProject/trunk@2019",
"bzr+http://bzr.myproject.org/MyProject/trunk@v1.0",
"bzr+https://bzr.myproject.org/MyProject/trunk@2019#src/somefile.c",
+ "BZR+HTTPS://BZR.MYPROJECT.ORG/MYPROJECT/TRUNK@2019#SRC/SOMEFILE.C",
],
)
def test_valid_package_download_location(input_value):
@@ -106,6 +108,7 @@ def test_invalid_package_download_location(input_value):
"https://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82...",
"h://someweirdtest^?",
"https://some.uri that goes on!?",
+ "HTtPS://SOME.URI With CAPITALS",
],
)
def test_valid_uri(input_value):
diff --git a/tests/spdx3/writer/json_ld/test_json_ld_writer.py b/tests/spdx3/writer/json_ld/test_json_ld_writer.py
index f46469b1f..4718f3e6e 100644
--- a/tests/spdx3/writer/json_ld/test_json_ld_writer.py
+++ b/tests/spdx3/writer/json_ld/test_json_ld_writer.py
@@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: 2023 spdx contributors
#
# SPDX-License-Identifier: Apache-2.0
-import os
+from importlib import resources
from spdx_tools.spdx3.bump_from_spdx2.spdx_document import bump_spdx_document
from spdx_tools.spdx3.payload import Payload
@@ -15,4 +15,5 @@ def test_json_writer():
payload: Payload = bump_spdx_document(spdx2_document)
# this currently generates an actual file to look at, this should be changed to a temp file later
- write_payload(payload, os.path.join(os.path.dirname(__file__), "../../../SPDX3_jsonld_test"))
+ with resources.as_file(resources.files("tests.spdx3.writer.json_ld").joinpath("SPDX3_jsonld_test")) as output_file:
+ write_payload(payload, str(output_file))