Skip to content

fix[next]: Reduce type ignores in client code#2484

Open
DropD wants to merge 51 commits intoGridTools:mainfrom
DropD:explicit-fbuiltins-imports
Open

fix[next]: Reduce type ignores in client code#2484
DropD wants to merge 51 commits intoGridTools:mainfrom
DropD:explicit-fbuiltins-imports

Conversation

@DropD
Copy link
Contributor

@DropD DropD commented Feb 18, 2026

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 gtx pattern.

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

  • All fixes and/or new features come with corresponding tests.
  • Important design decisions have been documented in the appropriate ADR inside the docs/development/ADRs/ folder.

Radically reduce the amount of required type ignores in client code
which uses the `from gt4py import next as gtx` pattern.
@DropD DropD requested a review from egparedes February 18, 2026 10:42
Copy link
Contributor

@egparedes egparedes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. I just have a comment and a question

@DropD DropD changed the title fix[next]: Explicitly import fbuiltin members in gt4py.next fix[next]: Reduce type ignores in client code Feb 26, 2026
@DropD DropD linked an issue Feb 26, 2026 that may be closed by this pull request
@DropD
Copy link
Contributor Author

DropD commented Mar 9, 2026

cscs-ci run default

@DropD
Copy link
Contributor Author

DropD commented Mar 12, 2026

cscs-ci run default

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 *Dim symbols 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 (notably where, astype) plus explicit fbuiltins exports.
  • 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 is fromJSON). 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 existing fromJSON pattern 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 where overload that returns Tuple accepts true_field: Tuple | Scalar and false_field: Tuple | Scalar, but the runtime implementation explicitly raises ValueError when 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)
Copy link
Contributor

@egparedes egparedes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly questions. The only important issue remaining is the lack of the documentation in the mypy plugin. Other than that, it looks good to me. I have also requested a review from Copilot in case it finds something useful.

Comment on lines +48 to +57
- 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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have updated the versions of these actions in the meantime. Make sure they are up to date also here.

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]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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",
]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we assert here that all public names from builtins are explicitly exported?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we should

Comment on lines +115 to +127
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): ...
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/.*$']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a question: why only adding the start of line character to the tests patterns and not also to build, ci, docs..?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a leftover from a merge

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

next: dimension types are not valid types for static type checking purposes next: type hints for dsl decorators need to be improved.

3 participants