fix[next]: Reduce type ignores in client code#2484
fix[next]: Reduce type ignores in client code#2484DropD wants to merge 51 commits intoGridTools:mainfrom
Conversation
Radically reduce the amount of required type ignores in client code which uses the `from gt4py import next as gtx` pattern.
egparedes
left a comment
There was a problem hiding this comment.
Thanks for the PR. I just have a comment and a question
fbuiltin members in gt4py.next|
cscs-ci run default |
…void any output being written
|
cscs-ci run default |
There was a problem hiding this comment.
Pull request overview
This PR improves the static typing experience for client code using from gt4py import next as gtx, aiming to reduce type: ignore usage and add CI coverage for “typed client usability” scenarios.
Changes:
- Add a mypy plugin to treat
Dims[...]and*Dimsymbols as valid type-checking types and to blur scalar dtype precision in some cases. - Improve exported typing surface by refining decorator signatures (
program,field_operator,scan_operator) and enhancing builtin typings (notablywhere,astype) plus explicitfbuiltinsexports. - Add a dedicated typing test suite (
pytest-mypy-plugins) and wire it into nox + GitHub Actions.
Reviewed changes
Copilot reviewed 14 out of 15 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
uv.lock |
Updates locked dependencies and adds packages needed for typing-export checks (e.g., pytest-mypy-plugins, xarray). |
typing_tests/test_next.yaml |
Adds mypy-based “client usability” typing tests for decorators, dims, builtins, and exports. |
src/gt4py/next/type_system/mypy_plugin.py |
Introduces a mypy plugin to treat dimensions/dims as types and blur dtype precision. |
src/gt4py/next/program_processors/runners/dace/transformations/remove_scalar_copies.py |
Fixes docstring escaping and a spelling error. |
src/gt4py/next/program_processors/runners/dace/transformations/loop_blocking.py |
Fixes invalid escape sequences in docstring LaTeX. |
src/gt4py/next/iterator/embedded.py |
Removes some type ignores and adds comparison operator stubs for typing. |
src/gt4py/next/ffront/fbuiltins.py |
Makes exports explicit, adds overloads for builtins, and restructures math builtin registration. |
src/gt4py/next/ffront/decorator.py |
Broadens decorator typing from FunctionType to Callable and adjusts overload defaults. |
src/gt4py/next/embedded/nd_array_field.py |
Removes attr-defined ignores for builtins; adjusts typing around overloaded astype. |
src/gt4py/next/common.py |
Adds TYPE_CHECKING-only placeholder dim classes and expands Field protocol with comparison operators. |
src/gt4py/next/__init__.py |
Replaces star re-exports with explicit imports and an explicit __all__. |
src/gt4py/_core/definitions.py |
Adds a targeted mypy ignore related to new dtype-precision blurring behavior. |
pyproject.toml |
Enables the new mypy plugin and adds a dependency group for typing-export tests. |
noxfile.py |
Adds a nox session to run typing-export checks via pytest-mypy-plugins. |
.github/workflows/code-quality.yml |
Adds a CI job to run the typing-export checks. |
Comments suppressed due to low confidence (2)
.github/workflows/code-quality.yml:64
fromJson(...)is not a valid GitHub Actions expression function name (the built-in isfromJSON). As written, this step will fail to render the session name and the job will error before running nox; use the correct function name (or reuse the existingfromJSONpattern used earlier in this workflow).
- name: "Run typing checks on exported entities"
run: |
./noxfile.py -s 'test_typing_exports-${{ fromJson(needs.get-python-versions.outputs.python-versions) }}'
src/gt4py/next/ffront/fbuiltins.py:246
- The
whereoverload that returnsTupleacceptstrue_field: Tuple | Scalarandfalse_field: Tuple | Scalar, but the runtime implementation explicitly raisesValueErrorwhen only one side is a tuple. This makes the public typing contract disagree with actual behavior (and can let mypy accept code that will fail at runtime). Either narrow the tuple overload to require tuples on both sides, or extend the implementation to support broadcasting a scalar to a tuple (and update the error checks accordingly).
@overload # type: ignore[override] # this technically clashes with the superclass
def __call__( # type: ignore[overload-overlap] # without both overloads mypy raises false positives
self,
cond: CondT,
true_field: common.Field | core_defs.ScalarT,
false_field: common.Field | core_defs.ScalarT,
) -> common.Field: ...
@overload
def __call__(
self,
cond: CondT,
true_field: Tuple | core_defs.ScalarT,
false_field: Tuple | core_defs.ScalarT,
) -> Tuple: ...
def __call__(self, cond: CondT, true_field: FieldT1, false_field: FieldT2) -> _R: # type: ignore[misc] # supposedly this signature does not accept all the possible args allowed by the overloads ??
if isinstance(true_field, tuple) or isinstance(false_field, tuple):
if not (isinstance(true_field, tuple) and isinstance(false_field, tuple)):
raise ValueError(
# TODO(havogt) find a strategy to unify parsing and embedded error messages
f"Either both or none can be tuple in '{true_field=}' and '{false_field=}'."
)
if len(true_field) != len(false_field):
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| def foo( | ||
| mask: CellKField[bool], a: CKTuple[gtx.float64], b: CKTuple[gtx.float64] | ||
| ) -> CKTuple[T]: | ||
| return gtx.where(mask, b, 0.0) |
|
|
||
| def scan_operator_inner(definition: types.FunctionType) -> FieldOperator: | ||
| def scan_operator_inner(definition: Callable) -> FieldOperator: | ||
| assert isinstance(definition, types.FunctionType) |
.github/workflows/code-quality.yml
Outdated
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: ${{ fromJSON(needs.get-python-versions.outputs.python-versions) }} | ||
|
|
||
| - name: Install uv | ||
| uses: astral-sh/setup-uv@v6 | ||
| with: |
There was a problem hiding this comment.
We have updated the versions of these actions in the meantime. Make sure they are up to date also here.
src/gt4py/next/iterator/embedded.py
Outdated
| def __pow__(self, other: common.Field | core_defs.ScalarT) -> common.Field: | ||
| raise NotImplementedError() | ||
|
|
||
| def __lt__(self, other: common.Field | core_defs.ScalarT) -> common.Field[Any, bool]: |
There was a problem hiding this comment.
Just a question, is Any the right type hint for the domain part of the field? Shouldn't be something derived from Dims?
| "uint32", | ||
| "uint64", | ||
| "where", | ||
| ] |
There was a problem hiding this comment.
Shouldn't we assert here that all public names from builtins are explicitly exported?
src/gt4py/next/common.py
Outdated
| class DimA(Dimension): ... | ||
|
|
||
| @dataclasses.dataclass(frozen=True) | ||
| class DimB(Dimension): ... | ||
|
|
||
| @dataclasses.dataclass(frozen=True) | ||
| class DimC(Dimension): ... | ||
|
|
||
| @dataclasses.dataclass(frozen=True) | ||
| class DimD(Dimension): ... | ||
|
|
||
| @dataclasses.dataclass(frozen=True) | ||
| class AnyDim(Dimension): ... |
There was a problem hiding this comment.
I know it's just for the plugin, but since it's an implementation detail, I think it might be better to name them with an underscore preffix...
pyproject.toml
Outdated
| disallow_incomplete_defs = true | ||
| disallow_untyped_defs = true | ||
| exclude = ['^setup\.py$', 'build/.*$', 'ci/*.$', 'docs/.*$', 'tests/.*$'] | ||
| exclude = ['^setup\.py$', 'build/.*$', 'ci/*.$', 'docs/.*$', '^tests/.*$'] |
There was a problem hiding this comment.
Just a question: why only adding the start of line character to the tests patterns and not also to build, ci, docs..?
There was a problem hiding this comment.
I think this is a leftover from a merge
There was a problem hiding this comment.
Or from when I was envisioning typing_tests to be written in python modules. So they would be tested by mypy alongside gt4py code but not actually run.
Description
Partner-PR in icon4py: C2SM/icon4py#1096 (make sure this passes tests first)
Radically reduce the amount of required type ignores in client code which uses the
from gt4py import next as gtxpattern.Experimenting with this in icon4py reveals more ways in which gt4py blocks client code type checking, which have not been addressed in this PR so far:
Requirements