From 27dd3a7b5dd9fa19115fad26c11250c1c07e1b3d Mon Sep 17 00:00:00 2001 From: Remco de Boer <29308176+redeboer@users.noreply.github.com> Date: Sun, 3 Dec 2023 17:32:51 +0100 Subject: [PATCH] FEAT: convert `_relink_references.py` to module --- .cspell.json | 51 ++++++ .editorconfig | 20 +++ .github/release-drafter.yml | 37 ++++ .github/workflows/cd.yml | 20 +++ .github/workflows/ci.yml | 33 ++++ .github/workflows/clean-caches.yml | 22 +++ .github/workflows/pr-linting.yml | 33 ++++ .github/workflows/release-drafter.yml | 16 ++ .gitignore | 63 +++++++ .pre-commit-config.yaml | 109 ++++++++++++ .prettierignore | 1 + .taplo.toml | 12 ++ .vscode/extensions.json | 30 ++++ .vscode/launch.json | 20 +++ .vscode/settings.json | 62 +++++++ CONTRIBUTING.md | 8 + LICENSE | 29 +++ README.md | 52 ++++++ environment.yml | 10 ++ pyproject.toml | 243 ++++++++++++++++++++++++++ src/sphinx_api_relink/__init__.py | 120 +++++++++++++ src/sphinx_api_relink/py.typed | 0 22 files changed, 991 insertions(+) create mode 100644 .cspell.json create mode 100644 .editorconfig create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/cd.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/clean-caches.yml create mode 100644 .github/workflows/pr-linting.yml create mode 100644 .github/workflows/release-drafter.yml create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 .prettierignore create mode 100644 .taplo.toml create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 environment.yml create mode 100644 pyproject.toml create mode 100644 src/sphinx_api_relink/__init__.py create mode 100644 src/sphinx_api_relink/py.typed diff --git a/.cspell.json b/.cspell.json new file mode 100644 index 0000000..1ac1e52 --- /dev/null +++ b/.cspell.json @@ -0,0 +1,51 @@ +{ + "version": "0.2", + "enableFiletypes": [ + "git-commit", + "github-actions-workflow", + "julia", + "jupyter" + ], + "flagWords": [ + "analyse", + "colour", + "comparision", + "favour", + "flavour", + "hte", + "optimise", + "paramater", + "parmater", + "transision", + "transisions" + ], + "ignorePaths": [ + "**/.cspell.json", + ".editorconfig", + ".gitignore", + ".pre-commit-config.yaml", + ".prettierignore", + ".vscode/*", + "pyproject.toml" + ], + "language": "en-US", + "words": [ + "autoapi", + "autodoc", + "ComPWA", + "conda", + "mypy", + "PYTHONHASHSEED", + "SymPy" + ], + "ignoreWords": [ + "PyPI", + "commitlint", + "prereleased", + "refdomain", + "refspecific", + "reftarget", + "reftype", + "rtfd" + ] +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2737f6f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +root = true + +[*] +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ipynb] +indent_size = unset + +[*.{py,toml}] +indent_size = 4 + +[LICENSE] +indent_size = unset + +[setup.cfg] +indent_size = 4 diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..78a3e23 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,37 @@ +name-template: sphinx-api-relink $NEXT_PATCH_VERSION +tag-template: $NEXT_PATCH_VERSION + +references: + - main + - epic/* + +categories: + - title: ✨ New features + label: ✨ Feature + - title: ⚠️ Enhancements and optimizations + label: ⚙️ Enhancement + - title: ⚠️ API changes + label: ⚠️ Interface + - title: ⚠️ Changes that may affect behavior + label: ❗ Behavior + - title: 🐛 Bug fixes + label: 🐛 Bug + - title: 📝 Documentation + label: 📝 Docs + - title: 🔨 Maintenance + label: 🔨 Maintenance + - title: 🖱️ Developer Experience + label: 🖱️ DX + +change-template: "- $TITLE (#$NUMBER)" + +replacers: + - search: /([A-Z]+!?:\s*)(.*)/g + replace: $2 + +sort-direction: ascending + +template: | + $CHANGES + + _The full changelog as commits can be found [here](https://github.com/ComPWA/sphinx-api-relink/compare/$PREVIOUS_TAG...$NEXT_PATCH_VERSION)._ diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000..925b84d --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,20 @@ +name: CD + +on: + release: + types: + - prereleased + - released + +jobs: + milestone: + if: startsWith(github.ref, 'refs/tags') + uses: ComPWA/actions/.github/workflows/close-milestone.yml@v1 + pypi: + if: startsWith(github.ref, 'refs/tags') + secrets: inherit + uses: ComPWA/actions/.github/workflows/publish-to-pypi.yml@v1 + push: + if: startsWith(github.ref, 'refs/tags') && !github.event.release.prerelease + secrets: inherit + uses: ComPWA/actions/.github/workflows/push-to-version-branches.yml@v1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b1b457a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,33 @@ +name: CI + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + PYTHONHASHSEED: "0" + +on: + push: + branches: + - main + - epic/* + - "[0-9]+.[0-9]+.x" + pull_request: + branches: + - main + - epic/* + - "[0-9]+.[0-9]+.x" + workflow_dispatch: + inputs: + specific-pip-packages: + description: Run CI with specific pip packages + required: false + type: string + +jobs: + style: + if: inputs.specific-pip-packages == '' + secrets: + token: ${{ secrets.PAT }} + uses: ComPWA/actions/.github/workflows/pre-commit.yml@v1 diff --git a/.github/workflows/clean-caches.yml b/.github/workflows/clean-caches.yml new file mode 100644 index 0000000..a66c407 --- /dev/null +++ b/.github/workflows/clean-caches.yml @@ -0,0 +1,22 @@ +name: Clean caches + +on: + pull_request: + types: + - closed + workflow_dispatch: + inputs: + ref: + description: Clean caches for this branch name or ref + required: false + type: string + +jobs: + cleanup: + name: Remove caches + runs-on: ubuntu-22.04 + steps: + - uses: ComPWA/actions/clean-caches@v1 + with: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ inputs.ref }} diff --git a/.github/workflows/pr-linting.yml b/.github/workflows/pr-linting.yml new file mode 100644 index 0000000..cd3bb74 --- /dev/null +++ b/.github/workflows/pr-linting.yml @@ -0,0 +1,33 @@ +name: PR linting +on: + pull_request: + types: + - edited + - labeled + - opened + - reopened + - synchronize + - unlabeled + +jobs: + check-labels: + name: Check labels + runs-on: ubuntu-22.04 + steps: + - uses: docker://agilepathway/pull-request-label-checker:latest # cspell:ignore agilepathway + with: + any_of: >- + 🐛 Bug,✨ Feature,⚙️ Enhancement,⚠️ Interface,❗ Behavior,📝 Docs,🔨 Maintenance,🖱️ DX + none_of: Epic,💫 Good first issue + repo_token: ${{ secrets.GITHUB_TOKEN }} + + check-title: + name: Check title + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - run: npm install @compwa/commitlint-config + - name: Create commitlint config + run: | + echo "module.exports = {extends: ['@compwa/commitlint-config']}" > commitlint.config.js + - uses: JulienKode/pull-request-name-linter-action@v0.5.0 # cspell:ignore kode diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 0000000..d64f197 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,16 @@ +name: Release Drafter + +on: + push: + branches: + - main + - epic/* + workflow_dispatch: + +jobs: + update_release_draft: + runs-on: ubuntu-22.04 + steps: + - uses: release-drafter/release-drafter@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..03171f1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,63 @@ +/Jamroot + +# Output files +*.gv +*.json +*.npy +*.out +*.pdf +*.pickle +*.png +*.root +*.svg +*.v2 +*.xml +*.yaml +*.yml + +# Temporary files +.fuse_hidden* +.ipynb_checkpoints/ +__pycache__/ + +# Build files +*.egg-info/ +*build/ +.eggs/ +.fuse_* +dist/ +version.py + +# Temporary files +*.pyc +*condaenv.* +.coverage +.coverage.* +.ipynb_checkpoints/ +.mypy*/ +.pytest_cache/ +__pycache__/ +htmlcov/ +prof/ + +# Virtual environments +*venv/ +.tox/ +pyvenv*/ + +# Settings +.idea/ +**.code-workspace + +# Exceptions +!.clang-format +!.cspell.json +!.github/*.yml +!.github/*/*.yml +!.gitpod.yml +!.pre-commit-config.yaml +!.readthedocs.yml +!.vscode/*.json +!codecov.yml +!environment.yml +!pyrightconfig.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..f97da1a --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,109 @@ +ci: + autoupdate_commit_msg: "MAINT: autoupdate pre-commit hooks" + autoupdate_schedule: quarterly # already done by requirements-cron.yml + skip: + - mypy + - prettier + - pyright + - taplo + +repos: + - repo: meta + hooks: + - id: check-hooks-apply + - id: check-useless-excludes + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-ast + - id: check-case-conflict + - id: check-json + - id: check-merge-conflict + - id: check-toml + - id: check-vcs-permalinks + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + exclude: > + (?x)^( + .*\.bib| + .*\.svg| + \.cspell\.json + )$ + - id: mixed-line-ending + - id: trailing-whitespace + + - repo: https://github.com/psf/black-pre-commit-mirror + rev: 23.11.0 + hooks: + - id: black + + - repo: https://github.com/asottile/blacken-docs + rev: 1.16.0 + hooks: + - id: blacken-docs + + - repo: https://github.com/ComPWA/repo-maintenance + rev: 0.1.6 + hooks: + - id: check-dev-files + args: + - --no-gitpod + - --no-notebooks + - --no-prettierrc + - --repo-name=sphinx-api-relink + - --repo-title=sphinx-api-relink + + - repo: https://github.com/streetsidesoftware/cspell-cli + rev: v8.0.0 + hooks: + - id: cspell + + - repo: https://github.com/editorconfig-checker/editorconfig-checker.python + rev: 2.7.3 + hooks: + - id: editorconfig-checker + name: editorconfig + alias: ec + exclude: >- + (?x)^( + .*\.py + )$ + + - repo: local + hooks: + - id: mypy + name: mypy + entry: mypy + language: system + types: + - python + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v4.0.0-alpha.3 + hooks: + - id: prettier + + - repo: https://github.com/ComPWA/mirrors-pyright + rev: v1.1.338 + hooks: + - id: pyright + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.6 + hooks: + - id: ruff + args: [--fix] + + - repo: https://github.com/ComPWA/mirrors-taplo + rev: v0.8.1 + hooks: + - id: taplo + + - repo: https://github.com/pappasam/toml-sort + rev: v0.23.1 + hooks: + - id: toml-sort + args: + - --in-place diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..6b1d0bf --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +LICENSE diff --git a/.taplo.toml b/.taplo.toml new file mode 100644 index 0000000..0db1df7 --- /dev/null +++ b/.taplo.toml @@ -0,0 +1,12 @@ +[formatting] +align_comments = false +align_entries = false +allowed_blank_lines = 1 +array_auto_collapse = false +array_auto_expand = true +array_trailing_comma = true +column_width = 88 +compact_inline_tables = true +indent_string = " " +reorder_arrays = true +reorder_keys = true diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..59f88b7 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,30 @@ +{ + "recommendations": [ + "charliermarsh.ruff", + "christian-kohler.path-intellisense", + "eamodio.gitlens", + "editorconfig.editorconfig", + "esbenp.prettier-vscode", + "garaioag.garaio-vscode-unwanted-recommendations", + "github.vscode-github-actions", + "ms-python.black-formatter", + "ms-python.mypy-type-checker", + "ms-python.python", + "ms-python.vscode-pylance", + "redhat.vscode-yaml", + "ryanluker.vscode-coverage-gutters", + "stkb.rewrap", + "streetsidesoftware.code-spell-checker", + "tamasfe.even-better-toml", + "tyriar.sort-lines", + "yzhang.markdown-all-in-one" + ], + "unwantedRecommendations": [ + "bungcip.better-toml", + "davidanson.vscode-markdownlint", + "ms-python.flake8", + "ms-python.isort", + "ms-python.pylint", + "travisillig.vscode-json-stable-stringify" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..84ad26a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug current file", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal" + }, + { + "name": "Deep-debug current file", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "justMyCode": false + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..fce99d7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,62 @@ +{ + "[bibtex]": { + "editor.formatOnSave": false + }, + "[git-commit]": { + "editor.rulers": [72], + "rewrap.wrappingColumn": 72 + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[jsonc]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[markdown]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[pip-requirements]": { + "editor.wordWrap": "off" + }, + "[python]": { + "editor.codeActionsOnSave": { + "source.organizeImports": true + }, + "editor.defaultFormatter": "ms-python.black-formatter", + "editor.rulers": [88] + }, + "[yaml]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "black-formatter.importStrategy": "fromEnvironment", + "coverage-gutters.coverageFileNames": ["coverage.xml"], + "coverage-gutters.coverageReportFileName": "**/htmlcov/index.html", + "coverage-gutters.showGutterCoverage": false, + "coverage-gutters.showLineCoverage": true, + "cSpell.enabled": true, + "diffEditor.experimental.showMoves": true, + "editor.formatOnSave": true, + "files.watcherExclude": { + "**/*_cache/**": true, + "**/.eggs/**": true, + "**/.git/**": true, + "**/.tox/**": true + }, + "git.rebaseWhenSync": true, + "github-actions.workflows.pinned.refresh.enabled": true, + "github-actions.workflows.pinned.workflows": [".github/workflows/ci.yml"], + "mypy-type-checker.args": ["--config-file=${workspaceFolder}/pyproject.toml"], + "mypy-type-checker.importStrategy": "fromEnvironment", + "python.analysis.autoImportCompletions": false, + "python.analysis.typeCheckingMode": "strict", + "python.testing.unittestEnabled": false, + "rewrap.wrappingColumn": 88, + "ruff.enable": true, + "ruff.organizeImports": true, + "search.exclude": { + "**/tests/**/__init__.py": true, + "*/.pydocstyle": true, + "src/*/*/__init__.py": true, + "src/*/__init__.py": true + } +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..4998329 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,8 @@ +# How to contribute? + +[![Open in Visual Studio Code](https://img.shields.io/badge/vscode-open-blue?logo=visualstudiocode)](https://open.vscode.dev/ComPWA/sphinx-api-relink) +[![GitPod](https://img.shields.io/badge/gitpod-open-blue?logo=gitpod)](https://gitpod.io/#https://github.com/ComPWA/sphinx-api-relink) + +This package is part of the [ComPWA Organization](https://github.com/ComPWA). For more +information about how to contribute to the packages, go to +[compwa-org.rtfd.io/en/stable/develop.html](https://compwa-org.readthedocs.io/en/stable/develop.html)! diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..627f124 --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2021, Common Partial Wave Analysis +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f1ee04 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +# PDG role for Sphinx + +[![PyPI package](https://badge.fury.io/py/sphinx-api-relink.svg)](https://pypi.org/project/sphinx-api-relink) +[![Supported Python versions](https://img.shields.io/pypi/pyversions/sphinx-api-relink)](https://pypi.org/project/sphinx-api-relink) +[![BSD 3-Clause license](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) +[![Open in Visual Studio Code](https://img.shields.io/badge/vscode-open-blue?logo=visualstudiocode)](https://open.vscode.dev/ComPWA/sphinx-api-relink) +[![CI status](https://github.com/ComPWA/sphinx-api-relink/workflows/CI/badge.svg)](https://github.com/ComPWA/sphinx-api-relink/actions?query=branch%3Amain+workflow%3ACI) +[![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy.readthedocs.io) +[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/ComPWA/sphinx-api-relink/main.svg)](https://results.pre-commit.ci/latest/github/ComPWA/sphinx-api-relink/main) +[![Spelling checked](https://img.shields.io/badge/cspell-checked-brightgreen.svg)](https://github.com/streetsidesoftware/cspell/tree/master/packages/cspell) +[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) + +This package is a plugin for the [`sphinx.ext.autodoc`](https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html) extension. The [`autodoc_type_aliases`](https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc_type_aliases) configuration does not always work well when using postponed evaluation of annotations ([PEP 563](https://peps.python.org/pep-0563), i.e. `from __future__ import annotations`) or when importing through [`typing.TYPE_CHECKING`](https://docs.python.org/3/library/typing.html#typing.TYPE_CHECKING), because `sphinx.ext.autodoc` generates the API dynamically (not statically, as opposed to [`sphinx-autoapi`](https://github.com/readthedocs/sphinx-autoapi)). + +## Installation + +Just install through [PyPI](https://pypi.org) with `pip`: + +```bash +pip install sphinx-api-relink +``` + +Next, in your +[Sphinx configuration file](https://www.sphinx-doc.org/en/master/usage/configuration.html) +(`conf.py`), add `"sphinx_api_relink"` to your `extensions`: + +```python +extensions = [ + "sphinx_api_relink", +] +``` + +## Usage + +There are two ways to relink type hint references in your API. The first one, **`api_target_substitutions`**, should be used when the target is different than the type hint itself: + +```python +api_target_substitutions: dict[str, str | tuple[str, str]] = { + "sp.Expr": "sympy.core.expr.Expr", + "Protocol": ("obj", "typing.Protocol"), +} +``` + +The second, **`api_target_types`**, is useful when you want to redirect the reference **type**. This is for instance useful when Sphinx thinks the reference is a [`class`](https://www.sphinx-doc.org/en/master/usage/domains/python.html#role-py-class), but it should be an [`obj`](https://www.sphinx-doc.org/en/master/usage/domains/python.html#role-py-obj): + +```python +api_target_types: dict[str, str] = { + "RangeDefinition": "obj", +} +``` diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000..a27cd9c --- /dev/null +++ b/environment.yml @@ -0,0 +1,10 @@ +name: sphinx-api-relink +channels: + - defaults +dependencies: + - python==3.8.* + - pip + - pip: + - -e .[dev] +variables: + PRETTIER_LEGACY_CLI: "1" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..17633bd --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,243 @@ +[build-system] +build-backend = "setuptools.build_meta" +requires = [ + "setuptools>=61.2", + "setuptools_scm", +] + +[project] +authors = [{name = "Common Partial Wave Analysis", email = "compwa-admin@ep1.rub.de"}] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python", + "Topic :: Documentation", + "Topic :: Utilities", + "Typing :: Typed", +] +dependencies = [ + "Sphinx>=4.4", + "docutils", +] +description = "Relink type hints in your Sphinx API" +dynamic = ["version"] +keywords = [ + "autodoc", + "reference", + "relink", + "sphinx", +] +license = {text = "BSD 3-Clause License"} +maintainers = [{email = "compwa-admin@ep1.rub.de"}] +name = "sphinx-api-relink" +requires-python = ">=3.7" + +[project.optional-dependencies] +dev = ["sphinx-api-relink[sty]"] +format = ["black"] +lint = [ + "ruff", + "sphinx-api-relink[mypy]", +] +mypy = [ + "mypy", + "types-docutils", +] +sty = [ + "pre-commit >=1.4.0", + "sphinx-api-relink[format]", + "sphinx-api-relink[lint]", +] + +[project.readme] +content-type = "text/markdown" +file = "README.md" + +[project.urls] +Changelog = "https://github.com/ComPWA/sphinx-api-relink/releases" +Documentation = "https://github.com/ComPWA/sphinx-api-relink/blob/main/README.md" +Source = "https://github.com/ComPWA/sphinx-api-relink" +Tracker = "https://github.com/ComPWA/sphinx-api-relink/issues" + +[tool.setuptools] +include-package-data = true +license-files = ["LICENSE"] +package-dir = {"" = "src"} + +[tool.setuptools.package-data] +"*" = ["py.typed"] + +[tool.setuptools.packages.find] +namespaces = false +where = ["src"] + +[tool.setuptools_scm] +write_to = "src/version.py" + +[tool.black] +exclude = ''' +/( + .*\.egg-info + | .*build + | \.eggs + | \.git + | \.pytest_cache + | \.tox + | \.venv + | \.vscode + | dist +)/ +''' +include = '\.pyi?$' +preview = true +target-version = [ + "py310", + "py311", + "py312", + "py37", + "py38", + "py39", +] + +[tool.coverage.run] +branch = true +source = ["src"] + +[tool.mypy] +disallow_incomplete_defs = true +disallow_untyped_defs = true +files = [ + "**/*.py", + "**/*.pyi", +] +show_error_codes = true +warn_unused_configs = true + +[[tool.mypy.overrides]] +check_untyped_defs = true +disallow_incomplete_defs = false +disallow_untyped_defs = false +module = ["tests.*"] + +[[tool.mypy.overrides]] +ignore_errors = true +module = ["typings.*"] + +[[tool.mypy.overrides]] +ignore_missing_imports = true +module = ["pyquery.*"] + +[tool.pyright] +exclude = [ + "**/.git", + "**/.ipynb_checkpoints", + "**/.mypy_cache", + "**/.pytest_cache", + "**/.tox", + "**/__pycache__", + "**/_build", +] +reportGeneralTypeIssues = false +reportMissingImports = false +reportMissingParameterType = false +reportMissingTypeArgument = false +reportUnboundVariable = false +reportUnknownArgumentType = false +reportUnknownMemberType = false +reportUnknownParameterType = false +reportUnknownVariableType = false +reportUntypedFunctionDecorator = false +reportUnusedClass = true +reportUnusedFunction = true +reportUnusedImport = true +reportUnusedVariable = true +typeCheckingMode = "strict" + +[tool.ruff] +extend-select = [ + "A", + "B", + "BLE", + "C4", + "C90", + "D", + "EM", + "ERA", + "FA", + "I", + "ICN", + "INP", + "ISC", + "N", + "NPY", + "PGH", + "PIE", + "PL", + "Q", + "RET", + "RSE", + "RUF", + "S", + "SIM", + "T20", + "TCH", + "TID", + "TRY", + "UP", + "YTT", +] +ignore = [ + "D101", + "D102", + "D103", + "D105", + "D107", + "D203", + "D213", + "D407", + "D416", + "E501", + "SIM108", + "UP036", +] +show-fixes = true +src = ["src"] +target-version = "py37" +task-tags = ["cspell"] + +[tool.ruff.per-file-ignores] +"setup.py" = ["D100"] +"tests/*" = [ + "D", + "INP001", + "PGH001", + "PLR0913", + "PLR2004", + "S101", +] + +[tool.ruff.pydocstyle] +convention = "google" + +[tool.tomlsort] +all = false +ignore_case = true +in_place = true +sort_first = [ + "build-system", + "project", + "tool.setuptools", + "tool.setuptools_scm", +] +sort_table_keys = true +spaces_indent_inline_array = 4 +trailing_comma_inline_array = true diff --git a/src/sphinx_api_relink/__init__.py b/src/sphinx_api_relink/__init__.py new file mode 100644 index 0000000..bf18ae1 --- /dev/null +++ b/src/sphinx_api_relink/__init__.py @@ -0,0 +1,120 @@ +"""Link to PDG reviews and listings in Sphinx documentation.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +import sphinx.domains.python +from docutils import nodes +from sphinx.addnodes import pending_xref, pending_xref_condition +from sphinx.domains.python import parse_reftarget + +if TYPE_CHECKING: + from sphinx.application import Sphinx + from sphinx.environment import BuildEnvironment + + +def setup(app: Sphinx) -> dict[str, Any]: + app.add_config_value("api_target_substitutions", default={}, rebuild="env") + app.add_config_value("api_target_types", default={}, rebuild="env") + app.connect("config-inited", replace_type_to_xref) + return { + "parallel_read_safe": True, + "parallel_write_safe": True, + } + + +def replace_type_to_xref(app: Sphinx, _: BuildEnvironment) -> None: + target_substitutions = _get_target_substitutions(app) + ref_targets = { + k: v if isinstance(v, str) else v[1] for k, v in target_substitutions.items() + } + ref_types = _get_target_types(app) + ref_types.update({ + v[1]: v[0] for v in target_substitutions.values() if isinstance(v, tuple) + }) + + def _new_type_to_xref( + target: str, + env: BuildEnvironment | None = None, # type: ignore[assignment] + suppress_prefix: bool = False, + ) -> pending_xref: + reftype, target, title, refspecific = parse_reftarget(target, suppress_prefix) + target = ref_targets.get(target, target) + reftype = ref_types.get(target, reftype) + if env is None: + msg = "Environment cannot be None" + raise TypeError(msg) + return pending_xref( + "", + *_create_nodes(env, title), + refdomain="py", + reftype=reftype, + reftarget=target, + refspecific=refspecific, + **_get_env_kwargs(env), + ) + + sphinx.domains.python.type_to_xref = _new_type_to_xref + + +def _get_target_substitutions(app: Sphinx) -> dict[str, str | tuple[str, str]]: + config_key = "api_target_substitutions" + target_substitutions = getattr(app.config, config_key, {}) + if not isinstance(target_substitutions, dict): + msg = f"{config_key} must be a dict, got {type(target_substitutions).__name__}" + raise TypeError(msg) + for k, v in target_substitutions.items(): + if not isinstance(k, str): + msg = f"{config_key} keys must be str, got {type(k).__name__}" + raise TypeError(msg) + if not isinstance(v, (str, tuple)): + msg = ( + f"{config_key} values must be str or a tuple, got" + f" {type(v).__name__} for key {k!r}" + ) + raise TypeError(msg) + if isinstance(v, tuple) and len(v) != 2: # noqa: PLR2004 + msg = ( + f"If dict values of {config_key} are a tuple, they must have length 2," + f" but got {len(v)} for key {k!r}" + ) + raise TypeError(msg) + return target_substitutions + + +def _get_target_types(app: Sphinx) -> dict[str, str]: + config_key = "api_target_types" + target_types = getattr(app.config, config_key, {}) + if not isinstance(target_types, dict): + msg = f"{config_key} must be a dict, got {type(target_types).__name__}" + raise TypeError(msg) + for k, v in target_types.items(): + if not isinstance(k, str): + msg = f"{config_key} keys must be str, got {type(k).__name__} for key {k!r}" + raise TypeError(msg) + if not isinstance(v, str): + msg = ( + f"{config_key} values must be str, got {type(v).__name__} for key {k!r}" + ) + raise TypeError(msg) + return target_types + + +def _get_env_kwargs(env: BuildEnvironment) -> dict: + if env: + return { + "py:module": env.ref_context.get("py:module"), + "py:class": env.ref_context.get("py:class"), + } + return {} + + +def _create_nodes(env: BuildEnvironment, title: str) -> list[nodes.Node]: + short_name = title.split(".")[-1] + if env.config.python_use_unqualified_type_names: + return [ + pending_xref_condition("", short_name, condition="resolved"), + pending_xref_condition("", title, condition="*"), + ] + return [nodes.Text(short_name)] diff --git a/src/sphinx_api_relink/py.typed b/src/sphinx_api_relink/py.typed new file mode 100644 index 0000000..e69de29