Repository navigation
GH-55263: [Python][Annotations] Add internal types and core data structures #55264
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9404659
5f7cdc8
610c48d
7bd5d7c
2f1a0ba
42ab12d
675ae64
14b924b
4ec84e4
235c90d
046fd11
0454fb9
e6bc69b
a33f327
ca9441c
dbc4e40
d2cc882
dce40ae
f278750
ca7c54e
c032596
3df0567
c990f32
cffdf24
bb8681e
376407d
bc27123
ddd9cb4
92250c5
9636e5c
4abbecc
325ed1d
26fa0e0
7d7a9fd
12ea05f
25a866b
94021e0
ae3cad1
ca5eb2e
d18e949
cf32c2e
516e8a9
401c3c9
368a6dc
91c3f0b
1589c4c
f6892cd
a1163e2
e51b18e
d297339
9dcdac6
2f49f15
3301e77
bf4a592
a9cdd20
905aa33
a94a96e
319b5c5
b642a3b
4b7dc90
41cc576
82f4e53
79a8f15
046f15f
5caf254
c093920
cce4648
61c7c26
6ed844e
6a4e614
da3465a
86fe422
5ea9439
42927be
724d480
1335885
bb913a8
17e3a08
e054197
4be907a
2d3f85c
6ae817e
ff91bcf
c7d1d97
d027724
9fc46cd
a8cc1ae
96ba7b9
0e446fc
faf04a9
4a899eb
91ae655
d2462bd
7f59731
7c1d685
dce7c2a
3675813
8e26b41
08bccc8
ceaf8c8
248bb04
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -105,15 +105,41 @@ Type Checking | |
| ============= | ||
|
|
||
| PyArrow provides type stubs (``*.pyi`` files) for static type checking. These | ||
| stubs are located in the ``pyarrow-stubs/`` directory and are automatically | ||
| included in the distributed wheel packages. | ||
| stubs are located in the ``pyarrow-stubs/`` directory, which is laid out as a | ||
| :pep:`561` stub-only package for ``pyarrow``, so type checkers and IDEs pick | ||
| it up from the ``arrow/python`` directory. The stubs aren't included in the | ||
| distributed wheel packages until they are complete (see | ||
| `GH-49831 <https://github.com/apache/arrow/issues/49831>`_). | ||
|
|
||
| Running Type Checkers | ||
| --------------------- | ||
|
|
||
| We support multiple type checkers. Their configurations are in | ||
| ``pyproject.toml``. | ||
|
|
||
| The easiest way to run all of them is with ``pre-commit``, which installs | ||
| the type checkers and stub packages pinned in ``requirements-stubs.txt`` (with | ||
| uv). This also lints and formats the stubs with ruff: | ||
|
|
||
| .. code-block:: | ||
|
|
||
| $ pre-commit run --hook-stage manual python-stubs --all-files | ||
|
|
||
| The type checkers only run on request, while ruff also runs on commit. The | ||
| Lint CI job runs all of them as well. CI also runs the type checkers, the type | ||
| stub tests and stubtest (see below) against the PyArrow it builds, with | ||
| ``ci/scripts/python_test_type_annotations.sh``. | ||
|
|
||
| To run a type checker directly instead, install the pinned type checkers and | ||
| stub packages in your PyArrow development environment: | ||
|
|
||
| .. code-block:: | ||
|
|
||
| $ pip install -r requirements-stubs.txt | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have I also had to update mypy settings in VSCode as the Workplace imports full arrow folder but mypy should take arrow/python folder as the cwd. Worth adding that in for setting up dev env. Happy to do that. |
||
|
|
||
| Checking the PyArrow tests also needs their dependencies from | ||
| ``requirements-test.txt``. | ||
|
|
||
| **mypy** | ||
|
|
||
| To run mypy on the PyArrow codebase: | ||
|
|
@@ -124,6 +150,12 @@ To run mypy on the PyArrow codebase: | |
| $ mypy | ||
|
|
||
| The mypy configuration is in the ``[tool.mypy]`` section of ``pyproject.toml``. | ||
| mypy only uses the stubs when it checks them too, so pass ``pyarrow-stubs`` | ||
| along with any other files you check: | ||
|
|
||
| .. code-block:: | ||
|
|
||
| $ mypy pyarrow-stubs pyarrow/tests/test_array.py | ||
|
Comment on lines
+153
to
+158
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is not what I see locally. Running eg |
||
|
|
||
| **pyright** | ||
|
|
||
|
|
@@ -145,6 +177,20 @@ To run ty (note: currently only partially configured): | |
| $ cd arrow/python | ||
| $ ty check | ||
|
|
||
| **stubtest** | ||
|
|
||
| mypy's stubtest compares the stubs with the signatures of the installed | ||
| PyArrow. Run it with: | ||
|
|
||
| .. code-block:: | ||
|
|
||
| $ cd arrow/python | ||
| $ python scripts/run_stubtest.py | ||
|
|
||
| Stubs that aren't written yet are skipped. The known mismatches are listed in | ||
| ``pyarrow-stubs-tests/stubtest_allowlist.txt``; remove the entries of the | ||
| mismatches you fix, as stubtest fails on unused entries. | ||
|
|
||
| Maintaining Type Stubs | ||
| ----------------------- | ||
|
|
||
|
|
@@ -169,6 +215,44 @@ in downstream projects and for users' IDEs. | |
| Note: ``py.typed`` marker file in the ``pyarrow/`` directory indicates to type | ||
| checkers that PyArrow supports type checking according to :pep:`561`. | ||
|
|
||
| Testing Type Stubs | ||
| ------------------ | ||
|
|
||
| The tests in ``pyarrow-stubs-tests/`` pin down the annotated types of public | ||
| APIs. Each check pairs :func:`typing.assert_type`, which every type checker | ||
| verifies statically, with a runtime ``isinstance`` check: | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| from typing import TYPE_CHECKING | ||
|
|
||
| import pyarrow as pa | ||
|
|
||
| from _check import assert_type, check | ||
|
|
||
| def test_list_type() -> None: | ||
| lst = pa.list_(pa.int32()) | ||
| check(assert_type(lst, pa.ListType), pa.ListType) | ||
| check(assert_type(lst.value_type, pa.DataType), pa.DataType) | ||
|
|
||
| if TYPE_CHECKING: | ||
| pa.list_(1) # type: ignore[call-overload] | ||
|
|
||
| The type checkers check these files as part of the regular runs above, and | ||
| pytest runs them against the installed PyArrow to make sure the stubs agree | ||
| with what PyArrow actually returns: | ||
|
|
||
| .. code-block:: | ||
|
|
||
| $ cd arrow/python | ||
| $ pytest pyarrow-stubs-tests | ||
|
|
||
| Code under ``if TYPE_CHECKING:`` is never executed. It holds the usage that | ||
| should fail type checking, which is asserted with a | ||
| ``# type: ignore[<mypy code>]`` comment: unused ignores are reported as errors | ||
| in these tests, so the test fails once the usage is no longer rejected. ty | ||
| doesn't honor mypy error codes, so it doesn't check invalid usage. | ||
|
|
||
| Doctest | ||
| ======= | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| # Licensed to the Apache Software Foundation (ASF) under one | ||
| # or more contributor license agreements. See the NOTICE file | ||
| # distributed with this work for additional information | ||
| # regarding copyright ownership. The ASF licenses this file | ||
| # to you under the Apache License, Version 2.0 (the | ||
| # "License"); you may not use this file except in compliance | ||
| # with the License. You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, | ||
| # software distributed under the License is distributed on an | ||
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| # KIND, either express or implied. See the License for the | ||
| # specific language governing permissions and limitations | ||
| # under the License. | ||
|
|
||
| """Helpers for the type annotation tests in this directory. | ||
|
|
||
| Each test pairs a static and a runtime check of the same expression:: | ||
|
|
||
| check(assert_type(pa.list_(pa.int32()), pa.ListType), pa.ListType) | ||
|
|
||
| ``assert_type`` is verified by every type checker we run in CI (mypy, | ||
| pyright and ty) and is a no-op at runtime. ``check`` runs under pytest and | ||
| verifies that the value really is an instance of the given class, so the | ||
| stubs can't drift away from what pyarrow actually returns. | ||
|
|
||
| Code that type checkers should *reject* goes under ``if TYPE_CHECKING:`` | ||
| with a ``# type: ignore[<mypy code>]`` comment. The block never runs, and | ||
| mypy and pyright are configured to report unused ignores in these tests, so | ||
| the test fails if the usage stops being an error. | ||
| """ | ||
|
|
||
| from typing import Any, TypeVar, assert_type | ||
|
|
||
| __all__ = ["assert_type", "check"] | ||
|
|
||
| _T = TypeVar("_T") | ||
|
|
||
|
|
||
| def check(actual: _T, klass: type[Any] | tuple[type[Any], ...]) -> _T: | ||
| """Assert that ``actual`` is an instance of ``klass`` and return it.""" | ||
| if not isinstance(actual, klass): | ||
| raise AssertionError( | ||
| f"Expected an instance of {klass}, got {type(actual)}: {actual!r}" | ||
| ) | ||
| return actual |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # Licensed to the Apache Software Foundation (ASF) under one | ||
| # or more contributor license agreements. See the NOTICE file | ||
| # distributed with this work for additional information | ||
| # regarding copyright ownership. The ASF licenses this file | ||
| # to you under the Apache License, Version 2.0 (the | ||
| # "License"); you may not use this file except in compliance | ||
| # with the License. You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, | ||
| # software distributed under the License is distributed on an | ||
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| # KIND, either express or implied. See the License for the | ||
| # specific language governing permissions and limitations | ||
| # under the License. | ||
|
|
||
| import sys | ||
| import types | ||
|
|
||
| import pyarrow as pa | ||
|
|
||
|
|
||
| def _stub_only_type(name: str) -> type[pa.DataType]: | ||
| # The factories of the stub-only DataType subclasses (Int32Type, ...) | ||
| # return plain DataType instances at runtime. | ||
| if name.endswith("Type") and not name.startswith("_"): | ||
| return pa.DataType | ||
| raise AttributeError(name) | ||
|
|
||
|
|
||
| # pyarrow._stubs_typing only exists in the type stubs. Provide it at runtime, | ||
| # so that the tests can import the stub-only types without a TYPE_CHECKING | ||
| # block. | ||
| _stubs_typing = types.ModuleType("pyarrow._stubs_typing") | ||
| setattr(_stubs_typing, "__getattr__", _stub_only_type) | ||
| sys.modules["pyarrow._stubs_typing"] = _stubs_typing |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| # Licensed to the Apache Software Foundation (ASF) under one | ||
| # or more contributor license agreements. See the NOTICE file | ||
| # distributed with this work for additional information | ||
| # regarding copyright ownership. The ASF licenses this file | ||
| # to you under the Apache License, Version 2.0 (the | ||
| # "License"); you may not use this file except in compliance | ||
| # with the License. You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, | ||
| # software distributed under the License is distributed on an | ||
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| # KIND, either express or implied. See the License for the | ||
| # specific language governing permissions and limitations | ||
| # under the License. | ||
|
|
||
| # Mismatches between the type stubs and the runtime that stubtest skips, one | ||
| # regex per line; see scripts/run_stubtest.py. Unused entries fail stubtest, | ||
| # so remove the entries of the mismatches you fix. Print the entries for the | ||
| # current mismatches with | ||
| # python scripts/run_stubtest.py --generate-allowlist | ||
|
|
||
| # Stub-only modules. The pyarrow.__lib modules are checked against | ||
| # pyarrow.lib, which has no __all__. | ||
| pyarrow\._stubs_typing | ||
| pyarrow\.__lib\.[a-z]+\.__all__ | ||
|
|
||
| # The __init__ of Cython extension types takes (*args, **kwargs) at runtime. | ||
| pyarrow\.__lib\.[a-z]+\.[A-Za-z_]+\.__init__ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest also unsetting
CONDA_PREFIXif we are doing unsetting. I have hit this issue when accidentally having conda env (from am mamba testing) active.