diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 5af0a95..0000000 --- a/.flake8 +++ /dev/null @@ -1,4 +0,0 @@ -[flake8] -max-line-length = 95 -ignore = E116,E241,E251 -exclude = .git,.tox,.venv diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..47a31bc --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml new file mode 100644 index 0000000..c6d3a57 --- /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@v4 + - name: Set up Python + uses: actions/setup-python@v5 + 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@v7 + 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@v4 + - name: Get release version + id: get_version + uses: actions/github-script@v7 + with: + script: core.setOutput('version', context.ref.replace("refs/tags/", "")) + + - name: Create GitHub release + uses: softprops/action-gh-release@v2 + if: startsWith(github.ref, 'refs/tags/') + with: + name: "sphinxcontrib-htmlhelp ${{ steps.get_version.outputs.version }}" + body: "Changelog: https://github.com/sphinx-doc/sphinxcontrib-htmlhelp/blob/master/CHANGES.rst" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bb8b901..e337f0f 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,47 +11,74 @@ 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.12" + - "3.13-dev" fail-fast: false steps: - - uses: actions/checkout@v3 - - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python }} + uses: actions/setup-python@v5 + if: "!endsWith(matrix.python, '-dev')" + with: + python-version: ${{ matrix.python }} + - name: Set up Python ${{ matrix.python }} (deadsnakes) + uses: deadsnakes/action@v3.1.0 + 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 .[standalone,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@v4 + - name: Set up Python ${{ matrix.python }} + uses: actions/setup-python@v5 + 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 + - ruff - mypy steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3" diff --git a/.github/workflows/transifex.yml b/.github/workflows/transifex.yml new file mode 100644 index 0000000..71633f2 --- /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@v4 + - name: Set up Python + uses: actions/setup-python@v5 + 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@v4 + - name: Set up Python + uses: actions/setup-python@v5 + 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@v6 + with: + commit-message: "[internationalisation] Update translations" + branch: bot/pull-translations + title: "[bot]: Update message catalogues" + labels: "internals:internationalisation" diff --git a/.gitignore b/.gitignore index fe75ce5..10cef94 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,17 @@ *.pyc -*.egg -*.so -*.swp .DS_Store +idea/ +.vscode/ + .mypy_cache/ +.pytest_cache/ +.ruff_cache/ .tags .tox/ +.venv/ +venv/ + build/ dist/ -sphinxcontrib_htmlhelp.egg-info/ diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 0000000..4b7dd2a --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,53 @@ +target-version = "py39" # Pin Ruff to Python 3.9 +output-format = "full" +line-length = 95 + +[lint] +preview = true +select = [ +# "ANN", # flake8-annotations + "C4", # flake8-comprehensions + "COM", # flake8-commas + "B", # flake8-bugbear + "DTZ", # flake8-datetimez + "E", # pycodestyle + "EM", # flake8-errmsg + "EXE", # flake8-executable + "F", # pyflakes + "FA", # flake8-future-annotations + "FLY", # flynt + "FURB", # refurb + "G", # flake8-logging-format + "I", # isort + "ICN", # flake8-import-conventions + "INT", # flake8-gettext + "LOG", # flake8-logging + "PERF", # perflint + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PT", # flake8-pytest-style + "SIM", # flake8-simplify + "SLOT", # flake8-slots + "TCH", # flake8-type-checking + "UP", # pyupgrade + "W", # pycodestyle + "YTT", # flake8-2020 +] +ignore = [ + "E116", + "E241", + "E251", +] + +[lint.per-file-ignores] +"tests/*" = [ + "ANN", # tests don't need annotations +] + +[lint.isort] +forced-separate = [ + "tests", +] +required-imports = [ + "from __future__ import annotations", +] diff --git a/CHANGES b/CHANGES deleted file mode 100644 index cf0663d..0000000 --- a/CHANGES +++ /dev/null @@ -1,27 +0,0 @@ -Release 2.0.1 (2023-01-31) -========================== - -* Drop support for Python 3.7 and lower -* Fix deprecation warnings from Sphinx 6.1 - -Release 2.0.0 (2021-05-23) -========================== - -* Drop python-3.5 support -* Remove deprecated method: ``HTMLHelpBuilder.open_file()`` -* #6685: htmlhelp builder should generate HTML4 docs instead of HTML5 - -Release 1.0.2 (2019-04-12) -========================== - -* #6229: htmlhelp builder generates invalid .hhc file - -Release 1.0.1 (2019-02-15) -========================== - -* Remove debug print - -Release 1.0.0 (2019-02-15) -========================== - -* Initial release (copied from sphinx package) diff --git a/CHANGES.rst b/CHANGES.rst new file mode 100644 index 0000000..14bd960 --- /dev/null +++ b/CHANGES.rst @@ -0,0 +1,63 @@ +Release 2.1.0 (2024-07-28) +========================== + +* Adopt Ruff +* Tighten MyPy settings +* Update GitHub actions versions +* Escape HTML entities + +Release 2.0.6 (2024-07-20) +========================== + +* Fix tests for Sphinx 7.4 and later. + +Release 2.0.5 (2024-01-13) +========================== + +* Remove Sphinx as a required dependency, as circular dependencies may cause + failure with package managers that expect a directed acyclic graph (DAG) + of dependencies. + +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) +========================== + +* Drop support for Python 3.7 and lower +* Fix deprecation warnings from Sphinx 6.1 + +Release 2.0.0 (2021-05-23) +========================== + +* Drop python-3.5 support +* Remove deprecated method: ``HTMLHelpBuilder.open_file()`` +* #6685: htmlhelp builder should generate HTML4 docs instead of HTML5 + +Release 1.0.2 (2019-04-12) +========================== + +* #6229: htmlhelp builder generates invalid .hhc file + +Release 1.0.1 (2019-02-15) +========================== + +* Remove debug print + +Release 1.0.0 (2019-02-15) +========================== + +* Initial release (copied from sphinx package) diff --git a/LICENSE b/LICENCE.rst similarity index 100% rename from LICENSE rename to LICENCE.rst 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/Makefile b/Makefile index 26f411a..438ee54 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ clean-mypyfiles: .PHONY: style-check style-check: - @flake8 + @ruff check .PHONY: type-check type-check: 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..86da7e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,19 +1,19 @@ [build-system] -requires = ["setuptools>=64"] -build-backend = "setuptools.build_meta" +requires = ["flit_core>=3.7"] +build-backend = "flit_core.buildapi" # project metadata [project] name = "sphinxcontrib-htmlhelp" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" readme = "README.rst" -urls.Changelog = "https://www.sphinx-doc.org/en/master/changes.html" -urls.Code = "https://github.com/sphinx-doc/sphinxcontrib-htmlhelp" +urls.Changelog = "https://github.com/sphinx-doc/sphinxcontrib-htmlhelp/blob/master/CHANGES.rst" +urls.Code = "https://github.com/sphinx-doc/sphinxcontrib-htmlhelp/" 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" +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", @@ -48,17 +48,59 @@ test = [ "html5lib", ] lint = [ - "flake8", + "ruff==0.5.5", "mypy", - "docutils-stubs", + "types-docutils", +] +standalone = [ + "Sphinx>=5", ] [[project.authors]] 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.rst", + "LICENCE.rst", + # Tests + "tests/", + "tox.ini", +] [tool.mypy] -ignore_missing_imports = true +python_version = "3.9" +packages = [ + "sphinxcontrib", + "tests", +] +exclude = [ + "tests/roots", +] +check_untyped_defs = true +disallow_any_generics = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +#explicit_package_bases = true +extra_checks = true +no_implicit_reexport = true +show_column_numbers = true +show_error_context = true +strict_optional = true +warn_redundant_casts = true +warn_unused_configs = true +warn_unused_ignores = true +enable_error_code = [ + "type-arg", + "redundant-self", + "truthy-iterable", + "ignore-without-code", + "unused-awaitable", +] diff --git a/sphinxcontrib/htmlhelp/__init__.py b/sphinxcontrib/htmlhelp/__init__.py index fa6fe28..3127554 100644 --- a/sphinxcontrib/htmlhelp/__init__.py +++ b/sphinxcontrib/htmlhelp/__init__.py @@ -1,26 +1,19 @@ -""" - 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 +import re +from html.entities import codepoint2name from os import path -from typing import Any, Dict, List, Set, Tuple, Type - -from docutils import nodes -from docutils.nodes import Element, Node, document +from pathlib import Path +from typing import TYPE_CHECKING, Any import sphinx +from docutils import nodes from sphinx import addnodes -from sphinx.application import Sphinx from sphinx.builders.html import StandaloneHTMLBuilder -from sphinx.config import Config from sphinx.environment.adapters.indexentries import IndexEntries from sphinx.locale import get_translation from sphinx.util import logging @@ -28,15 +21,19 @@ 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 TYPE_CHECKING: + from docutils.nodes import Element, Node + from sphinx.application import Sphinx + from sphinx.config import Config 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] + from sphinx.util import progress_message # type: ignore[no-redef] -__version__ = '2.0.1' -__version_info__ = (2, 0, 1) +__version__ = '2.1.0' +__version_info__ = (2, 1, 0) logger = logging.getLogger(__name__) __ = get_translation(__name__, 'console') @@ -96,9 +93,9 @@ def chm_htmlescape(s: str, quote: bool = True) -> str: class ToCTreeVisitor(nodes.NodeVisitor): - def __init__(self, document: document) -> None: + def __init__(self, document: nodes.document) -> None: super().__init__(document) - self.body = [] # type: List[str] + self.body: list[str] = [] self.depth = 0 def append(self, text: str) -> None: @@ -133,8 +130,8 @@ def depart_list_item(self, node: Element) -> None: def visit_reference(self, node: Element) -> None: title = chm_htmlescape(node.astext(), True) - self.append(' ' % title) - self.append(' ' % node['refuri']) + self.append(f' ') + self.append(f' ') self.append('') raise nodes.SkipNode @@ -173,33 +170,46 @@ 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[str, Any], + event_arg: str, + ) -> None: ctx['encoding'] = self.encoding + # escape the `body` part to 7-bit ASCII + body = ctx.get("body") + if body is not None: + ctx["body"] = re.sub(r"[^\x00-\x7F]", self._escape, body) + + @staticmethod + def _escape(match: re.Match[str]) -> str: + codepoint = ord(match.group(0)) + if codepoint in codepoint2name: + return f"&{codepoint2name[codepoint]};" + return f"&#{codepoint};" + def handle_finish(self) -> None: self.copy_stopword_list() self.build_project_file() self.build_toc_file() self.build_hhx(self.outdir, self.config.htmlhelp_basename) - def write_doc(self, docname: str, doctree: document) -> None: - for node in doctree.traverse(nodes.reference): + def write_doc(self, docname: str, doctree: nodes.document) -> None: + for node in doctree.findall(nodes.reference): # add ``target=_blank`` attributes to external links if node.get('internal') is None and 'refuri' in node: node['target'] = '_blank' super().write_doc(docname, doctree) - def render(self, name: str, context: Dict) -> str: + def render(self, name: str, context: dict[str, Any]) -> str: template = SphinxRenderer(template_dir) return template.render(name, context) @@ -222,7 +232,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() @@ -232,51 +242,54 @@ def build_project_file(self) -> None: fn = relpath(path.join(root, fn), self.outdir) project_files.append(fn.replace(os.sep, '\\')) - filename = path.join(self.outdir, self.config.htmlhelp_basename + '.hhp') - with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f: - context = { - 'outname': self.config.htmlhelp_basename, - 'title': self.config.html_title, - 'version': self.config.version, - 'project': self.config.project, - 'lcid': self.lcid, - 'master_doc': self.config.master_doc + self.out_suffix, - 'files': project_files, - } - body = self.render('project.hhp', context) - f.write(body) + context = { + 'outname': self.config.htmlhelp_basename, + 'title': self.config.html_title, + 'version': self.config.version, + 'project': self.config.project, + 'lcid': self.lcid, + 'master_doc': self.config.master_doc + self.out_suffix, + 'files': project_files, + } + body = self.render('project.hhp', context) + filename = Path(self.outdir, f'{self.config.htmlhelp_basename}.hhp') + filename.write_text(body, encoding=self.encoding, errors='xmlcharrefreplace') @progress_message(__('writing TOC file')) def build_toc_file(self) -> None: """Create a ToC file (.hhp) on outdir.""" - filename = path.join(self.outdir, self.config.htmlhelp_basename + '.hhc') - with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f: - toctree = self.env.get_and_resolve_doctree(self.config.master_doc, self, - prune_toctrees=False) - visitor = ToCTreeVisitor(toctree) - matcher = NodeMatcher(addnodes.compact_paragraph, toctree=True) - for node in toctree.traverse(matcher): # type: addnodes.compact_paragraph - node.walkabout(visitor) - - context = { - 'body': visitor.astext(), - 'suffix': self.out_suffix, - 'short_title': self.config.html_short_title, - 'master_doc': self.config.master_doc, - 'domain_indices': self.domain_indices, - } - f.write(self.render('project.hhc', context)) - - def build_hhx(self, outdir: str, outname: str) -> None: + toctree = self.env.get_and_resolve_doctree(self.config.master_doc, self, + prune_toctrees=False) + visitor = ToCTreeVisitor(toctree) + matcher = NodeMatcher(addnodes.compact_paragraph, toctree=True) + for node in toctree.findall(matcher): + node.walkabout(visitor) + + context = { + 'body': visitor.astext(), + 'suffix': self.out_suffix, + 'short_title': self.config.html_short_title, + 'master_doc': self.config.master_doc, + 'domain_indices': self.domain_indices, + } + body = self.render('project.hhc', context) + filename = Path(self.outdir, f'{self.config.htmlhelp_basename}.hhc') + filename.write_text(body, encoding=self.encoding, errors='xmlcharrefreplace') + + 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') + filename = Path(outdir, outname + '.hhk') with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f: f.write('