Skip to content

Commit 9de3c51

Browse files
committed
Drop the [validation] extra from cyclonedx-python-lib runtime dependency
1 parent befdd7b commit 9de3c51

6 files changed

Lines changed: 58 additions & 26 deletions

File tree

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- [mpm] Fix `pkg:cpan/…`, `pkg:guix/…`, and `pkg:nix/…` pURL specifiers raising `Unrecognized pURL type` even though those managers are implemented. The `cpan`, `guix`, and `nix` entries in `PURL_MAP` were set to `None`, which shadowed the manager-id fallback in `Specifier.parse_purl()`; they now map to their respective managers.
99
- [mpm] Add a `scope` attribute (`ManagerScope.SYSTEM` or `ManagerScope.PROJECT`) to `PackageManager` to distinguish system-wide installers from project-local dependency managers, plus a `discover_projects()` extension point reserved for the latter. All maintained managers are system-scoped; project scope is not supported yet.
1010
- [zerobrew] Add `upgrade` and `upgrade_all` support by wrapping the `zb upgrade` command introduced in zerobrew `0.3.0`, and bump the minimum required `zb` version from `0.2.0` to `0.3.0`. `0.3.0` also fixes the Linux Python install failures reported in https://github.com/lucasgelfond/zerobrew/issues/336, so the `zerobrew` install tests now run as stable on both x86 and ARM Linux.
11+
- [mpm] Drop the `[validation]` extra from the `cyclonedx-python-lib` runtime dependency, removing `jsonschema`, `rfc3987-syntax`, `lark`, and `lxml` from `mpm`'s install footprint. CycloneDX SBOM schema validation now runs in the test suite instead of inside `CycloneDX.export()`.
1112

1213
## [`6.5.1` (2026-05-28)](https://github.com/kdeldycke/meta-package-manager/compare/v6.5.0...v6.5.1)
1314

meta_package_manager/managers/pip.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,8 @@ def _own_dependency_names() -> frozenset[str]:
202202
declared as an independent ``resource`` block in the formula and
203203
``pip install``-ed individually into the formula's virtualenv. Because
204204
each resource is installed in isolation, pip never sees that (for
205-
example) ``lxml`` is required by ``jsonschema[format-nongpl]`` which is
206-
required by ``cyclonedx-python-lib[validation]`` which is required by
205+
example) ``defusedxml`` is required by ``py-serializable`` which is
206+
required by ``cyclonedx-python-lib`` which is required by
207207
``meta-package-manager``. From pip's perspective every resource looks
208208
like a top-level package, so ``--not-required`` lets them through and
209209
they all appear as outdated.

meta_package_manager/sbom.py

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@
3939
from cyclonedx.output import make_outputter
4040
from cyclonedx.output.json import JsonV1Dot5
4141
from cyclonedx.schema import OutputFormat, SchemaVersion
42-
from cyclonedx.validation import BaseSchemabasedValidator, make_schemabased_validator
43-
from cyclonedx.validation.json import JsonStrictValidator
4442
from extra_platforms import current_platform
4543
from packageurl import PackageURL
4644
from spdx_tools.spdx.model import (
@@ -390,25 +388,23 @@ def add_package(self, manager: PackageManager, package: Package) -> None:
390388
)
391389

392390
def export(self) -> str:
393-
validator: BaseSchemabasedValidator
394-
if self.export_format == ExportFormat.JSON:
395-
content = JsonV1Dot5(self.document).output_as_string(indent=2)
396-
validator = JsonStrictValidator(SchemaVersion.V1_5)
391+
"""Serialize the document to its string representation.
397392
398-
elif self.export_format == ExportFormat.XML:
399-
writer = make_outputter(self.document, OutputFormat.XML, SchemaVersion.V1_6)
400-
content = writer.output_as_string(indent=2)
401-
validator = make_schemabased_validator(
402-
writer.output_format, writer.schema_version
403-
)
393+
.. note::
404394
405-
else:
406-
raise ValueError(f"{self.export_format} not supported.")
395+
Unlike :py:meth:`SPDX.export`, the generated document is not
396+
validated against its schema here. CycloneDX schema validation
397+
relies on ``cyclonedx-python-lib``'s ``[validation]`` extra, which
398+
pulls in ``jsonschema`` and, transitively, ``rfc3987-syntax``,
399+
``lark``, and ``lxml``. To keep that stack out of ``mpm``'s runtime
400+
dependencies, the validation runs in the test suite instead. See
401+
``tests/test_cli_sbom.py``.
402+
"""
403+
if self.export_format == ExportFormat.JSON:
404+
return JsonV1Dot5(self.document).output_as_string(indent=2)
407405

408-
logging.debug("Validate document...")
409-
errors = validator.validate_str(content)
410-
if errors:
411-
logging.debug(content)
412-
raise ValueError(f"Document is not valid. Errors: {errors}")
406+
if self.export_format == ExportFormat.XML:
407+
writer = make_outputter(self.document, OutputFormat.XML, SchemaVersion.V1_6)
408+
return writer.output_as_string(indent=2)
413409

414-
return content
410+
raise ValueError(f"{self.export_format} not supported.")

pyproject.toml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,11 @@ dependencies = [
156156
# by merge_default_map, which caused test isolation failures when a config
157157
# file was loaded in one test and affected subsequent tests.
158158
"click-extra>=7.16.1",
159-
# cyclonedx-python-lib 11.2.0 is the first to support Python 3.14.
160-
"cyclonedx-python-lib[validation]>=11.2",
159+
# cyclonedx-python-lib 11.2.0 is the first to support Python 3.14. The [validation]
160+
# extra is intentionally omitted: CycloneDX schema validation runs in the test suite
161+
# (see tests/test_cli_sbom.py), which keeps jsonschema, rfc3987-syntax, lark, and lxml
162+
# out of the runtime install.
163+
"cyclonedx-python-lib>=11.2",
161164
# extra-platforms 10.0.0 drops the distro dependency.
162165
"extra-platforms>=10",
163166
# packageurl-python 0.16.0 is the first to support Python 3.13.
@@ -203,6 +206,10 @@ test = [
203206
"click-extra[pytest]",
204207
"click-extra[toml]",
205208
"coverage[toml]>=7.12",
209+
# The [validation] extra provides the jsonschema-based validators used to assert
210+
# generated CycloneDX SBOMs are schema-valid. Kept test-only (not a runtime dep) so
211+
# the heavy validation stack stays out of the install. See tests/test_cli_sbom.py.
212+
"cyclonedx-python-lib[validation]",
206213
"extra-platforms",
207214
"pytest>=9.0.1",
208215
"pytest-cov>=7",

tests/test_cli_sbom.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
from xml.etree import ElementTree
2323

2424
import pytest
25+
from cyclonedx.schema import OutputFormat, SchemaVersion
26+
from cyclonedx.validation import make_schemabased_validator
27+
from cyclonedx.validation.json import JsonStrictValidator
2528
from yaml import Loader, load
2629

2730
from meta_package_manager.capabilities import Operations
@@ -30,6 +33,23 @@
3033
from .test_cli import CLISubCommandTests
3134

3235

36+
def assert_valid_cyclonedx(content: str, export_format: ExportFormat) -> None:
37+
"""Assert a CycloneDX export validates against its schema.
38+
39+
This guarantee used to live in
40+
:py:meth:`meta_package_manager.sbom.CycloneDX.export` at runtime. It moved
41+
here so the ``jsonschema``-based validation stack (``rfc3987-syntax``,
42+
``lark``, ``lxml``) stays out of ``mpm``'s runtime dependencies. See
43+
:py:mod:`meta_package_manager.sbom`.
44+
"""
45+
if export_format == ExportFormat.JSON:
46+
validator = JsonStrictValidator(SchemaVersion.V1_5)
47+
else:
48+
validator = make_schemabased_validator(OutputFormat.XML, SchemaVersion.V1_6)
49+
errors = validator.validate_str(content)
50+
assert not errors, f"Invalid CycloneDX {export_format} export: {errors}"
51+
52+
3353
@pytest.fixture
3454
def subcmd():
3555
# The details of operations are only logged at INFO level.
@@ -182,6 +202,12 @@ def test_output_to_file(self, invoke, subcmd, export_format, standard_name):
182202
assert result.exit_code == 0
183203
self.check_manager_selection(result)
184204

205+
if standard_name == "CycloneDX" and export_format in (
206+
ExportFormat.JSON,
207+
ExportFormat.XML,
208+
):
209+
assert_valid_cyclonedx(content, export_format)
210+
185211
if export_format == ExportFormat.JSON:
186212
json_content = json.loads(content)
187213
assert json_content

uv.lock

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)