Skip to content

Commit 77a205b

Browse files
egparedestehrengruber
authored andcommitted
[style]: updates to coding guidelines and fixes for all QA errors after migrating to ruff
1 parent 4c8f706 commit 77a205b

File tree

89 files changed

+363
-364
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+363
-364
lines changed

CODING_GUIDELINES.md

+17-8
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ We follow the [Google Python Style Guide][google-style-guide] with a few minor c
2121

2222
We deviate from the [Google Python Style Guide][google-style-guide] only in the following points:
2323

24-
- We use [`flake8`][flake8] with some plugins instead of [`pylint`][pylint].
25-
- We use [`black`][black] and [`isort`][isort] for source code and imports formatting, which may work differently than indicated by the guidelines in section [_3. Python Style Rules_](https://google.github.io/styleguide/pyguide.html#3-python-style-rules). For example, maximum line length is set to 100 instead of 79 (although docstring lines should still be limited to 79).
24+
- We use [`ruff-linter`][ruff-linter] instead of [`pylint`][pylint].
25+
- We use [`ruff-formatter`][ruff-formatter] for source code and imports formatting, which may work differently than indicated by the guidelines in section [_3. Python Style Rules_](https://google.github.io/styleguide/pyguide.html#3-python-style-rules). For example, maximum line length is set to 100 instead of 79 (although docstring lines should still be limited to 79).
2626
- According to subsection [_2.19 Power Features_](https://google.github.io/styleguide/pyguide.html#219-power-features), direct use of _power features_ (e.g. custom metaclasses, import hacks, reflection) should be avoided, but standard library classes that internally use these power features are accepted. Following the same spirit, we allow the use of power features in infrastructure code with similar functionality and scope as the Python standard library.
2727
- According to subsection [_3.19.12 Imports For Typing_](https://google.github.io/styleguide/pyguide.html#31912-imports-for-typing), symbols from `typing` and `collections.abc` modules used in type annotations _"can be imported directly to keep common annotations concise and match standard typing practices"_. Following the same spirit, we allow symbols to be imported directly from third-party or internal modules when they only contain a collection of frequently used typying definitions.
2828

@@ -107,7 +107,7 @@ In general, you should structure new Python modules in the following way:
107107
1. _shebang_ line: `#! /usr/bin/env python3` (only for **executable scripts**!).
108108
2. License header (see `LICENSE_HEADER.txt`).
109109
3. Module docstring.
110-
4. Imports, alphabetically ordered within each block (fixed automatically by `isort`):
110+
4. Imports, alphabetically ordered within each block (fixed automatically by `ruff-formatter`):
111111
1. Block of imports from the standard library.
112112
2. Block of imports from general third party libraries using standard shortcuts when customary (e.g. `numpy as np`).
113113
3. Block of imports from specific modules of the project.
@@ -126,10 +126,17 @@ Consider configuration files as another type of source code and apply the same c
126126

127127
### Ignoring QA errors
128128

129-
You may occasionally need to disable checks from _quality assurance_ (QA) tools (e.g. linters, type checkers, etc.) on specific lines as some tool might not be able to fully understand why a certain piece of code is needed. This is usually done with special comments, e.g. `# type: ignore`. However, you should **only** ignore QA errors when you fully understand their source and rewriting your code to pass QA checks would make it less readable. Additionally, you should add a brief comment for future reference, e.g.:
129+
You may occasionally need to disable checks from _quality assurance_ (QA) tools (e.g. linters, type checkers, etc.) on specific lines as some tool might not be able to fully understand why a certain piece of code is needed. This is usually done with special comments, e.g. `# noqa: F401`, `# type: ignore`. However, you should **only** ignore QA errors when you fully understand their source and rewriting your code to pass QA checks would make it less readable. Additionally, you should add a short descriptive code if possible (check [ruff rules][ruff-rules] and [mypy error codes][mypy-error-codes] for reference):
130130

131131
```python
132-
f = lambda: 'empty' # noqa: E731 # assign lambda expression for testing
132+
f = lambda: 'empty' # noqa: E731 [lambda-assignment]
133+
```
134+
135+
and, if needed, a brief comment for future reference:
136+
137+
```python
138+
...
139+
return undeclared_symbol # noqa: F821 [undefined-name] on purpose to trigger black-magic
133140
```
134141

135142
## Testing
@@ -213,13 +220,15 @@ https://testandcode.com/116
213220

214221
<!-- Reference links -->
215222

216-
[black]: https://black.readthedocs.io/en/stable/
217223
[doctest]: https://docs.python.org/3/library/doctest.html
218-
[flake8]: https://flake8.pycqa.org/
219224
[google-style-guide]: https://google.github.io/styleguide/pyguide.html
220-
[isort]: https://pycqa.github.io/isort/
225+
[mypy]: https://mypy.readthedocs.io/
226+
[mypy-error-codes]: https://mypy.readthedocs.io/en/stable/error_code_list.html
221227
[pre-commit]: https://pre-commit.com/
222228
[pylint]: https://pylint.pycqa.org/
229+
[ruff-formatter]: https://docs.astral.sh/ruff/formatter/
230+
[ruff-linter]: https://docs.astral.sh/ruff/linter/
231+
[ruff-rules]: https://docs.astral.sh/ruff/rules/
223232
[sphinx]: https://www.sphinx-doc.org
224233
[sphinx-autodoc]: https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html
225234
[sphinx-napoleon]: https://sphinxcontrib-napoleon.readthedocs.io/en/latest/index.html#

src/gt4py/cartesian/__gtscript__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
import sys
2222

23-
from gt4py.cartesian.gtscript import *
23+
from gt4py.cartesian.gtscript import * # noqa: F403 [undefined-local-with-import-star]
2424

2525

2626
sys.modules["__gtscript__"] = sys.modules["gt4py.cartesian.__gtscript__"]

src/gt4py/cartesian/__init__.py

+17
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,20 @@
3030
type_hints,
3131
)
3232
from .stencil_object import StencilObject
33+
34+
35+
__all__ = [
36+
"typing",
37+
"caching",
38+
"cli",
39+
"config",
40+
"definitions",
41+
"frontend",
42+
"gt_cache_manager",
43+
"gtscript",
44+
"loader",
45+
"stencil_builder",
46+
"stencil_object",
47+
"type_hints",
48+
"StencilObject",
49+
]

src/gt4py/cartesian/backend/base.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,9 @@ def check_options(self, options: gt_definitions.BuildOptions) -> None:
271271
unknown_options = set(options.backend_opts.keys()) - set(self.options.keys())
272272
if unknown_options:
273273
warnings.warn(
274-
f"Unknown options '{unknown_options}' for backend '{self.name}'", RuntimeWarning
274+
f"Unknown options '{unknown_options}' for backend '{self.name}'",
275+
RuntimeWarning,
276+
stacklevel=2,
275277
)
276278

277279
def make_module(

src/gt4py/cartesian/backend/dace_backend.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ def _postprocess_dace_code(code_objects, is_gpu, builder):
516516
break
517517
for i, line in enumerate(lines):
518518
if "#include <dace/dace.h>" in line:
519-
cuda_code = [co.clean_code for co in code_objects if co.title == "CUDA"][0]
519+
cuda_code = next(co.clean_code for co in code_objects if co.title == "CUDA")
520520
lines = lines[0:i] + cuda_code.split("\n") + lines[i + 1 :]
521521
break
522522

src/gt4py/cartesian/backend/pyext_builder.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ def build_pybind_ext(
194194
libraries: Optional[List[str]] = None,
195195
extra_compile_args: Optional[Union[List[str], Dict[str, List[str]]]] = None,
196196
extra_link_args: Optional[List[str]] = None,
197-
build_ext_class: Type = None,
197+
build_ext_class: Optional[Type] = None,
198198
verbose: bool = False,
199199
clean: bool = False,
200200
) -> Tuple[str, str]: ...
@@ -211,7 +211,7 @@ def build_pybind_ext(
211211
libraries: Optional[List[str]] = None,
212212
extra_compile_args: Optional[Union[List[str], Dict[str, List[str]]]] = None,
213213
extra_link_args: Optional[List[str]] = None,
214-
build_ext_class: Type = None,
214+
build_ext_class: Optional[Type] = None,
215215
verbose: bool = False,
216216
clean: bool = False,
217217
) -> Tuple[str, str]:
@@ -242,7 +242,6 @@ def build_pybind_ext(
242242
ext_modules=[py_extension],
243243
script_args=[
244244
"build_ext",
245-
# "--parallel={}".format(gt_config.build_settings["parallel_jobs"]),
246245
"--build-temp={}".format(build_path),
247246
"--build-lib={}".format(build_path),
248247
"--force",
@@ -336,7 +335,7 @@ def build_pybind_cuda_ext(
336335

337336
def _clean_build_flags(config_vars: Dict[str, str]) -> None:
338337
for key, value in config_vars.items():
339-
if type(value) == str:
338+
if isinstance(value, str):
340339
value = " " + value + " "
341340
for s in value.split(" "):
342341
if (

src/gt4py/cartesian/frontend/defir_to_gtir.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def visit_StencilDefinition(
137137
def _nested_list_dim(self, a: List) -> List[int]:
138138
if not isinstance(a, list):
139139
return []
140-
return [len(a)] + self._nested_list_dim(a[0])
140+
return [len(a), *self._nested_list_dim(a[0])]
141141

142142
def visit_Assign(
143143
self, node: Assign, *, fields_decls: Dict[str, FieldDecl], **kwargs
@@ -181,7 +181,7 @@ def apply(cls, root, *, expected_dim: Tuple[int, ...], fields_decls: Dict[str, F
181181
# if the expression is just a scalar broadcast to the expected dimensions
182182
if not isinstance(result, list):
183183
result = functools.reduce(
184-
lambda val, len: [val for _ in range(len)], reversed(expected_dim), result
184+
lambda val, len_: [val for _ in range(len_)], reversed(expected_dim), result
185185
)
186186
return result
187187

src/gt4py/cartesian/frontend/gtscript_frontend.py

+12-14
Original file line numberDiff line numberDiff line change
@@ -211,15 +211,15 @@ def visit_BinOp(self, node: ast.BinOp) -> Union[gtscript.AxisIndex, nodes.AxisBo
211211
right = self.visit(node.right)
212212

213213
if isinstance(node.op, ast.Add):
214-
bin_op = lambda x, y: x + y # noqa: E731
215-
u_op = lambda x: x # noqa: E731
214+
bin_op = lambda x, y: x + y # noqa: E731 [lambda-assignment]
215+
u_op = lambda x: x # noqa: E731 [lambda-assignment]
216216
elif isinstance(node.op, ast.Sub):
217-
bin_op = lambda x, y: x - y # noqa: E731
218-
u_op = lambda x: -x # noqa: E731
217+
bin_op = lambda x, y: x - y # noqa: E731 [lambda-assignment]
218+
u_op = lambda x: -x # noqa: E731 [lambda-assignment]
219219
elif isinstance(node.op, ast.Mult):
220220
if left.level != right.level or not isinstance(left.level, nodes.LevelMarker):
221221
raise self.interval_error
222-
bin_op = lambda x, y: x * y # noqa: E731
222+
bin_op = lambda x, y: x * y # noqa: E731 [lambda-assignment]
223223
u_op = None
224224
else:
225225
raise GTScriptSyntaxError("Unexpected binary operator found in interval expression")
@@ -249,7 +249,7 @@ def visit_BinOp(self, node: ast.BinOp) -> Union[gtscript.AxisIndex, nodes.AxisBo
249249

250250
def visit_UnaryOp(self, node: ast.UnaryOp) -> nodes.AxisBound:
251251
if isinstance(node.op, ast.USub):
252-
op = lambda x: -x # noqa: E731
252+
op = lambda x: -x # noqa: E731 [lambda-assignment]
253253
else:
254254
raise self.interval_error
255255

@@ -417,9 +417,7 @@ def visit_Assign(self, node: ast.Assign):
417417
else:
418418
return self.generic_visit(node)
419419

420-
def visit_Call( # Cyclomatic complexity too high
421-
self, node: ast.Call, *, target_node=None
422-
):
420+
def visit_Call(self, node: ast.Call, *, target_node=None): # Cyclomatic complexity too high
423421
call_name = gt_meta.get_qualified_name_from_node(node.func)
424422

425423
if call_name in self.call_stack:
@@ -461,10 +459,10 @@ def visit_Call( # Cyclomatic complexity too high
461459
if name not in call_args:
462460
assert arg_infos[name] != nodes.Empty
463461
call_args[name] = ast.Constant(value=arg_infos[name])
464-
except Exception:
462+
except Exception as ex:
465463
raise GTScriptSyntaxError(
466464
message="Invalid call signature", loc=nodes.Location.from_ast_node(node)
467-
)
465+
) from ex
468466

469467
# Rename local names in subroutine to avoid conflicts with caller context names
470468
try:
@@ -601,7 +599,7 @@ def visit_If(self, node: ast.If):
601599
and node.test.func.id == "__INLINED"
602600
and len(node.test.args) == 1
603601
):
604-
warnings.warn(
602+
warnings.warn( # noqa: B028 [no-explicit-stacklevel]
605603
f"stencil {self.stencil_name}, line {node.lineno}, column {node.col_offset}: compile-time if condition via __INLINED deprecated",
606604
category=DeprecationWarning,
607605
)
@@ -1059,10 +1057,10 @@ def _eval_new_spatial_index(
10591057
for index_node in index_nodes:
10601058
try:
10611059
value = gt_meta.ast_eval(index_node, axis_context)
1062-
except Exception:
1060+
except Exception as ex:
10631061
raise GTScriptSyntaxError(
10641062
message="Could not evaluate axis shift expression.", loc=index_node
1065-
)
1063+
) from ex
10661064
if not isinstance(value, (gtscript.ShiftedAxis, gtscript.Axis)):
10671065
raise GTScriptSyntaxError(
10681066
message=f"Axis shift expression evaluated to unrecognized type {type(value)}.",

src/gt4py/cartesian/frontend/node_util.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def generic_visit(self, node: Node, **kwargs):
6969
else:
7070
pass
7171

72-
for key, value in items:
72+
for _, value in items:
7373
self._visit(value, **kwargs)
7474

7575

@@ -122,7 +122,7 @@ def iter_nodes_of_type(root_node: Node, node_type: Type) -> Generator[Node, None
122122
"""Yield an iterator over the nodes of node_type inside root_node in DFS order."""
123123

124124
def recurse(node: Node) -> Generator[Node, None, None]:
125-
for key, value in iter_attributes(node):
125+
for _, value in iter_attributes(node):
126126
if isinstance(node, collections.abc.Iterable):
127127
if isinstance(node, collections.abc.Mapping):
128128
children = node.values()

src/gt4py/cartesian/frontend/nodes.py

-18
Original file line numberDiff line numberDiff line change
@@ -322,15 +322,6 @@ class ScalarLiteral(Literal):
322322
loc = attribute(of=Location, optional=True)
323323

324324

325-
# @attribclass
326-
# class TupleLiteral(Node):
327-
# items = attribute(of=TupleOf[Expr])
328-
#
329-
# @property
330-
# def length(self):
331-
# return len(self.items)
332-
333-
334325
@attribclass
335326
class BuiltinLiteral(Literal):
336327
value = attribute(of=Builtin)
@@ -593,12 +584,6 @@ class Statement(Node):
593584
pass
594585

595586

596-
# @attribclass
597-
# class ExprStmt(Statement):
598-
# expr = attribute(of=Expr)
599-
# loc = attribute(of=Location, optional=True)
600-
601-
602587
class Decl(Statement):
603588
pass
604589

@@ -722,9 +707,6 @@ def is_single_index(self) -> bool:
722707
return self.start.level == self.end.level and self.start.offset == self.end.offset - 1
723708

724709
def disjoint_from(self, other: "AxisInterval") -> bool:
725-
# This made-up constant must be larger than any LevelMarker.offset used
726-
DOMAIN_SIZE: int = 1000
727-
728710
def get_offset(bound: AxisBound) -> int:
729711
return (
730712
0 + bound.offset if bound.level == LevelMarker.START else sys.maxsize + bound.offset

src/gt4py/cartesian/gtc/dace/expansion/tasklet_codegen.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ def _visit_conditional(
225225
indent = " " * 4
226226
body_code = [line for block in self.visit(body, **kwargs) for line in block.split("\n")]
227227
body_code = [indent + b for b in body_code]
228-
return "\n".join([mask_str] + body_code)
228+
return "\n".join([mask_str, *body_code])
229229

230230
def visit_MaskStmt(self, node: dcir.MaskStmt, **kwargs):
231231
return self._visit_conditional(cond=node.mask, body=node.body, keyword="if", **kwargs)

src/gt4py/cartesian/gtc/daceir.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class Stmt(common.Stmt):
4343

4444

4545
class Axis(eve.StrEnum):
46-
I = "I" # noqa: E741 ambiguous variable name 'I'
46+
I = "I" # noqa: E741 [ambiguous-variable-name]
4747
J = "J"
4848
K = "K"
4949

src/gt4py/cartesian/gtc/definitions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
class CartesianSpace:
2424
@enum.unique
2525
class Axis(enum.Enum):
26-
I = 0 # noqa: E741 # Do not use variables named 'I', 'O', or 'l'
26+
I = 0 # noqa: E741 [ambiguous-variable-name]
2727
J = 1
2828
K = 2
2929

src/gt4py/cartesian/gtc/gtcpp/oir_to_gtcpp.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from dataclasses import dataclass, field
1818
from typing import Any, Callable, Dict, List, Set, Union, cast
1919

20-
from devtools import debug # noqa: F401
20+
from devtools import debug # noqa: F401 [unused-import]
2121
from typing_extensions import Protocol
2222

2323
from gt4py import eve

src/gt4py/cartesian/gtc/numpy/npir.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
# --- Misc ---
2424
class AxisName(eve.StrEnum):
25-
I = "I" # noqa: E741 (ambiguous variable name)
25+
I = "I" # noqa: E741 [ambiguous-variable-name]
2626
J = "J"
2727
K = "K"
2828

src/gt4py/cartesian/gtc/passes/gtir_definitive_assignment_analysis.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,6 @@ def check(gtir_stencil_expr: gtir.Stencil) -> gtir.Stencil:
7474
"""Execute definitive assignment analysis and warn on errors."""
7575
invalid_accesses = analyze(gtir_stencil_expr)
7676
for invalid_access in invalid_accesses:
77-
warnings.warn(f"`{invalid_access.name}` may be uninitialized.")
77+
warnings.warn(f"`{invalid_access.name}` may be uninitialized.", stacklevel=2)
7878

7979
return gtir_stencil_expr

src/gt4py/cartesian/gtc/passes/oir_optimizations/horizontal_execution_merging.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ def first_has_horizontal_restriction() -> bool:
256256
or first_has_variable_access()
257257
or first_has_horizontal_restriction()
258258
):
259-
return [first] + self._merge(others, symtable, new_symbol_name, protected_fields)
259+
return [first, *self._merge(others, symtable, new_symbol_name, protected_fields)]
260260

261261
first_scalars = {decl.name for decl in first.declarations}
262262
writes = first_accesses.write_fields()

src/gt4py/cartesian/gtc/passes/oir_optimizations/vertical_loop_merging.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ def _mergeable(a: oir.VerticalLoop, b: oir.VerticalLoop) -> bool:
3838
def _merge(a: oir.VerticalLoop, b: oir.VerticalLoop) -> oir.VerticalLoop:
3939
sections = a.sections + b.sections
4040
if a.caches or b.caches:
41-
warnings.warn("AdjacentLoopMerging pass removed previously declared caches")
41+
warnings.warn(
42+
"AdjacentLoopMerging pass removed previously declared caches", stacklevel=2
43+
)
4244
return oir.VerticalLoop(
4345
loop_order=a.loop_order,
4446
sections=sections,

src/gt4py/cartesian/gtc/ufuncs.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
not_equal: np.ufunc = np.not_equal
4141
logical_and: np.ufunc = np.logical_and
4242
logical_or: np.ufunc = np.logical_or
43-
abs: np.ufunc = np.abs # noqa: A001 # shadowing abs builtin
43+
abs: np.ufunc = np.abs # noqa: A001 [builtin-variable-shadowing]
4444
minimum: np.ufunc = np.minimum
4545
maximum: np.ufunc = np.maximum
4646
remainder: np.ufunc = np.remainder

0 commit comments

Comments
 (0)