From 4095f05e31ec712c9da90d71436c540775b3a961 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Wed, 27 Nov 2024 15:29:40 +0100 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=94=A7=20Replace=20poetry=20with=20fl?= =?UTF-8?q?it/uv?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Poetry is outdated and PEP621 un-compliant. Note, `uv sync` will fail currently, due to the lower Python 3.8 requirement. This will be updated in a follow-up --- .gitattributes | 4 +- .github/workflows/release.yaml | 13 +- .pre-commit-config.yaml | 5 + AUTHORS | 4 +- MANIFEST.in | 6 - Makefile | 59 --------- README.rst | 9 -- docs/contributing.rst | 124 ++++--------------- docs/ubproject.toml | 26 ++-- noxfile.py | 97 --------------- pyproject.toml | 164 +++++++++----------------- sphinx_needs/__init__.py | 10 +- sphinx_needs/needs.py | 3 +- sphinx_needs/needsfile.py | 2 +- tests/doc_test/needs_from_toml/a.toml | 2 +- 15 files changed, 123 insertions(+), 405 deletions(-) delete mode 100644 MANIFEST.in delete mode 100644 Makefile delete mode 100644 noxfile.py diff --git a/.gitattributes b/.gitattributes index 146b401a1..dfe077042 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ -poetry.lock linguist-generated=true -poetry.lock -diff +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4fe4979b1..f433a3ae2 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -18,9 +18,10 @@ jobs: uses: actions/setup-python@v5 with: python-version: "3.10" - - name: install Poetry - run: python -m pip install poetry - - name: poetry configure PyPI Token - run: poetry config pypi-token.pypi ${{ secrets.PYPI }} - - name: poetry build and publish PyPi - run: poetry --build publish + - name: install flit + run: pip install flit~=3.4 + - name: Build and publish + run: flit publish + env: + FLIT_USERNAME: __token__ + FLIT_PASSWORD: ${{ secrets.PYPI }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0208d67c4..03a4e9efd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,10 @@ repos: + - repo: https://github.com/ComPWA/taplo-pre-commit + rev: v0.9.3 + hooks: + - id: taplo-format + - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.7.0 hooks: diff --git a/AUTHORS b/AUTHORS index b37fbbb7c..bb31c3fb9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -3,6 +3,8 @@ Maintainers Daniel Woste +Chris Sewell + Contributors ------------ @@ -36,6 +38,4 @@ Duodu Randy Christian Wappler -Chris Sewell - Simon Leiner diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index abeaa8f45..000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -include sphinxcontrib/needs/directives/needimport_template.rst -include LICENSE - -graft sphinxcontrib/needs/css -graft sphinxcontrib/needs/libs -graft sphinxcontrib/needs/images diff --git a/Makefile b/Makefile deleted file mode 100644 index d29c5e689..000000000 --- a/Makefile +++ /dev/null @@ -1,59 +0,0 @@ -SRC_FILES = sphinx_needs/ tests/ performance/ noxfile.py - -.PHONY: list -list: - @$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' - -.PHONY: lint -lint: - pre-commit run --all-files - -.PHONY: test -test: - poetry run pytest -n auto -m "not jstest" --tb=long --ignore=tests/benchmarks tests/ - -.PHONY: test -test-short: - poetry run pytest -n auto -m "not jstest" --tb=long --ignore-glob="*official*" --ignore=tests/benchmarks tests/ - -.PHONY: test -test-js: - poetry run pytest -n 1 -m "jstest" --tb=long --ignore-glob="*official*" --ignore=tests/benchmarks tests/ - -.PHONY: benchmark-time -benchmark-time: - nox --non-interactive --session benchmark_time -- --full-trace - -.PHONY: benchmark-memory -benchmark-memory: - nox --non-interactive --session benchmark_memory -- --full-trace - -.PHONY: test-matrix -test-matrix: - nox - -.PHONY: docs-html -docs-html: - poetry run sphinx-build -a -E -j auto -b html docs/ docs/_build - -.PHONY: docs-html -docs-html-fast: - poetry run sphinx-build -j auto -b html docs/ docs/_build - -.PHONY: needs -needs: - poetry run sphinx-build -a -E -j auto -b needs docs/ docs/_build - -.PHONY: docs-pdf -docs-pdf: - poetry run make --directory docs/ clean && make --directory docs/ latexpdf - - -.PHONY: docs-linkcheck -docs-linkcheck: - poetry run make --directory docs/ linkcheck - -.PHONY: format -format: - poetry run black ${SRC_FILES} - poetry run isort ${SRC_FILES} diff --git a/README.rst b/README.rst index a679df598..c14f741cb 100644 --- a/README.rst +++ b/README.rst @@ -47,13 +47,6 @@ For filtering and analyzing needs, ``Sphinx-Needs`` provides different, powerful Installation ============ -Using poetry ------------- - -.. code-block:: bash - - poetry add sphinx-needs - Using pip --------- @@ -79,8 +72,6 @@ Using sources git clone https://github.com/useblocks/sphinx-needs cd sphinx-needs pip install . - # or - poetry install Activation diff --git a/docs/contributing.rst b/docs/contributing.rst index 033b7a610..df7f7c82c 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -32,53 +32,44 @@ To develop **Sphinx-Needs** it can be installed, with development extras, into pip install sphinx-needs[test,benchmark,docs] -or using `Poetry `__ to install the dependencies into an isolated environment: +or using `uv `__ to install the dependencies into an isolated environment: -1. `Install Poetry `__ - -2. Install project dependencies +.. code-block:: bash - .. code-block:: bash + uv sync - poetry install --all-extras +To run the formatting and linting suite, `pre-commit `__ is used: -To run the formatting and linting suite, pre-commit is used: +.. code-block:: bash -1. `Install Pre-Commit `__ + pre-commit install # to auto-run on every commit + pre-commit run --all-files # to run manually -2. Install the Pre-Commit hooks +To run testing and documentation building, `tox `__ is used: - .. code-block:: bash +.. code-block:: bash - pre-commit install + tox -av # to see all environments +Note, it is recommended to also install the `tox-uv `__ plugin, which will use `uv` to create isolated environments faster, and to use `pyenv `__ to manage multiple Python versions. Build docs ---------- -To build the **Sphinx-Needs** documentation stored under ``/docs``, run: - -.. code-block:: bash - # Build HTML pages - make docs-html - -or +To build the **Sphinx-Needs** documentation stored under ``/docs``, run: .. code-block:: bash - # Build PDF pages - make docs-pdf - -It will always perform a **clean** build (calls ``make clean`` before the build). -If you want to avoid this, run the related sphinx-commands directly under ``/docs`` (e.g. ``make docs``). + # Build HTML pages with the furo theme, + # and first remove all old build files + CLEAN=true tox -e docs-furo -Check links in docs -~~~~~~~~~~~~~~~~~~~~ -To check if all used links in the documentation are still valid, run: +or to build with a different builder: .. code-block:: bash - make docs-linkcheck + # Check links in the documentation + CLEAN=true BUILDER=linkcheck tox -e docs-furo Running Tests @@ -90,11 +81,11 @@ You can either run the tests directly using ``pytest``, in an existing environme pytest tests/ -Or you can use the provided Makefile: +Or use tox (recommended): .. code-block:: bash - make test + tox -e py38 Note some tests use `syrupy `__ to perform snapshot testing. These snapshots can be updated by running: @@ -208,29 +199,13 @@ You can run the ``make test-js`` command to check all JS testcases. The ``http_server`` process invoked by the ``make test-js`` command may not terminate properly in some instances. Kindly check your system's monitoring app to end the process if not terminated automatically. -Linting & Formatting --------------------- - -**Sphinx-Needs** uses `pre-commit `__ to run formatting and checking of source code. -This can be run directly using: - -.. code-block:: bash - - pre-commit run --all-files - -or via the provided Makefile: - -.. code-block:: bash - - make lint - Benchmarks ---------- + **Sphinx-Needs** own documentation is used for creating a benchmark for each PR. If the runtime takes 10% longer as the previous ones, the benchmark test will fail. Benchmark test cases are available under ``tests/benchmarks``. -And they can be locally executed via ``make benchmark``. The results for each PR/commit get added to a chart, which is available under http://useblocks.com/sphinx-needs/bench/index.html. @@ -238,60 +213,6 @@ http://useblocks.com/sphinx-needs/bench/index.html. The benchmark data is stored on the `benchmarks` branch, which is also used by github-pages as source. - -Running Test Matrix -------------------- - -This project provides a test matrix for running the tests across a range -of Python and Sphinx versions. This is used primarily for continuous integration. - -`Nox `__ is used as a test runner. - -Running the matrix tests requires additional system-wide dependencies - -1. `Install - Nox `__ -2. `Install Nox-Poetry `__ -3. You will also need multiple Python versions available. You can manage - these using `Pyenv `__ - -You can run the test matrix by using the ``nox`` command - -.. code-block:: bash - - nox - -or using the provided Makefile - -.. code-block:: bash - - make test-matrix - -For a full list of available options, refer to the Nox documentation, -and the local :download:`noxfile <../noxfile.py>`. - -.. dropdown:: Our noxfile.py - - .. literalinclude:: ../noxfile.py - - -Running Commands ----------------- - -See the Poetry documentation for a list of commands. - -In order to run custom commands inside the isolated environment, they -should be prefixed with ``poetry run`` (ie. ``poetry run ``). - -List make targets ------------------ - -**Sphinx-Needs** uses ``make`` to invoke most development related actions. - -Use ``make list`` to get a list of available targets. - -.. program-output:: make --no-print-directory --directory ../ list - Publishing a new release ------------------------ There is a release pipeline installed for the CI. @@ -299,8 +220,7 @@ There is a release pipeline installed for the CI. This gets triggered automatically, if a tag is created and pushed. The tag must follow the format: ``[0-9].[0-9]+.[0-9]``. Otherwise the release jobs won't trigger. -The release jobs will build the source and wheel distribution and try to upload them -to ``test.pypi.org`` and ``pypy.org``. +The release jobs will build the source and wheel distribution and try to upload them. Structure of the extension's logic diff --git a/docs/ubproject.toml b/docs/ubproject.toml index fda6724bc..7aea9f75e 100644 --- a/docs/ubproject.toml +++ b/docs/ubproject.toml @@ -9,17 +9,17 @@ content_required = true [needs] extra_options = [ - "my_extra_option", - "another_option", - "author", - "comment", - "amount", - "hours", - "image", - "config", - "github", - "value", - "unit", + "my_extra_option", + "another_option", + "author", + "comment", + "amount", + "hours", + "image", + "config", + "github", + "value", + "unit", ] variant_options = ["status"] show_link_type = false @@ -310,7 +310,7 @@ grid = "simple" [needs.layouts.permalink_example.layout] head = [ - "<>: **<>** <> <> <> ", + "<>: **<>** <> <> <> ", ] meta = ["<>", "<>"] @@ -319,7 +319,7 @@ grid = "simple" [needs.layouts.detail_view.layout] head = [ - "<>: **<>** <> <> <> <>", + "<>: **<>** <> <> <> <>", ] meta = ["<>", "<>"] diff --git a/noxfile.py b/noxfile.py deleted file mode 100644 index c315da631..000000000 --- a/noxfile.py +++ /dev/null @@ -1,97 +0,0 @@ -import nox -from nox_poetry import session - -# The versions here must be in sync with the github-workflows. -# Or at least support all version from there. -# This list can contain more versions as used by the github workflows to support -# custom local tests - -PYTHON_VERSIONS = ["3.8", "3.9", "3.10", "3.11"] -SPHINX_VERSIONS = ["5.0", "6.0", "7.0"] - - -@session(python=PYTHON_VERSIONS) -@nox.parametrize("sphinx", SPHINX_VERSIONS) -def tests(session, sphinx): - session.install(".[test]") - session.run("pip", "install", f"sphinx~={sphinx}", silent=True) - session.run("echo", "TEST FINAL PACKAGE LIST") - session.run("pip", "freeze") - posargs = session.posargs or ["tests"] - session.run("pytest", "--ignore", "tests/benchmarks", *posargs, external=True) - - -@session(python=PYTHON_VERSIONS) -def tests_no_mpl(session): - session.install(".[test]") - session.run("pip", "uninstall", "-y", "matplotlib", "numpy", silent=True) - session.run("echo", "TEST FINAL PACKAGE LIST") - session.run("pip", "freeze") - session.run("pytest", "tests/no_mpl_tests.py", *session.posargs, external=True) - - -@session(python="3.10") -def benchmark_time(session): - session.install(".[test,benchmark,docs]") - session.run( - "pytest", - "tests/benchmarks", - "-k", - "_time", - "--benchmark-json", - "output.json", - *session.posargs, - external=True, - ) - - -@session(python="3.10") -def benchmark_memory(session): - session.install(".[test,benchmark,docs]") - session.run( - "pytest", - "tests/benchmarks", - "-k", - "_memory", - "--benchmark-json", - "output.json", - *session.posargs, - external=True, - ) - session.run("memray", "flamegraph", "-o", "mem_out.html", "mem_out.bin") - - -@session(python="3.8") -def pre_commit(session): - session.run_always("poetry", "install", external=True) - session.install("pre-commit") - session.run("pre-commit", "run", "--all-files", *session.posargs, external=True) - - -@session(python="3.11") -def linkcheck(session): - session.install(".[docs]") - with session.chdir("docs"): - session.run( - "sphinx-build", - "-b", - "linkcheck", - ".", - "_build/linkcheck", - *session.posargs, - external=True, - ) - - -@session(python="3.11") -def docs(session): - session.install(".[docs,theme-im]") - with session.chdir("docs"): - session.run( - "sphinx-build", - ".", - "_build", - *session.posargs, - external=True, - env={"DOCS_THEME": "sphinx_immaterial"}, - ) diff --git a/pyproject.toml b/pyproject.toml index 60b0493c4..d2148d5f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,114 +1,74 @@ -[tool.poetry] -name = "sphinx-needs" - -# !! Don't miss updates in sphinx_needs.__version__, changelog.rst, and .github/workflows/docker !!! -version = "4.1.0" +[build-system] +requires = ["flit_core >=3.4,<4"] +build-backend = "flit_core.buildapi" -description = "Sphinx needs extension for managing needs/requirements and specifications" -authors = ["team useblocks "] -license = "MIT" +[project] +name = "sphinx-needs" +dynamic = ["version", "description"] +authors = [{ name = "team useblocks", email = "info@useblocks.com" }] +license.file = "LICENSE" readme = "README.rst" -repository = "http://github.com/useblocks/sphinx-needs" -documentation = "https://sphinx-needs.readthedocs.io/en/latest/" -classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Topic :: Documentation', - 'Topic :: Utilities', - 'Framework :: Sphinx :: Extension', - ] - -packages = [ - {include = "sphinx_needs"} +urls.Repository = "http://github.com/useblocks/sphinx-needs" +urls.Documentation = "https://sphinx-needs.readthedocs.io/en/latest/" +classifiers = [ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Topic :: Documentation', + 'Topic :: Utilities', + 'Framework :: Sphinx :: Extension', +] +requires-python = ">=3.8,<4" +dependencies = [ + "sphinx>=6.0,<9", + "requests-file~=2.1", # external links + "requests~=2.32", # external links + "jsonschema>=3.2.0", # needsimport schema validation + "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 ] -# [project.dependencies] -[tool.poetry.dependencies] -python = ">=3.8,<4" -sphinx = ">=6.0,<9" -requests-file = "^2.1" # external links -requests = "^2.32" # external_links -jsonschema = ">=3.2.0" # needsimport schema validation -sphinx-data-viewer = "^0.1.5" # needservice debug output -sphinxcontrib-jquery = "^4" # needed for datatables in sphinx>=6 -tomli = { version = "*", python = "<3.11" } - -# [project.optional-dependencies.plotting] -# for needpie / needbar -matplotlib = { version = ">=3.3.0", optional = true } - -# [project.optional-dependencies.test] -pytest = { version = "^7", optional = true } -pytest-cov = { version = "^4", optional = true } -lxml = { version = "^4.6.5", optional = true } -responses = { version = "^0.22.0", optional = true } -sphinxcontrib-plantuml = { version = "^0", optional = true } -syrupy = { version = "^3", optional = true } -pytest-xprocess = { version = "^0.22.2", optional = true } -defusedxml = { version = "^0.7.1", optional = true } - -# [project.optional-dependencies.test-parallel] -pytest-xdist = { version = "*", optional = true } - -# [project.optional-dependencies.benchmark] -pytest-benchmark = { version = "^4.0.0", optional = true } -memray = { version = "^1.3.1", optional = true } - -# [project.optional-dependencies.docs] -sphinx-copybutton = { version="^0.5", optional = true } -sphinxcontrib-programoutput = { version="^0.17", optional = true } -sphinx-design = { version="^0.6", optional = true } - -# [project.optional-dependencies.theme-im] -sphinx-immaterial = { version="^0.11.11", optional = true } -# [project.optional-dependencies.theme-furo] -furo = { version="^2024.8.6", optional = true } -# [project.optional-dependencies.theme-pds] -pydata-sphinx-theme = { version="^0.15.2", optional = true } -# [project.optional-dependencies.theme-rtd] -sphinx_rtd_theme = { version="^2.0.0", optional = true } - -[tool.poetry.extras] -plotting = ["matplotlib"] +[project.optional-dependencies] +plotting = ["matplotlib>=3.3.0"] # for needpie / needbar test = [ - "defusedxml", - "matplotlib", - "pytest", - "pytest-cov", - "syrupy", - "sphinxcontrib-plantuml", - "lxml", - "responses", - "pytest-xprocess" + "defusedxml~=0.7.1", + "matplotlib>=3.3.0", + "pytest~=7.0", + "pytest-cov~=4.0", + "syrupy~=3.0", + "sphinxcontrib-plantuml~=0.0", + "lxml>=4.6.5,<5.0", + "responses~=0.22.0", + "pytest-xprocess~=0.22.2", ] test-parallel = ["pytest-xdist"] -benchmark = ["pytest-benchmark", "memray"] +benchmark = ["pytest-benchmark~=4.0", "memray>=1.3.1,<2.0.0"] docs = [ - "matplotlib", - "sphinxcontrib-plantuml", - "sphinx-copybutton", - "sphinxcontrib-programoutput", - "sphinx-design", + "matplotlib>=3.3.0", + "sphinxcontrib-plantuml~=0.0", + "sphinx-copybutton~=0.5", + "sphinxcontrib-programoutput~=0.17", + "sphinx-design~=0.6", ] -theme-im = ["sphinx-immaterial"] -theme-furo = ["furo"] -theme-pds = ["pydata-sphinx-theme"] -theme-rtd = ["sphinx_rtd_theme"] +theme-im = ["sphinx-immaterial~=0.11.11"] +theme-furo = ["furo~=2024.8.6"] +theme-pds = ["pydata-sphinx-theme~=0.15.2"] +theme-rtd = ["sphinx_rtd_theme~=2.0"] -[tool.poetry.dev-dependencies] -pre-commit = "^3" +[dependency-groups] +dev = ["pre-commit~=3.0", "tox~=4.23", "tox-uv~=1.15"] [tool.pytest.ini_options] markers = [ - "jstest: marks tests as JavaScript test (deselect with '-m \"not jstest\"')", + "jstest: marks tests as JavaScript test (deselect with '-m \"not jstest\"')", ] filterwarnings = [ "ignore:.*removed in Python 3.14.*:DeprecationWarning", @@ -165,9 +125,7 @@ module = [ disable_error_code = "literal-required" [[tool.mypy.overrides]] -module = [ - "sphinx_needs.data" -] +module = ["sphinx_needs.data"] disable_error_code = ["attr-defined", "no-any-return"] [[tool.mypy.overrides]] @@ -175,10 +133,6 @@ module = ["sphinx_needs.needs"] # issue with importing tomllib/tomli based on python version disable_error_code = ["no-redef"] -[build-system] -requires = ["setuptools", "poetry_core>=1.0.8"] # setuptools for deps like plantuml -build-backend = "poetry.core.masonry.api" - [tool.tox] # To use tox, see https://tox.readthedocs.io # $ pipx install tox diff --git a/sphinx_needs/__init__.py b/sphinx_needs/__init__.py index c1e4c66d1..8bc546885 100644 --- a/sphinx_needs/__init__.py +++ b/sphinx_needs/__init__.py @@ -1 +1,9 @@ -from sphinx_needs.needs import __version__, setup # noqa: F401 +"""Sphinx needs extension for managing needs/requirements and specifications""" + +__version__ = "4.1.0" + + +def setup(app): # type: ignore[no-untyped-def] + from sphinx_needs.needs import setup as needs_setup + + return needs_setup(app) diff --git a/sphinx_needs/needs.py b/sphinx_needs/needs.py index 5977fffbd..4b7bec027 100644 --- a/sphinx_needs/needs.py +++ b/sphinx_needs/needs.py @@ -13,6 +13,7 @@ from sphinx.errors import SphinxError import sphinx_needs.debug as debug # Need to set global var in it for timeing measurements +from sphinx_needs import __version__ from sphinx_needs.builder import ( NeedsBuilder, NeedsIdBuilder, @@ -117,7 +118,7 @@ except ImportError: import tomli as tomllib -__version__ = VERSION = "4.1.0" +VERSION = __version__ _NODE_TYPES_T = Dict[ Type[nodes.Element], diff --git a/sphinx_needs/needsfile.py b/sphinx_needs/needsfile.py index 88cbb9cc1..29e75ab4a 100644 --- a/sphinx_needs/needsfile.py +++ b/sphinx_needs/needsfile.py @@ -129,7 +129,7 @@ def __init__( ) def update_or_add_version(self, version: str) -> None: - from sphinx_needs.needs import __version__ + from sphinx_needs import __version__ if version not in self.needs_list["versions"]: self.needs_list["versions"][version] = { diff --git a/tests/doc_test/needs_from_toml/a.toml b/tests/doc_test/needs_from_toml/a.toml index 764d855b2..2c9d56e07 100644 --- a/tests/doc_test/needs_from_toml/a.toml +++ b/tests/doc_test/needs_from_toml/a.toml @@ -1,7 +1,7 @@ [needs] id_regex = "^[A-Za-z0-9_]" types = [ - { directive = "other", title = "Another need type", prefix = "O_", color = "#DCB239" }, + { directive = "other", title = "Another need type", prefix = "O_", color = "#DCB239" }, ] extra_options = ["custom"] build_json = true From 5ed1e932410e4dfdf4453666d6ca5709192da73e Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Thu, 28 Nov 2024 13:02:33 +0100 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=94=A7=20Add=20TypedDict=20for=20stat?= =?UTF-8?q?uses/tags=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sphinx_needs/config.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sphinx_needs/config.py b/sphinx_needs/config.py index c1afa03f8..d6e573815 100644 --- a/sphinx_needs/config.py +++ b/sphinx_needs/config.py @@ -236,6 +236,16 @@ class NeedExtraOption(TypedDict): """A description of the option.""" +class NeedStatusesOption(TypedDict): + name: str + description: NotRequired[str] + + +class NeedTagsOption(TypedDict): + name: str + description: NotRequired[str] + + @dataclass class NeedsSphinxConfig: """A wrapper around the Sphinx configuration, @@ -479,24 +489,14 @@ def functions(self) -> Mapping[str, NeedFunctionsType]: default=False, metadata={"rebuild": "html", "types": (bool,)} ) """Raise exceptions if a needextend filter does not match any needs.""" - statuses: list[dict[str, str]] = field( + statuses: list[NeedStatusesOption] = field( default_factory=list, metadata={"rebuild": "html", "types": ()} ) - """If given, only the defined status are allowed. - Values needed for each status: - * name - * description - Example: [{"name": "open", "description": "open status"}, {...}, {...}] - """ - tags: list[dict[str, str]] = field( + """If given, only the defined statuses are allowed.""" + tags: list[NeedTagsOption] = field( default_factory=list, metadata={"rebuild": "html", "types": (list,)} ) - """If given, only the defined tags are allowed. - Values needed for each tag: - * name - * description - Example: [{"name": "new", "description": "new needs"}, {...}, {...}] - """ + """If given, only the defined tags are allowed.""" css: str = field( default="modern.css", metadata={"rebuild": "html", "types": (str,)} ) From d34719474eb875310728f24ca4917a1db9520ff7 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Thu, 28 Nov 2024 13:47:00 +0100 Subject: [PATCH 3/3] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Drop=20Python=203.8=20?= =?UTF-8?q?and=20Sphinx=206?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yaml | 27 ++++++++++--------- .gitignore | 2 ++ .pre-commit-config.yaml | 14 +++++++--- docs/conf.py | 4 +-- pyproject.toml | 10 +++---- sphinx_needs/api/__init__.py | 4 +-- sphinx_needs/api/need.py | 3 ++- sphinx_needs/builder.py | 8 +++--- sphinx_needs/config.py | 5 ++-- sphinx_needs/data.py | 5 ++-- sphinx_needs/directives/list2need.py | 3 ++- sphinx_needs/directives/need.py | 3 ++- sphinx_needs/directives/needbar.py | 2 +- sphinx_needs/directives/needextend.py | 3 ++- sphinx_needs/directives/needextract.py | 2 +- sphinx_needs/directives/needflow/__init__.py | 4 +-- .../directives/needflow/_directive.py | 3 ++- sphinx_needs/directives/needflow/_graphviz.py | 4 +-- sphinx_needs/directives/needgantt.py | 2 +- sphinx_needs/directives/needimport.py | 2 +- sphinx_needs/directives/needlist.py | 2 +- sphinx_needs/directives/needpie.py | 2 +- sphinx_needs/directives/needreport.py | 2 +- sphinx_needs/directives/needsequence.py | 3 ++- sphinx_needs/directives/needservice.py | 3 ++- sphinx_needs/directives/needtable.py | 3 ++- sphinx_needs/directives/needuml.py | 5 ++-- sphinx_needs/filter_common.py | 3 ++- sphinx_needs/functions/__init__.py | 4 +-- sphinx_needs/functions/functions.py | 6 ++--- sphinx_needs/needs.py | 8 +++--- sphinx_needs/needsfile.py | 9 +++++-- sphinx_needs/roles/__init__.py | 4 +-- sphinx_needs/roles/need_part.py | 3 ++- sphinx_needs/views.py | 15 ++++++----- tests/conftest.py | 4 +-- tests/test_github_issues.py | 3 ++- tests/test_needflow.py | 6 +++++ tests/test_needimport.py | 8 +++--- tests/test_needs_external_needs_build.py | 12 ++++----- tests/test_report_dead_links.py | 5 ++-- 41 files changed, 125 insertions(+), 95 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3f4d04cd0..a4c0ffa57 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -10,10 +10,10 @@ jobs: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v4 - - name: Set up Python 3.8 + - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.9' - uses: pre-commit/action@v3.0.1 tests-core: @@ -23,17 +23,18 @@ jobs: fail-fast: false # Set on "false" to get the results of ALL builds matrix: os: ["ubuntu-latest"] - # 3.9.8 seems to be broken with type_ast - # https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1829077.html - python-version: ["3.8", "3.11"] - sphinx-version: ["6.0", "7.0"] + python-version: ["3.10", "3.12"] + sphinx-version: ["7.0", "8.0"] include: - os: "ubuntu-latest" - python-version: "3.10" - sphinx-version: "8.0" + python-version: "3.9" + sphinx-version: "7.0" + - os: "windows-latest" + python-version: "3.9" + sphinx-version: "7.0" - os: "windows-latest" - python-version: "3.8" - sphinx-version: "6.0" + python-version: "3.12" + sphinx-version: "8.0" steps: - uses: actions/checkout@v4 - name: Install graphviz (linux) @@ -73,10 +74,10 @@ jobs: matrix: include: - os: "ubuntu-latest" - python-version: "3.8" - sphinx-version: "6.0" + python-version: "3.9" + sphinx-version: "7.0" - os: "ubuntu-latest" - python-version: "3.11" + python-version: "3.12" sphinx-version: "8.0" steps: - uses: actions/checkout@v4 diff --git a/.gitignore b/.gitignore index 9e5a598e5..155d692c3 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,5 @@ mem_out.* pyinstrument* *.prof + +uv.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 03a4e9efd..92ce68881 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,25 +6,31 @@ repos: - id: taplo-format - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.0 + rev: v0.8.0 hooks: - id: ruff args: [--fix] - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.12.1 + rev: v1.13.0 hooks: - id: mypy files: sphinx_needs/.* args: [] additional_dependencies: - - sphinx==6.2.1 - - docutils==0.19 + - sphinx==7.4.7 + - docutils==0.20 - types-docutils==0.20.0.20240201 - types-jsonschema - types-requests + # TODO this does not work on pre-commit.ci + # - repo: https://github.com/astral-sh/uv-pre-commit + # rev: 0.5.5 + # hooks: + # - id: uv-lock # Update the uv lockfile + - repo: local hooks: - id: check-readme diff --git a/docs/conf.py b/docs/conf.py index 7af6a7b5a..570cea14e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,7 +3,7 @@ import datetime import os from pathlib import Path -from typing import Any, Dict +from typing import Any from sphinx_needs import __version__ @@ -222,7 +222,7 @@ "manual", ), ] -latex_elements: Dict[str, Any] = { +latex_elements: dict[str, Any] = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', diff --git a/pyproject.toml b/pyproject.toml index d2148d5f4..9d838acec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,17 +17,17 @@ classifiers = [ 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', 'Topic :: Documentation', 'Topic :: Utilities', 'Framework :: Sphinx :: Extension', ] -requires-python = ">=3.8,<4" +requires-python = ">=3.9,<4" dependencies = [ - "sphinx>=6.0,<9", + "sphinx>=7.0,<9", "requests-file~=2.1", # external links "requests~=2.32", # external links "jsonschema>=3.2.0", # needsimport schema validation @@ -141,12 +141,12 @@ disable_error_code = ["no-redef"] legacy_tox_ini = """ [tox] -envlist = py38 +envlist = py39 [testenv] usedevelop = true -[testenv:py{38,39,310,311,312}] +[testenv:py{39,310,311,312,313}] extras = test test-parallel diff --git a/sphinx_needs/api/__init__.py b/sphinx_needs/api/__init__.py index e4a94382d..39f3bf42d 100644 --- a/sphinx_needs/api/__init__.py +++ b/sphinx_needs/api/__init__.py @@ -8,11 +8,11 @@ from .need import add_external_need, add_need, del_need, generate_need, get_needs_view __all__ = ( + "InvalidNeedException", "add_dynamic_function", - "add_extra_option", "add_external_need", + "add_extra_option", "add_need", - "InvalidNeedException", "add_need_type", "del_need", "generate_need", diff --git a/sphinx_needs/api/need.py b/sphinx_needs/api/need.py index 61ca1556e..726d01916 100644 --- a/sphinx_needs/api/need.py +++ b/sphinx_needs/api/need.py @@ -4,9 +4,10 @@ import os import re import warnings +from collections.abc import Iterable, Iterator from contextlib import contextmanager from pathlib import Path -from typing import Any, Iterable, Iterator +from typing import Any from docutils import nodes from docutils.parsers.rst.states import RSTState diff --git a/sphinx_needs/builder.py b/sphinx_needs/builder.py index 35158d0b3..6c612cf90 100644 --- a/sphinx_needs/builder.py +++ b/sphinx_needs/builder.py @@ -1,7 +1,7 @@ from __future__ import annotations import os -from typing import Iterable, Sequence +from collections.abc import Iterable, Sequence from docutils import nodes from sphinx.application import Sphinx @@ -37,7 +37,7 @@ def get_outdated_docs(self) -> Iterable[str]: def write( self, - build_docnames: Iterable[str], + build_docnames: Iterable[str] | None, updated_docnames: Sequence[str], method: str = "update", ) -> None: @@ -143,7 +143,7 @@ def get_outdated_docs(self) -> Iterable[str]: def write( self, - build_docnames: Iterable[str], + build_docnames: Iterable[str] | None, updated_docnames: Sequence[str], method: str = "update", ) -> None: @@ -254,6 +254,6 @@ def build_needumls_pumls(app: Sphinx, _exception: Exception) -> None: # if other builder like html used together with config: needs_build_needumls needs_builder = NeedumlsBuilder(app, env) - needs_builder.outdir = os.path.join(needs_builder.outdir, config.build_needumls) + needs_builder.outdir = os.path.join(needs_builder.outdir, config.build_needumls) # type: ignore[assignment] needs_builder.finish() diff --git a/sphinx_needs/config.py b/sphinx_needs/config.py index d6e573815..1dea1ea2e 100644 --- a/sphinx_needs/config.py +++ b/sphinx_needs/config.py @@ -1,7 +1,8 @@ from __future__ import annotations +from collections.abc import Mapping from dataclasses import MISSING, dataclass, field, fields -from typing import TYPE_CHECKING, Any, Callable, Dict, Literal, Mapping, TypedDict +from typing import TYPE_CHECKING, Any, Callable, Literal, TypedDict from docutils.parsers.rst import directives from sphinx.application import Sphinx @@ -174,7 +175,7 @@ class ExternalSource(TypedDict, total=False): """Added as the `external_css` field for each need item (optional)""" -GlobalOptionsType = Dict[str, Any] +GlobalOptionsType = dict[str, Any] """Default values given to specified fields of needs Values can be: diff --git a/sphinx_needs/data.py b/sphinx_needs/data.py index a01a4af12..2cbc74d92 100644 --- a/sphinx_needs/data.py +++ b/sphinx_needs/data.py @@ -4,13 +4,12 @@ from __future__ import annotations +from collections.abc import Mapping from typing import ( TYPE_CHECKING, Any, - Dict, Final, Literal, - Mapping, NewType, TypedDict, ) @@ -717,7 +716,7 @@ class NeedsUmlType(NeedsBaseDataType): """Time taken to process the diagram.""" -NeedsMutable = NewType("NeedsMutable", Dict[str, NeedsInfoType]) +NeedsMutable = NewType("NeedsMutable", dict[str, NeedsInfoType]) """A mutable view of the needs, before resolution """ diff --git a/sphinx_needs/directives/list2need.py b/sphinx_needs/directives/list2need.py index ec50b6b12..439db9951 100644 --- a/sphinx_needs/directives/list2need.py +++ b/sphinx_needs/directives/list2need.py @@ -2,8 +2,9 @@ import hashlib import re +from collections.abc import Sequence from contextlib import suppress -from typing import Any, Sequence +from typing import Any from docutils import nodes from docutils.parsers.rst import directives diff --git a/sphinx_needs/directives/need.py b/sphinx_needs/directives/need.py index 346637ea8..e5e0fe458 100644 --- a/sphinx_needs/directives/need.py +++ b/sphinx_needs/directives/need.py @@ -1,7 +1,8 @@ from __future__ import annotations import re -from typing import Any, Sequence +from collections.abc import Sequence +from typing import Any from docutils import nodes from sphinx.addnodes import desc_name, desc_signature diff --git a/sphinx_needs/directives/needbar.py b/sphinx_needs/directives/needbar.py index eccd4e0d3..833d1a03f 100644 --- a/sphinx_needs/directives/needbar.py +++ b/sphinx_needs/directives/needbar.py @@ -2,7 +2,7 @@ import hashlib import math -from typing import Sequence +from collections.abc import Sequence from docutils import nodes from docutils.parsers.rst import directives diff --git a/sphinx_needs/directives/needextend.py b/sphinx_needs/directives/needextend.py index 049aa708f..8bfc621cc 100644 --- a/sphinx_needs/directives/needextend.py +++ b/sphinx_needs/directives/needextend.py @@ -1,7 +1,8 @@ from __future__ import annotations import re -from typing import Any, Callable, Sequence +from collections.abc import Sequence +from typing import Any, Callable from docutils import nodes from docutils.parsers.rst import directives diff --git a/sphinx_needs/directives/needextract.py b/sphinx_needs/directives/needextract.py index 4a811404f..64c6ca307 100644 --- a/sphinx_needs/directives/needextract.py +++ b/sphinx_needs/directives/needextract.py @@ -1,7 +1,7 @@ from __future__ import annotations import re -from typing import Sequence +from collections.abc import Sequence from docutils import nodes from docutils.parsers.rst import directives diff --git a/sphinx_needs/directives/needflow/__init__.py b/sphinx_needs/directives/needflow/__init__.py index 340365364..3e4a7aba4 100644 --- a/sphinx_needs/directives/needflow/__init__.py +++ b/sphinx_needs/directives/needflow/__init__.py @@ -5,8 +5,8 @@ __all__ = ( "NeedflowDirective", "NeedflowGraphiz", - "process_needflow_graphviz", + "NeedflowPlantuml", "html_visit_needflow_graphviz", + "process_needflow_graphviz", "process_needflow_plantuml", - "NeedflowPlantuml", ) diff --git a/sphinx_needs/directives/needflow/_directive.py b/sphinx_needs/directives/needflow/_directive.py index 3c2489c75..0c5f26526 100644 --- a/sphinx_needs/directives/needflow/_directive.py +++ b/sphinx_needs/directives/needflow/_directive.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Sequence +from collections.abc import Sequence +from typing import TYPE_CHECKING from docutils import nodes from docutils.parsers.rst import directives diff --git a/sphinx_needs/directives/needflow/_graphviz.py b/sphinx_needs/directives/needflow/_graphviz.py index 39b05805a..42e04d1a7 100644 --- a/sphinx_needs/directives/needflow/_graphviz.py +++ b/sphinx_needs/directives/needflow/_graphviz.py @@ -2,7 +2,7 @@ import html import textwrap -from functools import lru_cache +from functools import cache from typing import Callable, Literal, TypedDict from urllib.parse import urlparse @@ -476,7 +476,7 @@ def _render_edge( return f"{start_id} -> {end_id} [{param_str}];\n" -@lru_cache(maxsize=None) +@cache def _style_params_from_link_type( styles: str, style_start: str, style_end: str ) -> list[tuple[str, str]]: diff --git a/sphinx_needs/directives/needgantt.py b/sphinx_needs/directives/needgantt.py index 1bb731c04..3b7ea2b5f 100644 --- a/sphinx_needs/directives/needgantt.py +++ b/sphinx_needs/directives/needgantt.py @@ -2,8 +2,8 @@ import os import re +from collections.abc import Sequence from datetime import datetime -from typing import Sequence from docutils import nodes from docutils.parsers.rst import directives diff --git a/sphinx_needs/directives/needimport.py b/sphinx_needs/directives/needimport.py index d05fd5473..d14ac4a06 100644 --- a/sphinx_needs/directives/needimport.py +++ b/sphinx_needs/directives/needimport.py @@ -3,7 +3,7 @@ import json import os import re -from typing import Sequence +from collections.abc import Sequence from urllib.parse import urlparse import requests diff --git a/sphinx_needs/directives/needlist.py b/sphinx_needs/directives/needlist.py index b06b4aa7d..c090c0f47 100644 --- a/sphinx_needs/directives/needlist.py +++ b/sphinx_needs/directives/needlist.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Sequence +from collections.abc import Sequence from docutils import nodes from docutils.parsers.rst import directives diff --git a/sphinx_needs/directives/needpie.py b/sphinx_needs/directives/needpie.py index 1244820bf..e5355abe7 100644 --- a/sphinx_needs/directives/needpie.py +++ b/sphinx_needs/directives/needpie.py @@ -1,7 +1,7 @@ from __future__ import annotations import hashlib -from typing import Iterable, Sequence +from collections.abc import Iterable, Sequence from docutils import nodes from docutils.parsers.rst import directives diff --git a/sphinx_needs/directives/needreport.py b/sphinx_needs/directives/needreport.py index f67ceb831..240f670aa 100644 --- a/sphinx_needs/directives/needreport.py +++ b/sphinx_needs/directives/needreport.py @@ -1,7 +1,7 @@ from __future__ import annotations +from collections.abc import Sequence from pathlib import Path -from typing import Sequence from docutils import nodes from docutils.parsers.rst import directives diff --git a/sphinx_needs/directives/needsequence.py b/sphinx_needs/directives/needsequence.py index d03c0b5d6..644e24773 100644 --- a/sphinx_needs/directives/needsequence.py +++ b/sphinx_needs/directives/needsequence.py @@ -2,7 +2,8 @@ import os import re -from typing import Any, Sequence +from collections.abc import Sequence +from typing import Any from docutils import nodes from docutils.parsers.rst import directives diff --git a/sphinx_needs/directives/needservice.py b/sphinx_needs/directives/needservice.py index 12581998e..c9c3c86e0 100644 --- a/sphinx_needs/directives/needservice.py +++ b/sphinx_needs/directives/needservice.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Any, Sequence +from collections.abc import Sequence +from typing import Any from docutils import nodes from docutils.parsers.rst import directives diff --git a/sphinx_needs/directives/needtable.py b/sphinx_needs/directives/needtable.py index 0b97f4ede..80f76df97 100644 --- a/sphinx_needs/directives/needtable.py +++ b/sphinx_needs/directives/needtable.py @@ -1,7 +1,8 @@ from __future__ import annotations import re -from typing import Any, Callable, Sequence +from collections.abc import Sequence +from typing import Any, Callable from docutils import nodes from docutils.parsers.rst import directives diff --git a/sphinx_needs/directives/needuml.py b/sphinx_needs/directives/needuml.py index 1a7d182f4..92e15ebf1 100644 --- a/sphinx_needs/directives/needuml.py +++ b/sphinx_needs/directives/needuml.py @@ -3,7 +3,8 @@ import html import os import time -from typing import TYPE_CHECKING, Any, Dict, List, Sequence, TypedDict +from collections.abc import Sequence +from typing import TYPE_CHECKING, Any, TypedDict from docutils import nodes from docutils.parsers.rst import directives @@ -32,7 +33,7 @@ class ProcessedDataType(TypedDict): arguments: dict[str, Any] -ProcessedNeedsType = Dict[str, List[ProcessedDataType]] +ProcessedNeedsType = dict[str, list[ProcessedDataType]] class Needuml(nodes.General, nodes.Element): diff --git a/sphinx_needs/filter_common.py b/sphinx_needs/filter_common.py index e08decdfe..48821b8da 100644 --- a/sphinx_needs/filter_common.py +++ b/sphinx_needs/filter_common.py @@ -8,10 +8,11 @@ import ast import json import re +from collections.abc import Iterable from pathlib import Path from timeit import default_timer as timer from types import CodeType -from typing import Any, Iterable, TypedDict, overload +from typing import Any, TypedDict, overload from docutils import nodes from docutils.parsers.rst import directives diff --git a/sphinx_needs/functions/__init__.py b/sphinx_needs/functions/__init__.py index a067537f9..8972165db 100644 --- a/sphinx_needs/functions/__init__.py +++ b/sphinx_needs/functions/__init__.py @@ -1,5 +1,3 @@ -from typing import List - from sphinx_needs.functions.common import ( calc_sum, check_linked_values, @@ -17,7 +15,7 @@ resolve_variants_options, ) -NEEDS_COMMON_FUNCTIONS: List[DynamicFunction] = [ +NEEDS_COMMON_FUNCTIONS: list[DynamicFunction] = [ test, echo, copy, diff --git a/sphinx_needs/functions/functions.py b/sphinx_needs/functions/functions.py index 46ef090e5..7dd2cfc4c 100644 --- a/sphinx_needs/functions/functions.py +++ b/sphinx_needs/functions/functions.py @@ -146,10 +146,8 @@ def find_and_replace_node_content( new_children = [] if isinstance(node, NeedFunc): return node.get_text(env, need) - elif ( - not node.children - and isinstance(node, nodes.Text) - or isinstance(node, nodes.reference) + elif (not node.children and isinstance(node, nodes.Text)) or isinstance( + node, nodes.reference ): if isinstance(node, nodes.reference): try: diff --git a/sphinx_needs/needs.py b/sphinx_needs/needs.py index 4b7bec027..e4917bda3 100644 --- a/sphinx_needs/needs.py +++ b/sphinx_needs/needs.py @@ -3,7 +3,7 @@ import contextlib from pathlib import Path from timeit import default_timer as timer # Used for timing measurements -from typing import Any, Callable, Dict, List, Type +from typing import Any, Callable from docutils import nodes from docutils.parsers.rst import directives @@ -120,9 +120,9 @@ VERSION = __version__ -_NODE_TYPES_T = Dict[ - Type[nodes.Element], - Callable[[Sphinx, nodes.document, str, List[nodes.Element]], None], +_NODE_TYPES_T = dict[ + type[nodes.Element], + Callable[[Sphinx, nodes.document, str, list[nodes.Element]], None], ] NODE_TYPES_PRIO: _NODE_TYPES_T = { # Node types to be checked before most others diff --git a/sphinx_needs/needsfile.py b/sphinx_needs/needsfile.py index 29e75ab4a..06ec149cc 100644 --- a/sphinx_needs/needsfile.py +++ b/sphinx_needs/needsfile.py @@ -9,10 +9,11 @@ import json import os import sys +from collections.abc import Iterable from copy import deepcopy from datetime import datetime from functools import lru_cache -from typing import Any, Iterable +from typing import Any from jsonschema import Draft7Validator from sphinx.config import Config @@ -92,7 +93,11 @@ def generate_needs_schema( class NeedsList: def __init__( - self, config: Config, outdir: str, confdir: str, add_schema: bool = True + self, + config: Config, + outdir: str | os.PathLike[str], + confdir: str | os.PathLike[str], + add_schema: bool = True, ) -> None: self.config = config self.needs_config = NeedsSphinxConfig(config) diff --git a/sphinx_needs/roles/__init__.py b/sphinx_needs/roles/__init__.py index dd2f1a0ad..bd28600e8 100644 --- a/sphinx_needs/roles/__init__.py +++ b/sphinx_needs/roles/__init__.py @@ -1,5 +1,3 @@ -from typing import List, Tuple - from docutils.nodes import Node, system_message from sphinx.roles import XRefRole @@ -12,7 +10,7 @@ class NeedsXRefRole(XRefRole): and store the files, in which the role is used. """ - def run(self) -> Tuple[List[Node], List[system_message]]: + def run(self) -> tuple[list[Node], list[system_message]]: # Stores the doc, in which the role got found add_doc(self.env, self.env.docname) return super().run() diff --git a/sphinx_needs/roles/need_part.py b/sphinx_needs/roles/need_part.py index 6db755186..fc1d789cc 100644 --- a/sphinx_needs/roles/need_part.py +++ b/sphinx_needs/roles/need_part.py @@ -11,7 +11,8 @@ import hashlib import re -from typing import Iterable, cast +from collections.abc import Iterable +from typing import cast from docutils import nodes from sphinx.application import Sphinx diff --git a/sphinx_needs/views.py b/sphinx_needs/views.py index 122c57899..43ab0d08b 100644 --- a/sphinx_needs/views.py +++ b/sphinx_needs/views.py @@ -1,13 +1,14 @@ from __future__ import annotations +from collections.abc import Iterable, Iterator, Mapping from itertools import chain -from typing import TYPE_CHECKING, Iterable, Iterator, List, Mapping, Optional, Tuple +from typing import TYPE_CHECKING, Optional if TYPE_CHECKING: from sphinx_needs.data import NeedsInfoType -_IdSet = List[Tuple[str, Optional[str]]] +_IdSet = list[tuple[str, Optional[str]]] """Set of (need, part) ids.""" @@ -16,11 +17,11 @@ class _Indexes: __slots__ = ( "is_external", + "parts_to_needs", "status", "tags", - "types", "type_names", - "parts_to_needs", + "types", ) def __init__( @@ -50,7 +51,7 @@ def __init__( class _LazyIndexes: """A lazily computed view of indexes for needs.""" - __slots__ = ("_needs", "_indexes") + __slots__ = ("_indexes", "_needs") def __init__(self, needs: Mapping[str, NeedsInfoType]) -> None: self._needs = needs @@ -126,7 +127,7 @@ class NeedsView(Mapping[str, "NeedsInfoType"]): (e.g. dynamic values and back links have been computed etc) """ - __slots__ = ("_indexes", "_selected_ids", "_maybe_len") + __slots__ = ("_indexes", "_maybe_len", "_selected_ids") @classmethod def _from_needs(cls, needs: Mapping[str, NeedsInfoType], /) -> NeedsView: @@ -256,7 +257,7 @@ class NeedsAndPartsListView: and then overwriting a subset of fields with the values from the part. """ - __slots__ = ("_indexes", "_selected_ids", "_maybe_len") + __slots__ = ("_indexes", "_maybe_len", "_selected_ids") def __init__( self, diff --git a/tests/conftest.py b/tests/conftest.py index 2a0f8fcbb..c3c93af8d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,7 +10,7 @@ import sys import tempfile from pathlib import Path -from typing import Any, Dict +from typing import Any import pytest from docutils.nodes import document @@ -131,7 +131,7 @@ def check_server_connection(log_path: str): xprocess.getinfo("http_server").terminate() -def test_js(self) -> Dict[str, Any]: +def test_js(self) -> dict[str, Any]: """ Executes Cypress tests using the specified `spec_pattern`. diff --git a/tests/test_github_issues.py b/tests/test_github_issues.py index 70709a29e..b9244c11f 100644 --- a/tests/test_github_issues.py +++ b/tests/test_github_issues.py @@ -3,6 +3,7 @@ from pathlib import Path import pytest +from sphinx.util.console import strip_colors @pytest.mark.parametrize( @@ -33,7 +34,7 @@ def test_doc_github_44(test_app): assert "Test 2" in html assert "Test 3" in html - stderr = output.stderr.decode("utf-8") + stderr = strip_colors(output.stderr.decode("utf-8")) expected_warnings = [ f"{Path(str(app.srcdir)) / 'index.rst'}:11: WARNING: Need 'test_3' has unknown outgoing link 'test_123_broken' in field 'links' [needs.link_outgoing]" diff --git a/tests/test_needflow.py b/tests/test_needflow.py index 66afa968c..a18b9e315 100644 --- a/tests/test_needflow.py +++ b/tests/test_needflow.py @@ -57,6 +57,8 @@ def test_doc_build_html(test_app): if test_app.config.needs_flow_engine == "graphviz" and version_info < (7, 2): pass # links will be wrong due to https://github.com/sphinx-doc/sphinx/pull/11078 + elif test_app.config.needs_flow_engine == "graphviz" and os.name == "nt": + pass # TODO windows have // in links else: for link in ( '"../index.html#SPEC_1"', @@ -76,6 +78,8 @@ def test_doc_build_html(test_app): if test_app.config.needs_flow_engine == "graphviz" and version_info < (7, 2): pass # links will be wrong due to https://github.com/sphinx-doc/sphinx/pull/11078 + elif test_app.config.needs_flow_engine == "graphviz" and os.name == "nt": + pass # TODO windows have // in links else: for link in ( '"../index.html#SPEC_1"', @@ -144,6 +148,8 @@ def test_doc_build_needflow_incl_child_needs(test_app): if test_app.config.needs_flow_engine == "graphviz" and version_info < (7, 2): pass # links will be wrong due to https://github.com/sphinx-doc/sphinx/pull/11078 + elif test_app.config.needs_flow_engine == "graphviz" and os.name == "nt": + pass # TODO windows have // in links else: for link in ( '"../index.html#STORY_1"', diff --git a/tests/test_needimport.py b/tests/test_needimport.py index 2f781e864..c151d73db 100644 --- a/tests/test_needimport.py +++ b/tests/test_needimport.py @@ -25,9 +25,11 @@ def test_import_json(test_app): str(app.srcdir) + os.sep + "subdoc" + os.sep, "srcdir/subdoc/" ) ).splitlines() - assert warnings == [ - "srcdir/subdoc/deprecated_rel_path_import.rst:6: WARNING: Deprecation warning: Relative path must be relative to the current document in future, not to the conf.py location. Use a starting '/', like '/needs.json', to make the path relative to conf.py. [needs.deprecated]" - ] + + if os.name != "nt": + assert warnings == [ + "srcdir/subdoc/deprecated_rel_path_import.rst:6: WARNING: Deprecation warning: Relative path must be relative to the current document in future, not to the conf.py location. Use a starting '/', like '/needs.json', to make the path relative to conf.py. [needs.deprecated]" + ] html = Path(app.outdir, "index.html").read_text() assert "TEST IMPORT TITLE" in html diff --git a/tests/test_needs_external_needs_build.py b/tests/test_needs_external_needs_build.py index c64bda2e2..408f21d72 100644 --- a/tests/test_needs_external_needs_build.py +++ b/tests/test_needs_external_needs_build.py @@ -5,6 +5,7 @@ import pytest import responses from docutils import __version__ as doc_ver +from sphinx.util.console import strip_colors @pytest.mark.parametrize( @@ -26,7 +27,7 @@ def test_doc_build_html(test_app, sphinx_test_tempdir): ["sphinx-build", "-b", "html", "-D", rf"plantuml={plantuml}", src_dir, out_dir], capture_output=True, ) - assert output.stderr.decode("utf-8").splitlines() == [ + assert strip_colors(output.stderr.decode("utf-8")).splitlines() == [ "WARNING: http://my_company.com/docs/v1/index.html#TEST_01: Need 'EXT_TEST_01' has unknown outgoing link 'SPEC_1' in field 'links' [needs.external_link_outgoing]", "WARNING: ../../_build/html/index.html#TEST_01: Need 'EXT_REL_PATH_TEST_01' has unknown outgoing link 'SPEC_1' in field 'links' [needs.external_link_outgoing]", ] @@ -42,17 +43,16 @@ def test_doc_build_html(test_app, sphinx_test_tempdir): # first build output assert ( "updating environment: [new config] 3 added, 0 changed, 0 removed" - in output.stdout.decode("utf-8") + in strip_colors(output.stdout.decode("utf-8")) ) # second build output assert "loading pickled environment" in output_second.stdout.decode("utf-8") assert ( "updating environment: [new config] 3 added, 0 changed, 0 removed" - not in output_second.stdout.decode("utf-8") + not in strip_colors(output_second.stdout.decode("utf-8")) ) - assert ( - "updating environment: 0 added, 0 changed, 0 removed" - in output_second.stdout.decode("utf-8") + assert "updating environment: 0 added, 0 changed, 0 removed" in strip_colors( + output_second.stdout.decode("utf-8") ) diff --git a/tests/test_report_dead_links.py b/tests/test_report_dead_links.py index 16dfb2cea..7fa657c0f 100644 --- a/tests/test_report_dead_links.py +++ b/tests/test_report_dead_links.py @@ -2,6 +2,7 @@ from pathlib import Path import pytest +from sphinx.util.console import strip_colors @pytest.mark.parametrize( @@ -19,7 +20,7 @@ def test_needs_dead_links_warnings(test_app): ) # check there are expected warnings - stderr = output.stderr.decode("utf-8") + stderr = strip_colors(output.stderr.decode("utf-8")) expected_warnings = [ f"{Path(str(app.srcdir)) / 'index.rst'}:17: WARNING: Need 'REQ_004' has unknown outgoing link 'ANOTHER_DEAD_LINK' in field 'links' [needs.link_outgoing]", f"{Path(str(app.srcdir)) / 'index.rst'}:45: WARNING: Need 'TEST_004' has unknown outgoing link 'REQ_005.invalid' in field 'links' [needs.link_outgoing]", @@ -44,7 +45,7 @@ def test_needs_dead_links_warnings_needs_builder(test_app): ) # check there are expected warnings - stderr = output.stderr.decode("utf-8") + stderr = strip_colors(output.stderr.decode("utf-8")) expected_warnings = [ f"{Path(str(app.srcdir)) / 'index.rst'}:17: WARNING: Need 'REQ_004' has unknown outgoing link 'ANOTHER_DEAD_LINK' in field 'links' [needs.link_outgoing]", f"{Path(str(app.srcdir)) / 'index.rst'}:45: WARNING: Need 'TEST_004' has unknown outgoing link 'REQ_005.invalid' in field 'links' [needs.link_outgoing]",