diff --git a/.github/workflows/code_quality.yml b/.github/workflows/code_quality.yml index 00848933..719e54ca 100644 --- a/.github/workflows/code_quality.yml +++ b/.github/workflows/code_quality.yml @@ -19,7 +19,7 @@ defaults: jobs: run_checks: - name: Run linting and formatting + name: Run linting, formatting and static type checker runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5b78d185..4fd64e4d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,6 +19,17 @@ repos: hooks: - id: cmake-format + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.17.1 + hooks: + - id: mypy + # Match all files under duckdb/ or _duckdb-stubs/, + # but not under duckdb/experimental/ or duckdb/query_graph/ + files: ^(duckdb/(?!experimental/|query_graph/)|_duckdb-stubs/) + args: ["--config-file", "pyproject.toml"] + language: system + entry: uv run mypy + - repo: local hooks: - id: post-checkout-submodules diff --git a/duckdb/polars_io.py b/duckdb/polars_io.py index 61ead5bc..2c075baf 100644 --- a/duckdb/polars_io.py +++ b/duckdb/polars_io.py @@ -277,8 +277,8 @@ def source_generator( for record_batch in iter(results.read_next_batch, None): if predicate is not None and duck_predicate is None: # We have a predicate, but did not manage to push it down, we fallback here - yield pl.from_arrow(record_batch).filter(predicate) # type: ignore[arg-type,misc] + yield pl.from_arrow(record_batch).filter(predicate) # type: ignore[arg-type,misc,unused-ignore] else: - yield pl.from_arrow(record_batch) # type: ignore[misc] + yield pl.from_arrow(record_batch) # type: ignore[misc,unused-ignore] return register_io_source(source_generator, schema=schema) diff --git a/pyproject.toml b/pyproject.toml index 52b63edc..2faea5e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -316,6 +316,7 @@ filterwarnings = [ [tool.mypy] packages = ["duckdb", "_duckdb"] +mypy_path = ["_duckdb-stubs"] strict = true warn_unreachable = true pretty = true @@ -323,13 +324,18 @@ python_version = "3.9" exclude = [ "duckdb/experimental/", # not checking the pyspark API "duckdb/query_graph/", # old and unmaintained (should probably remove) + "tests", + "scripts", + "sqlogic", ] [[tool.mypy.overrides]] module = [ "fsspec.*", + "numpy", "pandas", "polars", + "polars.io.plugins", "pyarrow.*", "torch", "tensorflow", diff --git a/tests/stubs/mypy.ini b/tests/stubs/mypy.ini deleted file mode 100644 index ff840b82..00000000 --- a/tests/stubs/mypy.ini +++ /dev/null @@ -1,14 +0,0 @@ -[mypy] -mypy_path = duckdb -[mypy-fsspec] -ignore_missing_imports = True -[mypy-pandas] -ignore_missing_imports = True -[mypy-polars] -ignore_missing_imports = True -[mypy-pyarrow] -ignore_missing_imports = True -[mypy-pyarrow.lib] -ignore_missing_imports = True -[mypy-torch] -ignore_missing_imports = True \ No newline at end of file diff --git a/tests/stubs/test_stubs.py b/tests/stubs/test_stubs.py deleted file mode 100644 index 96efed5d..00000000 --- a/tests/stubs/test_stubs.py +++ /dev/null @@ -1,25 +0,0 @@ -from pathlib import Path - -from mypy import stubtest - -MYPY_INI_PATH = Path(__file__).parent / "mypy.ini" - - -def test_generated_stubs(): - skip_stubs_errors = ["pybind11", "git_revision", "is inconsistent, metaclass differs"] - - options = stubtest.parse_options(["duckdb", "--mypy-config-file", MYPY_INI_PATH]) - stubtest.test_stubs(options) - - broken_stubs = [ - error.get_description() - for error in stubtest.test_module("duckdb") - if not any(skip in error.get_description() for skip in skip_stubs_errors) - ] - - if not broken_stubs: - return - print("Stubs must be updated, either add them to skip_stubs_errors or update __init__.pyi accordingly") - print(broken_stubs) - - assert not broken_stubs