diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml new file mode 100644 index 0000000..8387d74 --- /dev/null +++ b/.github/workflows/create-release.yml @@ -0,0 +1,90 @@ +name: Create release + +on: + push: + tags: + - "*.*.*" + workflow_dispatch: + +permissions: + contents: read + +jobs: + publish-pypi: + runs-on: ubuntu-latest + name: PyPI Release + environment: release + permissions: + id-token: write # for PyPI trusted publishing + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3 + cache: pip + cache-dependency-path: pyproject.toml + + - name: Install build dependencies (pypa/build, twine) + run: | + pip install -U pip + pip install build twine + + - name: Build distribution + run: python -m build + + - name: Mint PyPI API token + id: mint-token + uses: actions/github-script@v6 + with: + # language=JavaScript + script: | + // retrieve the ambient OIDC token + const oidc_request_token = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN; + const oidc_request_url = process.env.ACTIONS_ID_TOKEN_REQUEST_URL; + const oidc_resp = await fetch(`${oidc_request_url}&audience=pypi`, { + headers: {Authorization: `bearer ${oidc_request_token}`}, + }); + const oidc_token = (await oidc_resp.json()).value; + + // exchange the OIDC token for an API token + const mint_resp = await fetch('https://pypi.org/_/oidc/github/mint-token', { + method: 'post', + body: `{"token": "${oidc_token}"}` , + headers: {'Content-Type': 'application/json'}, + }); + const api_token = (await mint_resp.json()).token; + + // mask the newly minted API token, so that we don't accidentally leak it + core.setSecret(api_token) + core.setOutput('api-token', api_token) + + - name: Upload to PyPI + env: + TWINE_NON_INTERACTIVE: "true" + TWINE_USERNAME: "__token__" + TWINE_PASSWORD: "${{ steps.mint-token.outputs.api-token }}" + run: | + twine check dist/* + twine upload dist/* + + github-release: + runs-on: ubuntu-latest + name: GitHub release + environment: release + permissions: + contents: write # for softprops/action-gh-release to create GitHub release + steps: + - uses: actions/checkout@v3 + - name: Get release version + id: get_version + uses: actions/github-script@v6 + with: + script: core.setOutput('version', context.ref.replace("refs/tags/", "")) + + - name: Create GitHub release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + name: "sphinxcontrib-htmlhelp ${{ steps.get_version.outputs.version }}" + body: "Changelog: https://www.sphinx-doc.org/en/master/changes.html" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bb8b901..794d170 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,5 +1,8 @@ name: Test -on: [push, pull_request] +on: + push: + pull_request: + workflow_dispatch: permissions: contents: read @@ -8,42 +11,67 @@ concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true +env: + FORCE_COLOR: "1" + PYTHONDEVMODE: "1" # -X dev + PYTHONWARNDEFAULTENCODING: "1" # -X warn_default_encoding + jobs: tests: runs-on: ubuntu-latest strategy: matrix: python: - - "3.8" - "3.9" - "3.10" - "3.11" - "3.12-dev" + - "3.13-dev" fail-fast: false steps: - uses: actions/checkout@v3 - - name: Setup Python ${{ matrix.python }} + - name: Set up Python ${{ matrix.python }} uses: actions/setup-python@v4 + if: "!endsWith(matrix.python, '-dev')" + with: + python-version: ${{ matrix.python }} + - name: Set up Python ${{ matrix.python }} (deadsnakes) + uses: deadsnakes/action@v2.1.1 + if: "endsWith(matrix.python, '-dev')" with: python-version: ${{ matrix.python }} - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install --upgrade tox + python -m pip install .[test] - - name: Run tox + - name: Test with pytest + run: python -m pytest -vv --durations 25 + + test-latest-sphinx: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python }} + uses: actions/setup-python@v4 + with: + python-version: "3" + - name: Install dependencies run: | - python -V - tox -- -v --durations=25 + python -m pip install --upgrade pip + python -m pip install .[test] + python -m pip install "Sphinx @ git+https://github.com/sphinx-doc/sphinx" + + - name: Test with pytest + run: python -m pytest -vv --durations 25 lint: runs-on: ubuntu-latest strategy: matrix: - env: - - flake8 - - mypy + env: [flake8, mypy] steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/transifex.yml b/.github/workflows/transifex.yml new file mode 100644 index 0000000..1f3bab3 --- /dev/null +++ b/.github/workflows/transifex.yml @@ -0,0 +1,75 @@ +name: Synchronise translations + +on: + schedule: + # 22:38 GMT, every Sunday. Chosen to be a random time. + - cron: "38 22 * * SUN" + workflow_dispatch: + +permissions: + contents: read + +jobs: + push: + if: github.repository_owner == 'sphinx-doc' + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3 + - name: Install transifex client + run: | + mkdir -p /tmp/tx_cli && cd $_ + curl -o- https://raw.githubusercontent.com/transifex/cli/master/install.sh | bash + shell: bash + - name: Install dependencies + run: pip install --upgrade babel jinja2 + - name: Extract translations from source code + run: python utils/babel_runner.py extract + - name: Push translations to transifex.com + run: | + cd sphinxcontrib/htmlhelp/locales + /tmp/tx_cli/tx push --source --use-git-timestamps --workers 10 + env: + TX_TOKEN: ${{ secrets.TX_TOKEN }} + + pull: + permissions: + contents: write # for peter-evans/create-pull-request to create branch + pull-requests: write # for peter-evans/create-pull-request to create a PR + if: github.repository_owner == 'sphinx-doc' + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3 + - name: Install transifex client + run: | + mkdir -p /tmp/tx_cli && cd $_ + curl -o- https://raw.githubusercontent.com/transifex/cli/master/install.sh | bash + shell: bash + - name: Install dependencies + run: pip install --upgrade babel jinja2 + - name: Extract translations from source code + run: python utils/babel_runner.py extract + - name: Pull translations from transifex.com + run: | + cd sphinxcontrib/htmlhelp/locales + /tmp/tx_cli/tx pull --translations --all --force --use-git-timestamps --workers 10 + env: + TX_TOKEN: ${{ secrets.TX_TOKEN }} + - name: Compile message catalogs + run: python utils/babel_runner.py compile + - name: Create Pull Request + uses: peter-evans/create-pull-request@v4 + with: + commit-message: "[internationalisation] Update translations" + branch: bot/pull-translations + title: "[bot]: Update message catalogues" + labels: "internals:internationalisation" diff --git a/CHANGES b/CHANGES index cf0663d..5067364 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,19 @@ +Release 2.0.4 (2023-08-14) +========================== + +* Use ``os.PathLike`` over ``pathlib.Path`` + +Release 2.0.3 (2023-08-09) +========================== + +* Fix tests for Sphinx 7.1 and below + +Release 2.0.2 (2023-08-07) +========================== + +* Drop support for Python 3.8 +* Raise minimum required Sphinx version to 5.0 + Release 2.0.1 (2023-01-31) ========================== diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 7031593..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -include README.rst -include LICENSE -include CHANGES - -include tox.ini - -recursive-include sphinxcontrib/htmlhelp/templates * -recursive-include sphinxcontrib/htmlhelp/locales * -recursive-include tests * diff --git a/README.rst b/README.rst index ff3800c..0fd52f1 100644 --- a/README.rst +++ b/README.rst @@ -13,23 +13,6 @@ Install from PyPI:: pip install -U sphinxcontrib-htmlhelp -Release signatures -================== - -Releases are signed with following keys: - -* `498D6B9E `_ -* `5EBA0E07 `_ - -Testing -======= - -To run the tests with the interpreter available as ``python``, use:: - - tox - -Continuous testing runs on travis: https://travis-ci.org/sphinx-doc/sphinxcontrib-htmlhelp - Contributing ============ diff --git a/pyproject.toml b/pyproject.toml index 0434071..d704e4e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] -requires = ["setuptools>=64"] -build-backend = "setuptools.build_meta" +requires = ["flit_core>=3.7"] +build-backend = "flit_core.buildapi" # project metadata [project] @@ -13,7 +13,7 @@ urls.Download = "https://pypi.org/project/sphinxcontrib-htmlhelp/" urls.Homepage = "https://www.sphinx-doc.org/" urls."Issue tracker" = "https://github.com/sphinx-doc/sphinx/issues" license.text = "BSD-2-Clause" -requires-python = ">=3.8" +requires-python = ">=3.9" # Classifiers list: https://pypi.org/classifiers/ classifiers = [ @@ -27,11 +27,11 @@ classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "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", + "Programming Language :: Python :: 3.13", "Framework :: Sphinx", "Framework :: Sphinx :: Extension", "Topic :: Documentation", @@ -39,7 +39,9 @@ classifiers = [ "Topic :: Text Processing", "Topic :: Utilities", ] -dependencies = [] +dependencies = [ + "Sphinx>=5", +] dynamic = ["version"] [project.optional-dependencies] @@ -57,8 +59,17 @@ lint = [ name = "Georg Brandl" email = "georg@python.org" -[tool.setuptools.dynamic] -version.attr = "sphinxcontrib.htmlhelp.__version__" +[tool.flit.module] +name = "sphinxcontrib.htmlhelp" + +[tool.flit.sdist] +include = [ + "CHANGES", + "LICENSE", + # Tests + "tests/", + "tox.ini", +] [tool.mypy] ignore_missing_imports = true diff --git a/sphinxcontrib/htmlhelp/__init__.py b/sphinxcontrib/htmlhelp/__init__.py index fa6fe28..837e5ed 100644 --- a/sphinxcontrib/htmlhelp/__init__.py +++ b/sphinxcontrib/htmlhelp/__init__.py @@ -1,17 +1,11 @@ -""" - sphinxcontrib.htmlhelp - ~~~~~~~~~~~~~~~~~~~~~~ +"""Build HTML help support files.""" - Build HTML help support files. - - :copyright: Copyright 2007-2019 by the Sphinx team, see README. - :license: BSD, see LICENSE for details. -""" +from __future__ import annotations import html import os from os import path -from typing import Any, Dict, List, Set, Tuple, Type +from typing import Any from docutils import nodes from docutils.nodes import Element, Node, document @@ -28,15 +22,14 @@ from sphinx.util.nodes import NodeMatcher from sphinx.util.osutil import make_filename_from_project, relpath from sphinx.util.template import SphinxRenderer -from sphinx.writers.html import HTMLTranslator if sphinx.version_info[:2] >= (6, 1): from sphinx.util.display import progress_message else: from sphinx.util import progress_message # type: ignore[attr-defined,no-redef] -__version__ = '2.0.1' -__version_info__ = (2, 0, 1) +__version__ = '2.0.4' +__version_info__ = (2, 0, 4) logger = logging.getLogger(__name__) __ = get_translation(__name__, 'console') @@ -98,7 +91,7 @@ def chm_htmlescape(s: str, quote: bool = True) -> str: class ToCTreeVisitor(nodes.NodeVisitor): def __init__(self, document: document) -> None: super().__init__(document) - self.body = [] # type: List[str] + self.body: list[str] = [] self.depth = 0 def append(self, text: str) -> None: @@ -173,16 +166,11 @@ def init(self) -> None: if locale is not None: self.lcid, self.encoding = locale - @property - def default_translator_class(self) -> "Type[nodes.NodeVisitor]": # type: ignore - # Use HTML4 writer always - return HTMLTranslator - - def prepare_writing(self, docnames: Set[str]) -> None: + def prepare_writing(self, docnames: set[str]) -> None: super().prepare_writing(docnames) self.globalcontext['html5_doctype'] = False - def update_page_context(self, pagename: str, templatename: str, ctx: Dict, event_arg: str) -> None: # NOQA + def update_page_context(self, pagename: str, templatename: str, ctx: dict, event_arg: str) -> None: # NOQA ctx['encoding'] = self.encoding def handle_finish(self) -> None: @@ -199,7 +187,7 @@ def write_doc(self, docname: str, doctree: document) -> None: super().write_doc(docname, doctree) - def render(self, name: str, context: Dict) -> str: + def render(self, name: str, context: dict) -> str: template = SphinxRenderer(template_dir) return template.render(name, context) @@ -222,7 +210,7 @@ def copy_stopword_list(self) -> None: def build_project_file(self) -> None: """Create a project file (.hhp) on outdir.""" # scan project files - project_files = [] # type: List[str] + project_files: list[str] = [] for root, dirs, files in os.walk(self.outdir): dirs.sort() files.sort() @@ -267,14 +255,14 @@ def build_toc_file(self) -> None: } f.write(self.render('project.hhc', context)) - def build_hhx(self, outdir: str, outname: str) -> None: + def build_hhx(self, outdir: str | os.PathLike[str], outname: str) -> None: logger.info(__('writing index file...')) index = IndexEntries(self.env).create_index(self) filename = path.join(outdir, outname + '.hhk') with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f: f.write('