diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml new file mode 100644 index 000000000..469f1a85b --- /dev/null +++ b/.github/workflows/mypy.yml @@ -0,0 +1,27 @@ +name: mypy + +on: + push: + pull_request: + +jobs: + mypy: + runs-on: ubuntu-latest + + 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: ${{ env.max_python_version }} + - name: Install mypy + run: | + pip install 'mypy @ git+https://github.com/python/mypy.git' + - name: Run mypy + run: | + mypy --show-traceback src diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 80d3310fc..87b20db4c 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 @@ -39,3 +40,25 @@ repos: - id: codespell additional_dependencies: - tomli +# See https://github.com/scverse/anndata/pull/2174 +# 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, --tb, .] + pass_filenames: false + additional_dependencies: + - array-api-compat + - h5py + - legacy-api-wrap + - natsort + - numpy + - packaging + - 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 7ac7bcd7b..2580be42f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -265,3 +265,46 @@ 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$', +] +explicit_package_bases = true +ignore_missing_imports = true +mypy_path = [ '$MYPY_CONFIG_FILE_DIR/src' ] + +[[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", +]