diff --git a/.copier-answers.yml b/.copier-answers.yml index 3cfac89673..ea3dc46796 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,9 +1,11 @@ # Changes here will be overwritten by Copier -_commit: 2.2.0 +_commit: 2.5.0 _src_path: gh:DiamondLightSource/python-copier-template author_email: dominic.oram@diamond.ac.uk author_name: Dominic Oram +component_lifecycle: production component_owner: group:data-acquisition +component_type: library description: Ophyd devices and other utils that could be used across DLS beamlines distribution_name: dls-dodal docker: false diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 56a615f2ed..ef5cd52199 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -24,4 +24,4 @@ It is recommended that developers use a [vscode devcontainer](https://code.visua This project was created using the [Diamond Light Source Copier Template](https://github.com/DiamondLightSource/python-copier-template) for Python projects. -For more information on common tasks like setting up a developer environment, running the tests, and setting a pre-commit hook, see the template's [How-to guides](https://diamondlightsource.github.io/python-copier-template/2.2.0/how-to.html). +For more information on common tasks like setting up a developer environment, running the tests, and setting a pre-commit hook, see the template's [How-to guides](https://diamondlightsource.github.io/python-copier-template/2.5.0/how-to.html). diff --git a/.github/pages/make_switcher.py b/.github/pages/make_switcher.py index 29f646c3ac..c06813afad 100755 --- a/.github/pages/make_switcher.py +++ b/.github/pages/make_switcher.py @@ -1,3 +1,5 @@ +"""Make switcher.json to allow docs to switch between different versions.""" + import json import logging from argparse import ArgumentParser @@ -6,6 +8,7 @@ def report_output(stdout: bytes, label: str) -> list[str]: + """Print and return something received frm stdout.""" ret = stdout.decode().strip().split("\n") print(f"{label}: {ret}") return ret @@ -52,14 +55,12 @@ def get_versions(ref: str, add: str | None) -> list[str]: return versions -def write_json(path: Path, repository: str, versions: str): +def write_json(path: Path, repository: str, versions: list[str]): + """Write the JSON switcher to path.""" org, repo_name = repository.split("/") - pages_url = f"https://{org}.github.io" - if repo_name != f"{org}.github.io": - # Only add the repo name if it isn't the source for the org pages site - pages_url += f"/{repo_name}" struct = [ - {"version": version, "url": f"{pages_url}/{version}/"} for version in versions + {"version": version, "url": f"https://{org}.github.io/{repo_name}/{version}/"} + for version in versions ] text = json.dumps(struct, indent=2) print(f"JSON switcher:\n{text}") @@ -67,6 +68,7 @@ def write_json(path: Path, repository: str, versions: str): def main(args=None): + """Parse args and write switcher.""" parser = ArgumentParser( description="Make a versions.json file from gh-pages directories" ) diff --git a/.github/workflows/_pypi.yml b/.github/workflows/_pypi.yml index 0c5258dbee..8032bbaac4 100644 --- a/.github/workflows/_pypi.yml +++ b/.github/workflows/_pypi.yml @@ -15,3 +15,5 @@ jobs: - name: Publish to PyPI using trusted publishing uses: pypa/gh-action-pypi-publish@release/v1 + with: + attestations: false diff --git a/.github/workflows/_release.yml b/.github/workflows/_release.yml index 10d8ed87d1..81b626438e 100644 --- a/.github/workflows/_release.yml +++ b/.github/workflows/_release.yml @@ -23,7 +23,7 @@ jobs: - name: Create GitHub Release # We pin to the SHA, not the tag, for security reasons. # https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#using-third-party-actions - uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8 + uses: softprops/action-gh-release@e7a8f85e1c67a31e6ed99a94b41bd0b71bbee6b8 # v2.0.9 with: prerelease: ${{ contains(github.ref_name, 'a') || contains(github.ref_name, 'b') || contains(github.ref_name, 'rc') }} files: "*" diff --git a/.gitignore b/.gitignore index 6b1fba131b..cdf2407c97 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,7 @@ tmp/ # Sphinx documentation docs/_build/ docs/reference/generated/ +docs/_api # PyBuilder target/ diff --git a/Dockerfile b/Dockerfile index c4404ecabb..35d2abf7a5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # The devcontainer should use the developer target and run as root with podman # or docker with user namespaces. ARG PYTHON_VERSION=3.11 -FROM python:${PYTHON_VERSION} as developer +FROM python:${PYTHON_VERSION} AS developer # Add any system dependencies for the developer/build environment here RUN apt-get update && apt-get install -y --no-install-recommends \ diff --git a/README.md b/README.md index be3bd3a1d4..84b9d91618 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![CI](https://github.com/DiamondLightSource/dodal/actions/workflows/ci.yml/badge.svg)](https://github.com/DiamondLightSource/dodal/actions/workflows/ci.yml) [![Coverage](https://codecov.io/gh/DiamondLightSource/dodal/branch/main/graph/badge.svg)](https://codecov.io/gh/DiamondLightSource/dodal) [![PyPI](https://img.shields.io/pypi/v/dls-dodal.svg)](https://pypi.org/project/dls-dodal) -[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) # dodal diff --git a/docs/_api.rst b/docs/_api.rst new file mode 100644 index 0000000000..74b3d27e0d --- /dev/null +++ b/docs/_api.rst @@ -0,0 +1,16 @@ +:orphan: + +.. + This page is not included in the TOC tree, but must exist so that the + autosummary pages are generated for dodal and all its + subpackages + +API +=== + +.. autosummary:: + :toctree: _api + :template: custom-module-template.rst + :recursive: + + dodal diff --git a/docs/_templates/custom-module-template.rst b/docs/_templates/custom-module-template.rst new file mode 100644 index 0000000000..9aeca54015 --- /dev/null +++ b/docs/_templates/custom-module-template.rst @@ -0,0 +1,37 @@ +{{ ('``' + fullname + '``') | underline }} + +{%- set filtered_members = [] %} +{%- for item in members %} + {%- if item in functions + classes + exceptions + attributes %} + {% set _ = filtered_members.append(item) %} + {%- endif %} +{%- endfor %} + +.. automodule:: {{ fullname }} + :members: + + {% block modules %} + {% if modules %} + .. rubric:: Submodules + + .. autosummary:: + :toctree: + :template: custom-module-template.rst + :recursive: + {% for item in modules %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block members %} + {% if filtered_members %} + .. rubric:: Members + + .. autosummary:: + :nosignatures: + {% for item in filtered_members %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} diff --git a/docs/conf.py b/docs/conf.py index 5d29e18465..65a6f1cdaa 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,8 +1,9 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html +"""Configuration file for the Sphinx documentation builder. + +This file only contains a selection of the most common options. For a full +list see the documentation: +https://www.sphinx-doc.org/en/master/usage/configuration.html +""" import sys from inspect import isclass @@ -33,6 +34,7 @@ extensions = [ # Use this for generating API docs "sphinx.ext.autodoc", + # and making summary tables at the top of API docs "sphinx.ext.autosummary", # This can parse google style docstrings "sphinx.ext.napoleon", @@ -91,6 +93,12 @@ autosummary_generate = True autosummary_imported_members = False +# Document only what is in __all__ +autosummary_ignore_module_all = False + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + # Output graphviz directive produced images in a scalable format graphviz_output_format = "svg" diff --git a/docs/reference.md b/docs/reference.md index 7362151f5c..0361832d00 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -6,7 +6,7 @@ Technical reference material including APIs and release notes. :maxdepth: 1 :glob: -reference/* +API <_api/dodal> genindex Release Notes ``` diff --git a/docs/reference/api.md b/docs/reference/api.md deleted file mode 100644 index 0c92e9e065..0000000000 --- a/docs/reference/api.md +++ /dev/null @@ -1,26 +0,0 @@ -# API - -```{eval-rst} -.. autosummary:: - :recursive: - :toctree: generated - - dodal.beamlines - dodal.common - dodal.devices - dodal.plans - -.. automodule:: dodal - - ``dodal`` - ----------------------------------- -``` - -This is the internal API reference for dodal - -```{eval-rst} -.. data:: dodal.__version__ - :type: str - - Version number as calculated by https://github.com/pypa/setuptools_scm -``` diff --git a/pyproject.toml b/pyproject.toml index 9202389772..4bd710efd2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -91,6 +91,7 @@ dodal = ["*.txt"] version_file = "src/dodal/_version.py" [tool.pyright] +typeCheckingMode = "standard" reportMissingImports = false # Ignore missing stubs in imported modules [tool.pytest.ini_options] diff --git a/src/dodal/__init__.py b/src/dodal/__init__.py index 26d23badb6..a2ffbf3694 100644 --- a/src/dodal/__init__.py +++ b/src/dodal/__init__.py @@ -1,3 +1,11 @@ +"""Top level API. + +.. data:: __version__ + :type: str + + Version number as calculated by https://github.com/pypa/setuptools_scm +""" + from ._version import __version__ __all__ = ["__version__"] diff --git a/tests/conftest.py b/tests/conftest.py index c757681669..000afd5ec8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,6 +7,7 @@ from collections.abc import Mapping from os import environ, getenv from pathlib import Path +from typing import Any from unittest.mock import MagicMock, patch import pytest @@ -67,11 +68,16 @@ def patched_open(*args, **kwargs): if os.getenv("PYTEST_RAISE", "0") == "1": @pytest.hookimpl(tryfirst=True) - def pytest_exception_interact(call): - raise call.excinfo.value + def pytest_exception_interact(call: pytest.CallInfo[Any]): + if call.excinfo is not None: + raise call.excinfo.value + else: + raise RuntimeError( + f"{call} has no exception data, an unknown error has occurred" + ) @pytest.hookimpl(tryfirst=True) - def pytest_internalerror(excinfo): + def pytest_internalerror(excinfo: pytest.ExceptionInfo[Any]): raise excinfo.value