Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/development/ADRs/next/0012-GridTools_Cpp_OTF_Steps.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ tags: [backend, cpp, gridtools, bindings, otf]
- **Updated**: 2026-09-15

> [!NOTE]
> The entry points named below have since been renamed or moved: `program_processors.formatters.gtfn.format_sourcecode` is now `format_cpp`; `program_processors.codegens.gtfn_modules.translate_program` is now the `GTFNTranslationStep` class in `program_processors.codegens.gtfn.gtfn_module`; `otf.binding.pybind.bind_source` is now `otf.binding.nanobind.create_bindings` (nanobind replaced pybind11); and `program_processors.runners.gtfn_cpu.run_gtfn` is now `program_processors.runners.gtfn.run_gtfn`. `otf.step_types` was merged into `otf.stages`, `otf.workflow.Step` is now the `otf.workflow.Workflow` protocol, and `processor_interface` was split up — `ProgramFormatter` lives in `program_processors.program_formatter`. The decision — building the GTFN backend out of composable OTF steps — is unchanged.
> The entry points named below have since been renamed or moved: `program_processors.formatters.gtfn.format_sourcecode` is now `format_cpp`; `program_processors.codegens.gtfn_modules.translate_program` is now the `GTFNTranslationStep` class in `program_processors.codegens.gtfn.gtfn_module`; `otf.binding.pybind.bind_source` is now `otf.binding.nanobind.create_bindings` (nanobind replaced pybind11); and `program_processors.runners.gtfn_cpu.run_gtfn` is now `program_processors.runners.gtfn.run_gtfn`. `otf.step_types` was merged into `otf.stages`, `otf.workflow.Step` is now the `otf.workflow.Workflow` protocol, and `processor_interface` was split up — `ProgramFormatter` lives in `program_processors.program_formatter`. The decision — building the GTFN backend out of composable OTF steps — is unchanged. `SourceCodeSpec` no longer carries formatting options, and generated code is no longer auto-formatted in `gt4py.next`.

## Context

Expand Down
7 changes: 4 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ profiling = [
]
scripts = ["pyyaml>=6.0.1", "typer>=0.16.0", "packaging"]
test = [
'black>=25.11',
'clang-format>=18.1',
'factory-boy>=3.3.3',
'hypothesis>=6.0.0',
'nbmake>=1.4.6',
Expand Down Expand Up @@ -91,7 +93,6 @@ classifiers = [
dependencies = [
'attrs>=21.3',
'array-api-compat>=1.13',
'black>=25.11',
'boltons>=20.1',
'cached-property>=1.5.1',
'click>=8.0.0',
Expand Down Expand Up @@ -142,7 +143,7 @@ readme = 'README.md'
requires-python = '>=3.12, <3.15, !=3.13.10, !=3.14.1'

[project.optional-dependencies]
cartesian = ['gt4py[jax,standard,testing]']
cartesian = ['black>=25.11', 'clang-format>=18.1', 'gt4py[jax,standard,testing]']
cuda12 = ['cupy-cuda12x>=12.0']
cuda13 = ['cupy-cuda13x>=14.0']
jax = [
Expand All @@ -154,7 +155,7 @@ jax-cuda13 = ['jax[cuda13-local]>=0.7.0', 'gt4py[cuda13]']
next = ['gt4py[jax,standard,testing]']
rocm6 = ['cupy>=13.4.1,<14.0']
rocm7 = ['cupy-rocm-7-0>=14.0']
standard = ['clang-format>=18.1', 'scipy>=1.16.1']
standard = ['scipy>=1.16.1']
testing = ['hypothesis>=6.93', 'pytest>=7.0']

[project.urls]
Expand Down
6 changes: 3 additions & 3 deletions src/gt4py/cartesian/backend/dace_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
from gt4py.cartesian.gtc.passes.oir_optimizations.utils import compute_fields_extents
from gt4py.cartesian.gtc.passes.oir_pipeline import DefaultPipeline
from gt4py.cartesian.utils import shash
from gt4py.eve import codegen
from gt4py.eve import formatting
from gt4py.eve.codegen import MakoTemplate as as_mako
from gt4py.storage.cartesian import layout, layout_registry

Expand Down Expand Up @@ -679,7 +679,7 @@ def apply(cls, builder: StencilBuilder, sdfg: SDFG) -> str:
"""

if builder.options.format_source:
generated_code = codegen.format_source("cpp", generated_code, style="LLVM")
generated_code = formatting.format_cpp_source(generated_code)

return generated_code

Expand Down Expand Up @@ -863,7 +863,7 @@ def generate_sdfg_bindings(self, sdfg: SDFG, module_name: str) -> str:
def apply(cls, sdfg: SDFG, module_name: str, *, backend: BaseDaceBackend) -> str:
generated_code = cls(backend).generate_sdfg_bindings(sdfg, module_name)
if backend.builder.options.format_source:
generated_code = codegen.format_source("cpp", generated_code, style="LLVM")
generated_code = formatting.format_cpp_source(generated_code)
return generated_code


Expand Down
4 changes: 2 additions & 2 deletions src/gt4py/cartesian/backend/debug_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from gt4py.cartesian.gtc.debug.debug_codegen import DebugCodeGen
from gt4py.cartesian.gtc.gtir_to_oir import GTIRToOIR
from gt4py.cartesian.gtc.passes import oir_optimizations
from gt4py.eve import codegen
from gt4py.eve import formatting
from gt4py.storage import layout
from gt4py.storage.cartesian import layout_registry

Expand Down Expand Up @@ -45,7 +45,7 @@ def _generate_computation(self) -> dict[str, str | dict]:
source_code = DebugCodeGen().visit(oir)

if self.builder.options.format_source:
source_code = codegen.format_source("python", source_code)
source_code = formatting.format_python_source(source_code)

caching = self.builder.caching
computation_name = f"{caching.module_prefix}computation{caching.module_postfix}.py"
Expand Down
4 changes: 2 additions & 2 deletions src/gt4py/cartesian/backend/gtcpp_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from gt4py.cartesian.gtc.gtcpp.oir_to_gtcpp import OIRToGTCpp
from gt4py.cartesian.gtc.gtir_to_oir import GTIRToOIR
from gt4py.cartesian.gtc.passes.oir_pipeline import DefaultPipeline
from gt4py.eve import codegen
from gt4py.eve import codegen, formatting
from gt4py.storage.cartesian import layout, layout_registry


Expand Down Expand Up @@ -117,7 +117,7 @@ def visit_Program(self, node: gtcpp.Program, **kwargs):
def apply(cls, root, *, module_name="stencil", **kwargs) -> str:
generated_code = cls(kwargs.get("backend")).visit(root, module_name=module_name, **kwargs)
if kwargs.get("format_source", True):
generated_code = codegen.format_source("cpp", generated_code, style="LLVM")
generated_code = formatting.format_cpp_source(generated_code)

return generated_code

Expand Down
8 changes: 4 additions & 4 deletions src/gt4py/cartesian/backend/module_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from gt4py.cartesian.gtc.passes.oir_access_kinds import compute_access_kinds
from gt4py.cartesian.gtc.passes.oir_optimizations.utils import compute_fields_extents
from gt4py.cartesian.gtc.utils import dimension_flags_to_names
from gt4py.eve import codegen
from gt4py.eve import formatting


if TYPE_CHECKING:
Expand Down Expand Up @@ -150,8 +150,8 @@ def __call__(self, args_data: ModuleData) -> str:
implementation=self.generate_implementation(),
)
if self.builder.options.as_dict()["format_source"]:
module_source = codegen.format_source(
"python", module_source, line_length=self.SOURCE_LINE_LENGTH
module_source = formatting.format_python_source(
module_source, line_length=self.SOURCE_LINE_LENGTH
)

return module_source
Expand Down Expand Up @@ -208,7 +208,7 @@ def generate_sources(self) -> dict[str, str]:
"""
if self.builder.gtir.sources is not None:
return {
key: codegen.format_source("python", value, line_length=self.SOURCE_LINE_LENGTH)
key: formatting.format_python_source(value, line_length=self.SOURCE_LINE_LENGTH)
for key, value in self.builder.gtir.sources.items()
}
return {}
Expand Down
4 changes: 2 additions & 2 deletions src/gt4py/cartesian/backend/numpy_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from gt4py.cartesian.gtc.gtir_to_oir import GTIRToOIR
from gt4py.cartesian.gtc.numpy import npir
from gt4py.cartesian.gtc.passes import oir_optimizations as oir_opt
from gt4py.eve import codegen
from gt4py.eve import formatting
from gt4py.storage import layout
from gt4py.storage.cartesian import layout_registry

Expand Down Expand Up @@ -44,7 +44,7 @@ def generate_computation(self) -> dict[str, str | dict]:
source = numpy.NpirCodegen.apply(self.npir, ignore_np_errstate=ignore_np_errstate)

if self.builder.options.format_source:
source = codegen.format_source("python", source)
source = formatting.format_python_source(source)

caching = self.builder.caching
computation_name = f"{caching.module_prefix}computation{caching.module_postfix}.py"
Expand Down
4 changes: 2 additions & 2 deletions src/gt4py/cartesian/gtc/gtcpp/gtcpp_codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
UnaryOperator,
)
from gt4py.cartesian.gtc.gtcpp import gtcpp
from gt4py.eve import codegen
from gt4py.eve import codegen, formatting
from gt4py.eve.codegen import FormatTemplate as as_fmt, MakoTemplate as as_mako
from gt4py.eve.concepts import LeafNode

Expand Down Expand Up @@ -326,6 +326,6 @@ def apply(cls, root: LeafNode, **kwargs: Any) -> str:
raise TypeError("apply() missing 1 required keyword-only argument: 'gt_backend_t'")
generated_code = super().apply(root, offset_limit=_offset_limit(root), **kwargs)
if kwargs.get("format_source", True):
generated_code = codegen.format_source("cpp", generated_code, style="LLVM")
generated_code = formatting.format_cpp_source(generated_code)

return generated_code
2 changes: 1 addition & 1 deletion src/gt4py/eve/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
The internal dependencies between modules are the following (each module depends
on some of the previous ones):

0. xtyping
0. formatting, xtyping
1. exceptions, pattern_matching, type_definitions
2. utils
3. type_validation
Expand Down
128 changes: 1 addition & 127 deletions src/gt4py/eve/codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,14 @@
import collections.abc
import contextlib
import inspect
import os
import re
import string
import subprocess
import sys
import textwrap
import types
from collections.abc import Callable, Collection, Iterator, Mapping, Sequence
from collections.abc import Collection, Iterator, Mapping, Sequence
Comment thread
Copilot marked this conversation as resolved.
from typing import Any, ClassVar, Optional, TypeVar, Union, overload

import black
import jinja2
from mako import template as mako_tpl
from typing_extensions import Protocol, runtime_checkable
Expand All @@ -34,24 +31,6 @@
from .visitors import NodeVisitor


SourceFormatter = Callable[[str], str]

SOURCE_FORMATTERS: dict[str, SourceFormatter] = {}
"""Global dict storing registered formatters."""


class FormatterNameError(exceptions.EveRuntimeError):
"""Run-time error registering a new source code formatter."""

...


class FormattingError(exceptions.EveRuntimeError):
"""Run-time error applying a source code formatter."""

...


class TemplateDefinitionError(exceptions.EveTypeError):
"""Template definition error."""

Expand All @@ -64,111 +43,6 @@ class TemplateRenderingError(exceptions.EveRuntimeError):
...


def register_formatter(language: str) -> Callable[[SourceFormatter], SourceFormatter]:
"""Register source code formatters for specific languages (decorator)."""

def _decorator(formatter: SourceFormatter) -> SourceFormatter:
if language in SOURCE_FORMATTERS:
raise FormatterNameError(f"Another formatter for language '{language}' already exists")

assert callable(formatter)
SOURCE_FORMATTERS[language] = formatter

return formatter

return _decorator


@register_formatter("python")
def format_python_source(
source: str,
*,
line_length: int = 100,
python_versions: Optional[set[str]] = None,
string_normalization: bool = True,
) -> str:
"""Format Python source code using black formatter."""
python_versions = python_versions or {f"{sys.version_info.major}{sys.version_info.minor}"}
target_versions = set(black.TargetVersion[f"PY{v.replace('.', '')}"] for v in python_versions) # type: ignore[attr-defined] # .TargetVersion implicitly exported

formatted_source = black.format_str(
source,
mode=black.FileMode(
line_length=line_length,
target_versions=target_versions,
string_normalization=string_normalization,
),
)
assert isinstance(formatted_source, str)

return formatted_source


def _get_clang_format() -> Optional[str]:
"""Return the clang-format executable, or None if not available."""
executable = os.getenv("CLANG_FORMAT_EXECUTABLE", "clang-format")
try:
assert isinstance(executable, str)
if subprocess.run([executable, "--version"], capture_output=True).returncode != 0:
return None
except Exception:
return None

return executable


_CLANG_FORMAT_EXECUTABLE = _get_clang_format()


if _CLANG_FORMAT_EXECUTABLE is not None:

@register_formatter("cpp")
def format_cpp_source(
source: str,
*,
style: Optional[str] = None,
fallback_style: Optional[str] = None,
sort_includes: bool = False,
) -> str:
"""Format C++ source code using clang-format."""
assert isinstance(_CLANG_FORMAT_EXECUTABLE, str)
args = [_CLANG_FORMAT_EXECUTABLE, "--assume-filename=_gt4py_generated_file.cpp"]
if style:
args.append(f"--style={style}")
if fallback_style:
args.append(f"--fallback-style={style}")
if sort_includes:
args.append("--sort-includes")

try:
# use a timeout as clang-format used to deadlock on some sources
formatted_source = subprocess.run(
args, check=True, input=source, capture_output=True, text=True, timeout=3
).stdout
except subprocess.TimeoutExpired:
return source

assert isinstance(formatted_source, str)
return formatted_source


def format_source(language: str, source: str, *, skip_errors: bool = True, **kwargs: Any) -> str:
"""Format source code if a formatter exists for the specific language."""
formatter = SOURCE_FORMATTERS.get(language, None)
try:
if formatter:
return formatter(source, **kwargs)
else:
raise FormattingError(f"Missing formatter for '{language}' language")
except Exception as e:
if skip_errors:
return source
else:
raise FormattingError(
f"Something went wrong when trying to format '{language}' source code"
) from e


class Name:
"""Text formatter with different case styles for symbol names in source code."""

Expand Down
Loading
Loading