From c2c517d9e59d8fee9158529453b4314039bf3bfd Mon Sep 17 00:00:00 2001 From: Gabor Szabo Date: Sat, 25 Oct 2025 11:11:30 +0300 Subject: [PATCH 01/12] Add type-checking * Add .mypy.ini * Add mypy to pre-commit * Add mypy to GitHub Actioons See #2173 --- .github/workflows/mypy.yml | 26 ++++++++++++++++++++++++++ .mypy.ini | 8 ++++++++ .pre-commit-config.yaml | 4 ++++ 3 files changed, 38 insertions(+) create mode 100644 .github/workflows/mypy.yml create mode 100644 .mypy.ini diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml new file mode 100644 index 000000000..6f6463ed7 --- /dev/null +++ b/.github/workflows/mypy.yml @@ -0,0 +1,26 @@ +name: Run mypy + +on: + push: + pull_request: + +jobs: + mypy: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python: ["3.12"] + + steps: + - name: Checkout + uses: actions/checkout@v5 + - uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python }} + - name: Install mypy + run: | + pip install mypy + - name: Run mypy + run: | + mypy src diff --git a/.mypy.ini b/.mypy.ini new file mode 100644 index 000000000..d440181d3 --- /dev/null +++ b/.mypy.ini @@ -0,0 +1,8 @@ +[mypy] + +exclude = (?x)( + src/anndata/__init__.py| + src/testing/anndata/ + ) + +disable_error_code = import-not-found, import-untyped, no-redef, attr-defined, union-attr, index, assignment, arg-type, return-value, type-arg, type-var, return, call-arg, misc, override, valid-type, has-type, name-defined, var-annotated, call-overload, operator, list-item diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 80d3310fc..f168a2046 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,3 +39,7 @@ repos: - id: codespell additional_dependencies: - tomli + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.18.2 + hooks: + - id: mypy From d01dbda58a1d16f7bc6d5b8c8952e3ec083e7f21 Mon Sep 17 00:00:00 2001 From: Gabor Szabo Date: Sat, 25 Oct 2025 12:55:09 +0300 Subject: [PATCH 02/12] exclude less files by mypy --- .mypy.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.mypy.ini b/.mypy.ini index d440181d3..e9b57c462 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -1,8 +1,8 @@ [mypy] exclude = (?x)( - src/anndata/__init__.py| - src/testing/anndata/ + src/testing/anndata/__init__.py| + src/testing/anndata/_doctest.py ) disable_error_code = import-not-found, import-untyped, no-redef, attr-defined, union-attr, index, assignment, arg-type, return-value, type-arg, type-var, return, call-arg, misc, override, valid-type, has-type, name-defined, var-annotated, call-overload, operator, list-item From 5558e9e8706c136e6f98ea53caeb26c9917e05f6 Mon Sep 17 00:00:00 2001 From: Gabor Szabo Date: Sat, 25 Oct 2025 15:42:04 +0300 Subject: [PATCH 03/12] comment out mypy in the pre-commit hook for now --- .pre-commit-config.yaml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f168a2046..d037b5eb4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,7 +39,8 @@ repos: - id: codespell additional_dependencies: - tomli - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.18.2 - hooks: - - id: mypy +# See https://github.com/scverse/anndata/pull/2174 +# - repo: https://github.com/pre-commit/mirrors-mypy +# rev: v1.18.2 +# hooks: +# - id: mypy From da088942103e8cb85b9f4080baf6274f17967ef8 Mon Sep 17 00:00:00 2001 From: Gabor Szabo Date: Sat, 25 Oct 2025 15:47:34 +0300 Subject: [PATCH 04/12] make name of the ci job stand out --- .github/workflows/mypy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 6f6463ed7..124444bd1 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -1,4 +1,4 @@ -name: Run mypy +name: mypy on: push: From 94bff0724372dd32f59565a6b75624e86908fb17 Mon Sep 17 00:00:00 2001 From: Gabor Szabo Date: Mon, 27 Oct 2025 15:15:35 +0200 Subject: [PATCH 05/12] move mypy configuration to pyproject.toml --- .mypy.ini | 8 -------- pyproject.toml | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 8 deletions(-) delete mode 100644 .mypy.ini diff --git a/.mypy.ini b/.mypy.ini deleted file mode 100644 index e9b57c462..000000000 --- a/.mypy.ini +++ /dev/null @@ -1,8 +0,0 @@ -[mypy] - -exclude = (?x)( - src/testing/anndata/__init__.py| - src/testing/anndata/_doctest.py - ) - -disable_error_code = import-not-found, import-untyped, no-redef, attr-defined, union-attr, index, assignment, arg-type, return-value, type-arg, type-var, return, call-arg, misc, override, valid-type, has-type, name-defined, var-annotated, call-overload, operator, list-item diff --git a/pyproject.toml b/pyproject.toml index 7ac7bcd7b..8e3509357 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -265,3 +265,45 @@ fragment.perf.name = "Performance" fragment.chore.name = "Miscellaneous changes" fragment.revert.name = "Revert" fragment.breaking.name = "Breaking changes" # add `!` to commit type (e.g. “feature!:”) + +[tool.mypy] +exclude = [ + '^src/testing/anndata/__init__\.py$', + '^src/testing/anndata/_doctest\.py$', +] + +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = [ "anndata/*" ] + +disable_error_code = [ + "import-untyped", + "no-redef", + "attr-defined", + "union-attr", + "index", + "assignment", + "arg-type", + "return-value", + "type-arg", + "type-var", + "return", + "call-arg", + "misc", + "override", + "valid-type", + "has-type", + "name-defined", + "var-annotated", + "call-overload", + "operator", + "list-item", +] + +[[tool.mypy.overrides]] +module = [ "testing/*" ] + +disable_error_code = [ + "attr-defined", +] From f17d1b4fde575d9cdcfdb1d0c546e21cebffb66c Mon Sep 17 00:00:00 2001 From: Gabor Szabo Date: Wed, 29 Oct 2025 16:44:04 +0200 Subject: [PATCH 06/12] try to add requiremens to pre-commit hook --- .pre-commit-config.yaml | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d037b5eb4..4da74f1f7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -40,7 +40,23 @@ repos: additional_dependencies: - tomli # See https://github.com/scverse/anndata/pull/2174 -# - repo: https://github.com/pre-commit/mirrors-mypy -# rev: v1.18.2 -# hooks: -# - id: mypy +# Running `pre-commit run -a` gives the following error: +# tests/conftest.py: error: Duplicate module named "conftest" (also at "./tests/lazy/conftest.py") +# tests/conftest.py: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#mapping-file-paths-to-modules for more info +# This seems to be the same failure we get on the prec-commit.ci + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.18.2 + hooks: + - id: mypy + args: [--config-file=pyproject.toml, .] + pass_filenames: false + additional_dependencies: + - pandas + - numpy + - scipy + - h5py + - natsort + - packaging + - array_api_compat + - legacy-api-wrap + - zarr From bf2d18a37d1cf525aa917de1c210739391dd4c3c Mon Sep 17 00:00:00 2001 From: Gabor Szabo Date: Wed, 29 Oct 2025 17:19:29 +0200 Subject: [PATCH 07/12] disable mypy pre-commit on the ci as it is too big --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4da74f1f7..95a28b283 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,6 @@ ci: autoupdate_commit_msg: "ci: pre-commit autoupdate" + skip: [mypy] # too big repos: - repo: https://github.com/astral-sh/ruff-pre-commit From e692b9080611be1650ff85877e9e78aeb2f6eff9 Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Fri, 31 Oct 2025 13:37:55 +0100 Subject: [PATCH 08/12] add --show-traceback --- .github/workflows/mypy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 124444bd1..af3ed4952 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -23,4 +23,4 @@ jobs: pip install mypy - name: Run mypy run: | - mypy src + mypy --show-traceback src From 91ed109f319c808f4cde4bdd51381dfa2c91889b Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Fri, 31 Oct 2025 13:40:48 +0100 Subject: [PATCH 09/12] use max Python version --- .github/workflows/mypy.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index af3ed4952..6a24645d2 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -7,17 +7,18 @@ on: jobs: mypy: runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python: ["3.12"] steps: - name: Checkout uses: actions/checkout@v5 + - name: Extract max Python version from classifiers + run: | + classifiers=$(yq .project.classifiers pyproject.toml -oy | grep --only-matching --perl-regexp '(?<=Python :: )(\d\.\d+)') + max_version=$(echo "$classifiers" | sort -V | tail -1) + echo "max_python_version=$max_version" >> $GITHUB_ENV - uses: actions/setup-python@v6 with: - python-version: ${{ matrix.python }} + python-version: ${{ env.max_python_version }} - name: Install mypy run: | pip install mypy From 40c895faad3909459f29a0d843ba02a3b7ec4937 Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Fri, 31 Oct 2025 13:47:37 +0100 Subject: [PATCH 10/12] fix pre-commit --- .pre-commit-config.yaml | 13 +++++++------ ci/scripts/min-deps.py | 2 +- pyproject.toml | 3 ++- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 95a28b283..87b20db4c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -49,15 +49,16 @@ repos: rev: v1.18.2 hooks: - id: mypy - args: [--config-file=pyproject.toml, .] + args: [--config-file=pyproject.toml, --tb, .] pass_filenames: false additional_dependencies: - - pandas - - numpy - - scipy + - array-api-compat - h5py + - legacy-api-wrap - natsort + - numpy - packaging - - array_api_compat - - legacy-api-wrap + - pandas + - scipy + - types-docutils - zarr diff --git a/ci/scripts/min-deps.py b/ci/scripts/min-deps.py index 27051cf8c..54c2e757e 100755 --- a/ci/scripts/min-deps.py +++ b/ci/scripts/min-deps.py @@ -63,7 +63,7 @@ def extract_min_deps( dependencies = deque(dependencies) # We'll be mutating this project_name = pyproject["project"]["name"] - deps = {} + deps: dict[str, Requirement] = {} while len(dependencies) > 0: req = dependencies.pop() diff --git a/pyproject.toml b/pyproject.toml index 8e3509357..2580be42f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -271,8 +271,9 @@ exclude = [ '^src/testing/anndata/__init__\.py$', '^src/testing/anndata/_doctest\.py$', ] - +explicit_package_bases = true ignore_missing_imports = true +mypy_path = [ '$MYPY_CONFIG_FILE_DIR/src' ] [[tool.mypy.overrides]] module = [ "anndata/*" ] From b067c8e9cb1c3009ca74600f9cc27f15eea5a548 Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Fri, 31 Oct 2025 13:48:38 +0100 Subject: [PATCH 11/12] use git mypy as instructed --- .github/workflows/mypy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 6a24645d2..213c64760 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -21,7 +21,7 @@ jobs: python-version: ${{ env.max_python_version }} - name: Install mypy run: | - pip install mypy + pip install 'mypy @ https://github.com/python/mypy.git' - name: Run mypy run: | mypy --show-traceback src From aa1c237773f703960de0e0a44e75208bd389fc2f Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Fri, 31 Oct 2025 13:49:19 +0100 Subject: [PATCH 12/12] oops --- .github/workflows/mypy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 213c64760..469f1a85b 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -21,7 +21,7 @@ jobs: python-version: ${{ env.max_python_version }} - name: Install mypy run: | - pip install 'mypy @ https://github.com/python/mypy.git' + pip install 'mypy @ git+https://github.com/python/mypy.git' - name: Run mypy run: | mypy --show-traceback src