From 910a76552661d6f1e5292c1a8b78fdcd84d97a39 Mon Sep 17 00:00:00 2001 From: jorenham Date: Tue, 27 Oct 2026 17:53:43 +0100 Subject: [PATCH] Enforce 100% public API type coverage in CI --- .github/workflows/test.yml | 1 + CONTRIBUTING.md | 10 +++++++++- Makefile | 6 +++++- poetry.lock | 24 +++++++++++++++++++++++- pyproject.toml | 1 + 5 files changed, 39 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d4d95220f..80dfc7245 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,6 +50,7 @@ jobs: run: | make lint make type-check + make type-coverage # Different python versions are covered differently: make unit diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e9fc43d2d..ce23896ff 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -54,11 +54,12 @@ Main targets: - `make format` — format and autofix code with `ruff` - `make lint` — run all linting checks (`ruff` and `flake8`) - `make type-check` — run `mypy` type checks +- `make type-coverage` — check public API type coverage with `pyrefly` - `make unit` — run standard tests with `pytest` - `make typesafety` — run type-safety tests (`pytest-mypy-plugins`) - `make slots` — check `__slots__` correctness with `slotscheck` - `make package` — check package dependencies with `pip check` -- `make test` — run all checks: lint, type-check, unit tests, slots, and package +- `make test` — run all checks: lint, type-check, type-coverage, unit tests, slots, and package ## Tests @@ -158,6 +159,13 @@ make type-check This step is mandatory during CI. +We also require the public API to be fully annotated. +We use `pyrefly` to check that its type coverage stays at 100%: + +```bash +make type-coverage +``` + ## Submitting your code diff --git a/Makefile b/Makefile index 5ff5aa8ff..ea951e72c 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,10 @@ type-check: ## Run type checking (mypy) $(POETRY) run mypy --enable-error-code=unused-awaitable returns $(POETRY) run mypy docs tests +.PHONY: type-coverage +type-coverage: ## Check public API type coverage (pyrefly) + $(POETRY) run pyrefly coverage check --public-only returns + .PHONY: unit unit: ## Run unit tests with pytest $(POETRY) run pytest returns docs/pages tests @@ -43,4 +47,4 @@ package: ## Check package dependencies with pip $(POETRY) run pip check .PHONY: test -test: lint type-check unit slots package ## Run all checks (lint, type-check, unit, slots, package) +test: lint type-check type-coverage unit slots package ## Run all checks (lint, type-check, type-coverage, unit, slots, package) diff --git a/poetry.lock b/poetry.lock index 4fecb975f..bff0c81ec 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1571,6 +1571,28 @@ markers = {main = "extra == \"check-laws\""} [package.extras] windows-terminal = ["colorama (>=0.4.6)"] +[[package]] +name = "pyrefly" +version = "1.4.0" +description = "A fast type checker and language server for Python with powerful IDE features" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pyrefly-1.4.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:460cec39bc8743915e9c2c8c10ba926ad0b29b099d89c1859fb97e0059f42fa7"}, + {file = "pyrefly-1.4.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5a6370d686b6cbbac8869832f09a52012888371956fbc68d446a349bc85ccd43"}, + {file = "pyrefly-1.4.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f1b16923a6583a6764e8aec07787e2ef4df644c7030294cba71339783a9451c"}, + {file = "pyrefly-1.4.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:688ad4cb4d6e711d67026fdcfb37b599f86061efe11ba5704287330e7041a69e"}, + {file = "pyrefly-1.4.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f5f3c9dddb6f3128beb49cf4e0f5cb88d4d17f039008d8e0c2be653966acd234"}, + {file = "pyrefly-1.4.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6951f51687304caebfedf13dd1a943aeafdbc95abcc4e24a683b97467a77007"}, + {file = "pyrefly-1.4.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:838de4d4a8b9b8ed39bedd0b8b55629c69661d73a6fa3a3a4adff24b9dbeb399"}, + {file = "pyrefly-1.4.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:afb9ce2e0ae3e6106fb45f6d47536ac92b6423e601e08401cdefa850066444f5"}, + {file = "pyrefly-1.4.0-py3-none-win32.whl", hash = "sha256:839db536d0ac29f1b23e2d974fb911075c2283e3e623755c45d826aafac175bc"}, + {file = "pyrefly-1.4.0-py3-none-win_amd64.whl", hash = "sha256:7aff37c2b711c8240ed650da0c6148237b327bf7e5dfacee65884f1adea22305"}, + {file = "pyrefly-1.4.0-py3-none-win_arm64.whl", hash = "sha256:88879f0cb749e5fea81d9ff4c2d5431a70349c458bcdf0b61de72c79214d90a5"}, + {file = "pyrefly-1.4.0.tar.gz", hash = "sha256:bb57c004a4842cfe93f93aebb5203ca6209a5f0c8bda47e8bc289e70940fd910"}, +] + [[package]] name = "pytest" version = "9.1.1" @@ -2595,4 +2617,4 @@ compatible-mypy = ["mypy"] [metadata] lock-version = "2.1" python-versions = "^3.11" -content-hash = "62809e143050a8d4c9476d5e138c8d1ae73a8e035f4fa528529d4053ce322532" +content-hash = "5f85594c10c90f74fbd27f9d787f411c59a4fc4734d1705388d9c1d0214a9ab8" diff --git a/pyproject.toml b/pyproject.toml index 5e66b4041..abc5ff553 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,6 +74,7 @@ pytest-subtests = "^0.15" pytest-shard = "^0.1" pytest-codspeed = "^5.0" covdefaults = "^2.3" +pyrefly = "^1.4" [tool.poetry.group.docs] optional = true