diff --git a/.github/workflows/docs_build_and_deploy.yml b/.github/workflows/docs_build_and_deploy.yml index fe65f42fc..682ea8f28 100644 --- a/.github/workflows/docs_build_and_deploy.yml +++ b/.github/workflows/docs_build_and_deploy.yml @@ -27,9 +27,48 @@ jobs: name: Build Sphinx Docs runs-on: ubuntu-latest steps: - - uses: neuroinformatics-unit/actions/build_sphinx_docs@main - with: - python-version: 3.11 + - uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: 3.11 + + - name: Upgrade pip + shell: bash + run: | + # install pip=>20.1 to use "pip cache dir" + python3 -m pip install --upgrade pip + + - name: Get pip cache dir + shell: bash + id: pip-cache + run: echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Install dependencies + shell: bash + run: python3 -m pip install -r ./docs/requirements.txt + + - name: Check links and build documentation + shell: bash + run: | + cd docs + make linkcheck + make html + + - name: Upload the content for deployment + uses: actions/upload-artifact@v4 + with: + name: docs-build + path: ./docs/build/ deploy_sphinx_docs: name: Deploy Sphinx Docs diff --git a/.gitignore b/.gitignore index 19c88937c..c46b1d71a 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,7 @@ instance/ docs/build/ docs/source/examples/ docs/source/api/ +docs/source/api_index.rst sg_execution_times.rst # MkDocs documentation diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 281c5eda5..5ee95524c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -209,32 +209,14 @@ If it is not yet defined and you have multiple external links pointing to the sa ### Updating the API reference -If your PR introduces new public modules, or renames existing ones, -make sure to add them to the `docs/source/api_index.rst` page, so that they are included in the [API reference](target-api), e.g.: - -```rst -API Reference -============= - -Information on specific functions, classes, and methods. - -.. rubric:: Modules - -.. autosummary:: - :toctree: api - :recursive: - :nosignatures: - - movement.move_accessor - movement.io.load_poses - movement.io.save_poses - movement.your_new_module -``` - -The API reference is auto-generated by the `sphinx-autodoc` and `sphinx-autosummary` plugins, based on docstrings. +The API reference is auto-generated by the `docs/make_api_index.py` script, and the `sphinx-autodoc` and `sphinx-autosummary` plugins. +The script generates the `docs/source/api_index.rst` file containing the list of modules to be included in the [API reference](target-api). +The plugins then generate the API reference pages for each module listed in `api_index.rst`, based on the docstrings in the source code. So make sure that all your public functions/classes/methods have valid docstrings following the [numpydoc](https://numpydoc.readthedocs.io/en/latest/format.html) style. Our `pre-commit` hooks include some checks (`ruff` rules) that ensure the docstrings are formatted consistently. +If your PR introduces new modules that should not be documented in the [API reference](target-api), or if there are changes to existing modules that necessitate their removal from the documentation, make sure to update the `exclude_modules` list within the `docs/make_api_index.py` script to reflect these exclusions. + ### Updating the examples We use [sphinx-gallery](sphinx-gallery:) to create the [examples](target-examples). @@ -259,14 +241,14 @@ pip install -r docs/requirements.txt Then, from the root of the repository, run: ```sh -sphinx-build docs/source docs/build +python docs/make_api_index.py && sphinx-build docs/source docs/build ``` You can view the local build by opening `docs/build/index.html` in a browser. To refresh the documentation, after making changes, remove the `docs/build` folder and re-run the above command: ```sh -rm -rf docs/build && sphinx-build docs/source docs/build +rm -rf docs/build && python docs/make_api_index.py && sphinx-build docs/source docs/build ``` To check that external links are correctly resolved, run: diff --git a/docs/Makefile b/docs/Makefile index d0c3cbf10..623d2906a 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -14,7 +14,18 @@ help: .PHONY: help Makefile +# Generate the API index file +api_index.rst: + python make_api_index.py + +# Remove all generated files +clean: + rm -rf ./build + rm -f ./source/api_index.rst + rm -rf ./source/api + rm -rf ./source/examples + # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile +%: Makefile api_index.rst @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/make.bat b/docs/make.bat index dc1312ab0..79a8b01a6 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -25,6 +25,9 @@ if errorlevel 9009 ( if "%1" == "" goto help +echo "Generating API index..." +python make_api_index.py + %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% goto end diff --git a/docs/make_api_index.py b/docs/make_api_index.py new file mode 100644 index 000000000..372231129 --- /dev/null +++ b/docs/make_api_index.py @@ -0,0 +1,44 @@ +"""Generate the API index page for all ``movement`` modules.""" + +import os + +# Modules to exclude from the API index +exclude_modules = ["cli_entrypoint"] + +# Set the current working directory to the directory of this script +script_dir = os.path.dirname(os.path.abspath(__file__)) +os.chdir(script_dir) + + +def make_api_index(): + """Create a doctree of all ``movement`` modules.""" + doctree = "\n" + + for root, _, files in os.walk("../movement"): + # Remove leading "../" + root = root[3:] + for file in sorted(files): + if file.endswith(".py") and not file.startswith("_"): + # Convert file path to module name + module_name = os.path.join(root, file) + module_name = module_name[:-3].replace(os.sep, ".") + # Check if the module should be excluded + if not any( + file.startswith(exclude_module) + for exclude_module in exclude_modules + ): + doctree += f" {module_name}\n" + + # Get the header + with open("./source/_templates/api_index_head.rst") as f: + api_head = f.read() + # Write api_index.rst with header + doctree + with open("./source/api_index.rst", "w") as f: + f.write("..\n This file is auto-generated.\n\n") + f.write(api_head) + f.write(doctree) + print(os.path.abspath("./source/api_index.rst")) + + +if __name__ == "__main__": + make_api_index() diff --git a/docs/source/_templates/api_index_head.rst b/docs/source/_templates/api_index_head.rst new file mode 100644 index 000000000..f1df7eb61 --- /dev/null +++ b/docs/source/_templates/api_index_head.rst @@ -0,0 +1,13 @@ +.. _target-api: + +API Reference +============= + +Information on specific functions, classes, and methods. + +.. rubric:: Modules + +.. autosummary:: + :toctree: api + :recursive: + :nosignatures: diff --git a/docs/source/api_index.rst b/docs/source/api_index.rst deleted file mode 100644 index d6d04edaf..000000000 --- a/docs/source/api_index.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _target-api: - -API Reference -============= - -Information on specific functions, classes, and methods. - -.. rubric:: Modules - -.. autosummary:: - :toctree: api - :recursive: - :nosignatures: - - movement.move_accessor - movement.io.load_poses - movement.io.save_poses - movement.filtering - movement.analysis.kinematics - movement.utils.vector - movement.utils.logging - movement.sample_data - movement.validators.files - movement.validators.datasets