From a9f65bc346b85198371a2730fcde5afdd4a12d9e Mon Sep 17 00:00:00 2001 From: sigma67 Date: Tue, 7 Oct 2025 07:57:30 +0200 Subject: [PATCH 1/4] make jsonschema dependency optional Signed-off-by: sigma67 --- pyproject.toml | 4 +++- sphinx_needs/needsfile.py | 9 +++++++-- sphinx_needs/schema/core.py | 15 ++++++++++++--- sphinx_needs/utils.py | 14 ++++++++++++++ 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0405e03e4..dfe00c1c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,6 @@ dependencies = [ "sphinx>=7.4,<9", "requests-file~=2.1", # external links "requests~=2.32", # external links - "jsonschema[format]>=3.2.0", # schema validation for needsimport and ontology "sphinx-data-viewer~=0.1.5", # needservice debug output "sphinxcontrib-jquery~=4.0", # needed for datatables in sphinx>=6 "tomli; python_version < '3.11'", # for needs_from_toml configuration @@ -40,6 +39,9 @@ dependencies = [ [project.optional-dependencies] plotting = ["matplotlib>=3.3.0"] # for needpie / needbar +schema = [ + "jsonschema[format]>=3.2.0", +] # for schema validation for needsimport and ontology test = [ "defusedxml~=0.7.1", "matplotlib>=3.3.0", diff --git a/sphinx_needs/needsfile.py b/sphinx_needs/needsfile.py index f8354704c..a16f15137 100644 --- a/sphinx_needs/needsfile.py +++ b/sphinx_needs/needsfile.py @@ -15,7 +15,6 @@ from functools import lru_cache from typing import Any -from jsonschema import Draft7Validator from sphinx.environment import BuildEnvironment from sphinx_needs.config import NeedsSphinxConfig @@ -23,6 +22,7 @@ from sphinx_needs.logging import get_logger, log_warning from sphinx_needs.need_item import NeedItem from sphinx_needs.needs_schema import FieldLiteralValue, FieldsSchema +from sphinx_needs.utils import import_jsonschema log = get_logger(__name__) @@ -276,7 +276,12 @@ def check_needs_data(data: Any) -> Errors: """ needs_schema = _load_schema() - validator = Draft7Validator(needs_schema) + jsonschema = import_jsonschema() + if jsonschema is None: + # skipping schema validation due to missing dependency + return Errors([]) + + validator = jsonschema.Draft7Validator(needs_schema) schema_errors = list(validator.iter_errors(data)) # In future there may be additional types of validations. diff --git a/sphinx_needs/schema/core.py b/sphinx_needs/schema/core.py index cfcf10d4a..659af7359 100644 --- a/sphinx_needs/schema/core.py +++ b/sphinx_needs/schema/core.py @@ -2,10 +2,9 @@ from __future__ import annotations +import typing from typing import Any, cast -from jsonschema import Draft202012Validator, FormatChecker, ValidationError - from sphinx_needs.config import NeedsSphinxConfig from sphinx_needs.need_item import NeedItem from sphinx_needs.schema.config import ( @@ -27,6 +26,7 @@ save_debug_files, ) from sphinx_needs.schema.utils import get_properties_from_schema +from sphinx_needs.utils import import_jsonschema from sphinx_needs.views import NeedsView # TODO(Marco): error for conflicting unevaluatedProperties @@ -62,6 +62,9 @@ _needs_schema: dict[str, Any] = {} """The needs schema as it would be written to needs.json, generated by generate_needs_schema().""" +if typing.TYPE_CHECKING: + from jsonschema import ValidationError + def merge_static_schemas(config: NeedsSphinxConfig) -> bool: """ @@ -561,7 +564,13 @@ def get_localschema_errors( :raises jsonschema_rs.ValidationError: If the schema is invalid cannot be built. """ - validator = Draft202012Validator(schema, format_checker=FormatChecker()) + jsonschema = import_jsonschema() + if jsonschema is None: + # skip schema validation if extra is not installed + return [] + validator = jsonschema.Draft202012Validator( + schema, format_checker=jsonschema.FormatChecker() + ) return list(validator.iter_errors(instance=need)) diff --git a/sphinx_needs/utils.py b/sphinx_needs/utils.py index 97c532caf..9f767f544 100644 --- a/sphinx_needs/utils.py +++ b/sphinx_needs/utils.py @@ -8,6 +8,7 @@ from collections.abc import Callable from dataclasses import dataclass from functools import lru_cache, reduce, wraps +from types import ModuleType from typing import TYPE_CHECKING, Any, Protocol, TypeVar from urllib.parse import urlparse @@ -419,6 +420,19 @@ def import_matplotlib() -> matplotlib | None: return matplotlib +@lru_cache +def import_jsonschema() -> ModuleType | None: + """Import and return matplotlib, or return None if it cannot be imported. + + Also sets the interactive backend to ``Agg``, if ``DISPLAY`` is not set. + """ + try: + import jsonschema + except ImportError: + return None + return jsonschema + + def save_matplotlib_figure( app: Sphinx, figure: FigureBase, basename: str, fromdocname: str ) -> nodes.image: From 233aef32933afeca07b8ff63bc004c82da7bb5ba Mon Sep 17 00:00:00 2001 From: sigma67 Date: Tue, 7 Oct 2025 08:10:09 +0200 Subject: [PATCH 2/4] add test Signed-off-by: sigma67 --- .github/workflows/ci.yaml | 24 ++++++++++++++++++++++++ README.rst | 6 ++++++ docker/Dockerfile | 8 ++++---- docs/installation.rst | 6 ++++++ tests/no_jsonschema_tests.py | 13 +++++++++++++ 5 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 tests/no_jsonschema_tests.py diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b96bbafd6..594125139 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -132,6 +132,30 @@ jobs: run: sphinx-build -b html . _build working-directory: docs + tests-no-jsonschema: + name: Test matplotlib uninstalled + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set Up Python + uses: actions/setup-python@v5 + with: + python-version: "3.10" + - name: Update pip + run: python -m pip install --upgrade pip + - name: Install Dependencies + run: | + python -m pip install -e .[test,docs] + python -m pip uninstall -y jsonschema + python -m pip freeze + - name: Run pytest + run: | + python -m pytest -v tests/no_mpl_tests.py + - name: Run HTML build + # the docs should build without matplotlib (just issuing warnings) + run: sphinx-build -b html . _build + working-directory: docs + check: # This job does nothing and is only used for the branch protection diff --git a/README.rst b/README.rst index c14f741cb..ef35bf02e 100644 --- a/README.rst +++ b/README.rst @@ -54,6 +54,12 @@ Using pip pip install sphinx-needs +To use schema validation features of sphinx-needs, you need to also install ``jsonschema``, which is available *via* the ``schema`` extra: + +.. code-block:: bash + + pip install sphinx-needs[schema] + If you wish to also use the plotting features of sphinx-needs (see ``needbar`` and ``needpie``), you need to also install ``matplotlib``, which is available *via* the ``plotting`` extra: .. code-block:: bash diff --git a/docker/Dockerfile b/docker/Dockerfile index 5fdbb505f..a00bbc460 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -47,11 +47,11 @@ RUN pip3 install --no-cache-dir \ # Install Sphinx-Needs RUN \ if [ -n "$NEEDS_VERSION" ] && [ "$NEEDS_VERSION" = "pre-release" ]; then \ - pip3 install --no-cache-dir "sphinx-needs[plotting] @ git+https://github.com/useblocks/sphinx-needs"; \ + pip3 install --no-cache-dir "sphinx-needs[plotting,schema] @ git+https://github.com/useblocks/sphinx-needs"; \ elif [ -n "$NEEDS_VERSION" ]; then \ - pip3 install --no-cache-dir "sphinx-needs[plotting] @ git+https://github.com/useblocks/sphinx-needs@$NEEDS_VERSION"; \ + pip3 install --no-cache-dir "sphinx-needs[plotting,schema] @ git+https://github.com/useblocks/sphinx-needs@$NEEDS_VERSION"; \ else \ - pip3 install --no-cache-dir sphinx-needs[plotting]; \ + pip3 install --no-cache-dir sphinx-needs[plotting,schema]; \ fi ## Clean up @@ -61,4 +61,4 @@ git WORKDIR /sphinxneeds # Start as user -USER ${DOCKER_USERNAME} \ No newline at end of file +USER ${DOCKER_USERNAME} diff --git a/docs/installation.rst b/docs/installation.rst index c8d81feda..4f5b8af5d 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -31,6 +31,12 @@ Using pip pip install sphinx-needs +To use schema validation features of sphinx-needs, you need to also install ``jsonschema``, which is available *via* the ``schema`` extra: + +.. code-block:: bash + + pip install sphinx-needs[schema] + If you wish to also use the plotting features of sphinx-needs (see :ref:`needbar` and :ref:`needpie`), you need to also install ``matplotlib``, which is available *via* the ``plotting`` extra: .. code-block:: bash diff --git a/tests/no_jsonschema_tests.py b/tests/no_jsonschema_tests.py new file mode 100644 index 000000000..851e28488 --- /dev/null +++ b/tests/no_jsonschema_tests.py @@ -0,0 +1,13 @@ +"""These tests should only be run in an environment without sphinx_needs installed.""" + +import pytest + + +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needsfile"}], + indirect=True, +) +def test_needsfile(test_app): + """Test the build fails correctly, if matplotlib is not installed.""" + test_app.build() From 9bbdcb79507274cdcdef56c5d4b4897c24de2045 Mon Sep 17 00:00:00 2001 From: sigma67 Date: Tue, 7 Oct 2025 08:18:43 +0200 Subject: [PATCH 3/4] fix test includes Signed-off-by: sigma67 --- .github/workflows/ci.yaml | 2 +- pyproject.toml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 594125139..0e46ff1a3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -133,7 +133,7 @@ jobs: working-directory: docs tests-no-jsonschema: - name: Test matplotlib uninstalled + name: Test jsonschema uninstalled runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/pyproject.toml b/pyproject.toml index dfe00c1c4..f790942b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,6 +45,7 @@ schema = [ test = [ "defusedxml~=0.7.1", "matplotlib>=3.3.0", + "jsonschema[format]>=3.2.0", "pytest~=8.0", "pytest-cov~=6.0", "syrupy~=4.0", From e56d55efbe042fb5b104461d4d865a28bb809d64 Mon Sep 17 00:00:00 2001 From: sigma67 Date: Tue, 7 Oct 2025 08:42:05 +0200 Subject: [PATCH 4/4] fix new test Signed-off-by: sigma67 --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0e46ff1a3..d1864602a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -150,7 +150,7 @@ jobs: python -m pip freeze - name: Run pytest run: | - python -m pytest -v tests/no_mpl_tests.py + python -m pytest -v tests/no_jsonschema_tests.py - name: Run HTML build # the docs should build without matplotlib (just issuing warnings) run: sphinx-build -b html . _build