diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index b648bf2715..5ef6c1c1da 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -507,15 +507,16 @@ jobs: - uses: actions/checkout@v5 with: ref: ${{ inputs.source_ref }} + fetch-depth: 0 - uses: actions/setup-python@v5 with: python-version: '3.13' - name: Setup flake8 annotations uses: rbialon/flake8-annotations@v1 - name: Install linting packages - run: pip install flake8 pylint + run: pip install flake8 numpydoc pylint - name: Lint codebase - run: make lint GITHUB_ACTIONS_FORMATTING=1 + run: make lint GITHUB_ACTIONS_FORMATTING=1 BASE=origin/${{ inputs.base_ref }} # Docs diff --git a/Makefile b/Makefile index cd8e2f651b..c3e5415fc8 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,19 @@ modules: @python setup.py build_ext --inplace > build.log 2>&1 || cat build.log .PHONY: lint -lint: srclint actionlint dockerlint +lint: srclint doclint actionlint dockerlint + +# CI passes BASE explicitly (it knows main vs release from the PR's base +# branch/label). Locally, guess it: whichever of origin/main, origin/release +# forked from HEAD more recently is the one this branch was cut from. +BASE ?= $(shell \ + m=$$(git merge-base HEAD origin/main 2>/dev/null); \ + r=$$(git merge-base HEAD origin/release 2>/dev/null); \ + if [ -n "$$r" ] && git merge-base --is-ancestor "$$m" "$$r" 2>/dev/null; then \ + echo origin/release; \ + else \ + echo origin/main; \ + fi) # Adds file annotations to Github Actions (only useful on CI) GITHUB_ACTIONS_FORMATTING=0 @@ -33,6 +45,12 @@ srclint: @echo " Linting TSFC" @python -m flake8 $(FLAKE8_FORMAT) tsfc +.PHONY: doclint +doclint: + @echo " Checking docstrings" + @git diff --name-only --diff-filter=ACM $(BASE)...HEAD -- '*.py' \ + | xargs -r python -m numpydoc lint + .PHONY: actionlint actionlint: @echo " Pull latest actionlint image" diff --git a/pyproject.toml b/pyproject.toml index 2e2c32ff73..f6a0858548 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,12 +72,14 @@ check = [ ] docs = [ "bibtexparser", + "flake8", "ipykernel", # kernel for executing the tutorial notebooks "ipympl", # %matplotlib widget backend used by the tutorial notebooks "jupytext", # convert the .py tutorial notebooks to .ipynb "matplotlib", # needed to resolve API "nbsphinx", # execute and render the tutorial notebooks "numpydoc", + "pylint", "pylit", "sphinx", "sphinx-autobuild", @@ -190,3 +192,9 @@ pyop2 = [ "*.pyx", "codegen/c/*.c", ] + +[tool.numpydoc_validation] +checks = ["GL06", "GL07", "PR01", "PR02", "PR03", "SS03", "SS05"] +# exclude matches the dotted object name; exclude_files matches the path. +exclude = ['\.__init__$'] +exclude_files = ['^tests/', '^firedrake/_version\.py$']