diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 0d6eb8c..97518e8 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -3,13 +3,22 @@ { "name": "Python 3", // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "image": "mcr.microsoft.com/devcontainers/python:3.13", + "image": "mcr.microsoft.com/devcontainers/python:3.14", // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "pip3 install --user pip-tools && pip3 install --user -r requirements_dev.txt", + // These packages let us build the PDF locally with either LaTeX engine the + // theme might select: texlive-luatex covers a LuaLaTeX build (fontspec + + // bundled fonts), while tex-gyre supplies tgtermes/tgheros etc. for a + // default pdfLaTeX build. tex-gyre must be named explicitly: on recent + // Debian it was split out of texlive-fonts-recommended into its own package + // that --no-install-recommends won't pull in. texlive-latex-extra/-recommended + // (+ latexmk, xindy) supply the shared styling/toolchain packages either + // engine needs (see .vscode/tasks.json and README.rst). sudo is needed + // since this image's default user (vscode) isn't root. + "postCreateCommand": "sudo apt-get update && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends texlive-luatex texlive-latex-extra texlive-fonts-recommended tex-gyre latexmk xindy && sudo rm -rf /var/lib/apt/lists/* && pip3 install --user pip-tools && pip3 install --user -r requirements_dev.txt", // Warn if a host-built .ve is present; its python symlink dangles here and breaks the Sphinx Autobuild debugger. "postStartCommand": "if [ -e .ve ] && ! .ve/bin/python -c '' 2>/dev/null; then printf '\\n\\033[1;31m⚠ A host-built .ve was found and its Python does not work in this container.\\033[0m\\n It will break the Sphinx Autobuild debugger. The devcontainer installs deps via pip (no venv needed).\\n Fix: remove the .ve, then rebuild/reopen.\\n\\n'; fi", // Configure tool-specific properties. diff --git a/.gitignore b/.gitignore index 6397e19..acde5cf 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ *.swp *~ .DS_Store +__pycache__/ +*.pyc diff --git a/.python-version b/.python-version index 24ee5b1..6324d40 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.13 +3.14 diff --git a/.readthedocs.yaml b/.readthedocs.yaml index ea0be75..f37105c 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -9,7 +9,7 @@ version: 2 build: os: ubuntu-24.04 tools: - python: "3.13" + python: "3.14" jobs: pre_build: - python -c "import iati_sphinx_theme, shutil, os; src = os.path.join(os.path.dirname(iati_sphinx_theme.__file__), \"locale\"); shutil.copytree(src, \"docs/locale\", dirs_exist_ok=True)" diff --git a/.vscode/launch.json b/.vscode/launch.json index 9ed20c2..b43f942 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,13 +5,26 @@ "name": "Sphinx Autobuild", "type": "debugpy", "request": "launch", + // Pin the image's Python, where the deps are installed. The image + // ships two interpreters (/usr/local/bin/python and Debian's + // /usr/bin/python3) on different minor versions; only the former + // has the --user packages, so leaving this to interpreter + // selection can launch the wrong one. + "python": "/usr/local/bin/python", + // sphinx-autobuild shells out to `python -m sphinx` to build. Let + // debugpy follow that child and it re-launches it in a way that + // drops the pip --user site (~/.local) from sys.path, so the build + // fails with "No module named sphinx". We debug the autobuild + // process itself, not the build subprocess, so don't follow it. + "subProcess": false, "module": "sphinx_autobuild", "args": [ "-b", "dirhtml", "docs", "docs/_build/html" ], - "justMyCode": false + "justMyCode": false, + "preLaunchTask": "Build PDF for Autobuild" } ] } diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..5a05950 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,18 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build PDF for Autobuild", + "type": "shell", + "command": "mkdir -p _build/html && (make -e SPHINXBUILD=\"/usr/local/bin/python -m sphinx.cmd.build\" latexpdf && cp _build/latex/*.pdf _build/html/ || true)", + "options": { + "cwd": "${workspaceFolder}/docs" + }, + "presentation": { + "reveal": "silent", + "panel": "dedicated" + }, + "problemMatcher": [] + } + ] +} diff --git a/README.rst b/README.rst index cbbee97..4aeaa07 100644 --- a/README.rst +++ b/README.rst @@ -106,7 +106,10 @@ To change the language, edit the `language` variable in `docs/conf.py`. Using VS Code ------------- -A devcontainer.json and launch.json are supplied which add sphinx-autobuild as a Run option +A devcontainer.json and launch.json are supplied which add sphinx-autobuild as a Run option. + +To build the PDF without VS Code, run ``make -C docs latexpdf``; the output is written to +``docs/_build/latex/``. Contributing ============ diff --git a/docs/conf.py b/docs/conf.py index a33f4d9..c3ead71 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -7,12 +7,20 @@ # Project-specific settings are imported from project_info.py. import os +import sys import sphinx.application from sphinx.locale import get_translation import iati_sphinx_theme +# Make project_info importable regardless of how Sphinx is invoked. `python -m +# sphinx`/sphinx-autobuild add the current working directory to sys.path +# automatically when run from this directory, but installed console scripts +# like `sphinx-build` (e.g. via `make latexpdf`) don't - their sys.path[0] is +# their own bin directory instead. +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + # Import project-specific settings from project_info import ( project, @@ -55,6 +63,7 @@ "sphinxcontrib.redoc", "sphinxcontrib.video", "sphinxcontrib.youtube", + "iati_sphinx_theme", # Register theme as extension for LaTeX defaults ] templates_path = ["_templates"] @@ -85,6 +94,19 @@ html_context = {} +# -- Options for LaTeX/PDF output ----------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#latex-options + +latex_documents = [ + ( + "index", # startdocname + "iati-docs-base.tex", # targetname + project_title, # title + author, # author + "manual", # theme + ), +] + if os.environ.get("READTHEDOCS") == "True": project_slug = os.environ.get("READTHEDOCS_PROJECT") version_slug = os.environ.get("READTHEDOCS_VERSION") @@ -94,6 +116,16 @@ pdf_url = f"https://{project_slug}.readthedocs-hosted.com/_/downloads/{language_slug}/{version_slug}/pdf/" html_context["pdf_url"] = pdf_url +else: + # Local dev builds have no RTD-hosted PDF. `make latexpdf` (wired up as + # a preLaunchTask in .vscode/tasks.json) builds one alongside the HTML + # output instead - only link to it if that's actually happened, so a + # plain `make html` or a terminal `sphinx-autobuild` run (which don't + # build the PDF) doesn't show a link that 404s. + pdf_filename = latex_documents[0][1].replace(".tex", ".pdf") + pdf_path = os.path.join(os.path.dirname(__file__), "_build", "html", pdf_filename) + if os.path.exists(pdf_path): + html_context["pdf_url"] = f"/{pdf_filename}" # -- Options for Texinfo output -------------------------------------------