diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c0ebb1f47..f607f856e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,20 +8,17 @@ on: - cron: '17 3 * * 0' jobs: - flake8: - name: Flake8 + ruff: + name: Ruff runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 - with: - # matches compat target in setup.py - python-version: '3.8' - name: "Main Script" run: | - curl -L -O https://gitlab.tiker.net/inducer/ci-support/raw/main/prepare-and-run-flake8.sh - . ./prepare-and-run-flake8.sh "$(basename $GITHUB_REPOSITORY)" test examples + pip install ruff + ruff check pylint: name: Pylint diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ffa1aeb63..57a13e048 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -39,17 +39,15 @@ Pylint: except: - tags - -Flake8: - script: - - curl -L -O https://gitlab.tiker.net/inducer/ci-support/raw/main/prepare-and-run-flake8.sh - - . ./prepare-and-run-flake8.sh ${CI_PROJECT_NAME} test examples +Ruff: + script: | + pipx install ruff + ruff check tags: - - python3 + - docker-runner except: - tags - Mypy: script: | curl -L -O https://tiker.net/ci-support-v0 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..fee2fce77 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,61 @@ +[tool.ruff.lint] +preview = true +extend-select = [ + "B", # flake8-bugbear + "C", # flake8-comprehensions + "E", # pycodestyle + "F", # pyflakes + # not yet + # "I", # flake8-isort + "N", # pep8-naming + "Q", # flake8-quotes + "W", # pycodestyle + "NPY" # numpy +] +extend-ignore = [ + "E226", + "E241", + "E242", + "E265", + "N802", + "E402", + "N814", + "N817", + "C90", + + # numpy random generators---disable for now + "NPY002", +] +[tool.ruff.lint.per-file-ignores] +"examples/advection.py" = ["B023"] +"test/test_linalg.py" = ["N806"] + +[tool.ruff.lint.isort] +known-first-party = ["pytools", "pymbolic", "loopy"] +known-local-folder = ["pytato"] +lines-after-imports = 2 +combine-as-imports = true + +[tool.ruff.lint.flake8-quotes] +inline-quotes = "double" +docstring-quotes = "double" +multiline-quotes = "double" + +[[tool.mypy.overrides]] +module = [ + "islpy", + "loopy.*", + "pymbolic.*", + "pyopencl.*", + "jax.*", + "pygments.*", + "mako.*", +] +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = [ + "pytato.transform", + "pytato.scalar_expr", +] +allow_subclassing_any = true diff --git a/pytato/array.py b/pytato/array.py index 270dcc176..991eb11f5 100644 --- a/pytato/array.py +++ b/pytato/array.py @@ -918,7 +918,8 @@ def __init__(self, data: Mapping[str, Array], *, from warnings import warn warn("Passing `tags=None` is deprecated and will result" " in an error from 2023. To remove this message either" - " call make_dict_of_named_arrays or pass the `tags` argument.") + " call make_dict_of_named_arrays or pass the `tags` argument.", + DeprecationWarning, stacklevel=2) tags = frozenset() object.__setattr__(self, "_data", data) @@ -1170,8 +1171,9 @@ def with_tagged_reduction(self, if isinstance(redn_axis, str): try: redn_axis_ = self.index_to_access_descr[redn_axis] - except KeyError: - raise InvalidEinsumIndex(f"'{redn_axis}': not a valid axis index.") + except KeyError as err: + raise InvalidEinsumIndex( + f"'{redn_axis}': not a valid axis index.") from err if isinstance(redn_axis_, EinsumReductionAxis): redn_axis = redn_axis_ else: diff --git a/pytato/diagnostic.py b/pytato/diagnostic.py index 6c7b1ebac..3d8879004 100644 --- a/pytato/diagnostic.py +++ b/pytato/diagnostic.py @@ -47,7 +47,7 @@ class CannotBroadcastError(ValueError): pass -class UnknownIndexLambdaExpr(ValueError): +class UnknownIndexLambdaExpr(ValueError): # noqa: N818 """ Raised when the structure :class:`pytato.array.IndexLambda` could not be inferred. @@ -55,20 +55,20 @@ class UnknownIndexLambdaExpr(ValueError): pass -class InvalidEinsumIndex(ValueError): +class InvalidEinsumIndex(ValueError): # noqa: N818 """ Raised when an einsum index was referred by an invalid value. """ -class NotAReductionAxis(ValueError): +class NotAReductionAxis(ValueError): # noqa: N818 """ Raised when a :class:`pytato.ReductionDescriptor` was referred by an invalid value. """ -class CannotBeLoweredToIndexLambda(ValueError): +class CannotBeLoweredToIndexLambda(ValueError): # noqa: N818 """ Raised when a :class:`pytato.Array` was expected to be lowered to an :class:`~pytato.array.IndexLambda`, but it cannot be. For ex. a diff --git a/pytato/distributed/verify.py b/pytato/distributed/verify.py index 436fb9d9b..ea905357f 100644 --- a/pytato/distributed/verify.py +++ b/pytato/distributed/verify.py @@ -332,9 +332,9 @@ def add_needed_pid(pid: _DistributedPartId, # Add edges between sends and receives (cross-rank) try: sending_pid = comm_id_to_sending_pid[comm_id] - except KeyError: + except KeyError as err: raise MissingSendError( - f"no matching send for recv on '{comm_id}'") + f"no matching send for recv on '{comm_id}'") from err add_needed_pid(sumpart.pid, sending_pid) @@ -371,8 +371,8 @@ def add_needed_pid(pid: _DistributedPartId, from pytato.distributed.verify import PartitionInducedCycleError try: compute_topological_order(pid_to_needed_pids) - except CycleError: - raise PartitionInducedCycleError + except CycleError as err: + raise PartitionInducedCycleError from err logger.info("verify_distributed_partition completed successfully.") diff --git a/pytato/loopy.py b/pytato/loopy.py index fea9274bd..0c6266cd5 100644 --- a/pytato/loopy.py +++ b/pytato/loopy.py @@ -273,7 +273,7 @@ def call_loopy(translation_unit: "lp.TranslationUnit", # {{{ shape inference -class ShapeInferenceFailure(RuntimeError): +class ShapeInferenceFailure(RuntimeError): # noqa: N818 pass diff --git a/pytato/target/loopy/__init__.py b/pytato/target/loopy/__init__.py index 34278dbf2..f6a38d59d 100644 --- a/pytato/target/loopy/__init__.py +++ b/pytato/target/loopy/__init__.py @@ -185,14 +185,15 @@ def _get_processed_bound_arguments(self, if self.program.default_entrypoint.options.no_numpy: raise TypeError(f"Got numpy array for the DataWrapper {name}" ", in no_numpy=True mode. Expects a" - " pyopencl.array.Array.") + " pyopencl.array.Array.") from None proc_bnd_args[name] = cla.to_device(queue, bnd_arg, allocator) elif isinstance(bnd_arg, cla.Array): proc_bnd_args[name] = bnd_arg else: raise TypeError("Data in a bound argument can be one of" " numpy array, pyopencl array or scalar." - f" Got {type(bnd_arg).__name__} for '{name}'.") + f" Got {type(bnd_arg).__name__} for '{name}'." + ) from None result: Mapping[str, Any] = immutabledict(proc_bnd_args) assert set(result.keys()) == set(self.bound_arguments.keys()) diff --git a/pytato/target/loopy/codegen.py b/pytato/target/loopy/codegen.py index 1b7e22819..eb87e44d8 100644 --- a/pytato/target/loopy/codegen.py +++ b/pytato/target/loopy/codegen.py @@ -689,8 +689,8 @@ def map_reduce(self, expr: scalar_expr.Reduce, try: loopy_redn = PYTATO_REDUCTION_TO_LOOPY_REDUCTION[type(expr.op)] - except KeyError: - raise NotImplementedError(expr.op) + except KeyError as err: + raise NotImplementedError(expr.op) from err unique_names_mapping = { old_name: state.var_name_gen(f"_pt_{loopy_redn}" + old_name) diff --git a/pytato/target/python/__init__.py b/pytato/target/python/__init__.py index cebe5ea5c..64da66bdb 100644 --- a/pytato/target/python/__init__.py +++ b/pytato/target/python/__init__.py @@ -91,7 +91,7 @@ def _compiled_function(self) -> Callable[..., Any]: def _bound_argment_names(self) -> Set[str]: return set(self.bound_arguments.keys()) - def __call__(self, *args: Any, **kwargs: Any) -> Any: + def __call__(self, *args: Any, **kwargs: Any) -> Any: if args: raise ValueError(f"'{type(self).__call__}' does not take positional" diff --git a/pytato/transform/metadata.py b/pytato/transform/metadata.py index 7f015d739..b2a91e7e5 100644 --- a/pytato/transform/metadata.py +++ b/pytato/transform/metadata.py @@ -212,7 +212,7 @@ def map_index_lambda(self, expr: IndexLambda) -> None: except UnknownIndexLambdaExpr: from warnings import warn warn(f"'{expr}' is an unknown index lambda type" - " no tags were propagated across it.") + " no tags were propagated across it.", stacklevel=1) # no propagation semantics implemented for such cases return diff --git a/pytato/utils.py b/pytato/utils.py index 2c547f205..10ea2c9a3 100644 --- a/pytato/utils.py +++ b/pytato/utils.py @@ -538,7 +538,7 @@ def _index_into( array_idx_shape = get_shape_after_broadcasting( [idx for idx in indices if isinstance(idx, Array)]) except CannotBroadcastError as e: - raise IndexError(str(e)) + raise IndexError(str(e)) from None # }}} diff --git a/pytato/visualization/fancy_placeholder_data_flow.py b/pytato/visualization/fancy_placeholder_data_flow.py index e3e20fe53..3fa066469 100644 --- a/pytato/visualization/fancy_placeholder_data_flow.py +++ b/pytato/visualization/fancy_placeholder_data_flow.py @@ -257,9 +257,9 @@ def show_fancy_placeholder_data_flow(dag: Union[Array, DictOfNamedArrays], """ try: from mako.template import Template - except ImportError: + except ImportError as err: raise RuntimeError("'show_fancy_placeholder_data_flow' requires" - " mako. Install as `pip install mako`.") + " mako. Install as `pip install mako`.") from err if isinstance(dag, Array): from pytato.array import make_dict_of_named_arrays diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 1940f30c9..000000000 --- a/setup.cfg +++ /dev/null @@ -1,45 +0,0 @@ -[flake8] -ignore = E126,E127,E128,E123,E226,E241,E242,E265,N802,W503,E402,N814,N817,W504 -max-line-length=85 - -inline-quotes = " -docstring-quotes = """ -multiline-quotes = """ - -per-file-ignores = - examples/advection.py:B023 - test/test_linalg.py:N806 - -# enable-flake8-bugbear - -[mypy] - -[mypy-pytato.transform] -disallow_subclassing_any = False - -[mypy-pytato.scalar_expr] -disallow_subclassing_any = False - -[mypy-islpy] -ignore_missing_imports = True - -[mypy-loopy.*] -ignore_missing_imports = True - -[mypy-numpy] -ignore_missing_imports = True - -[mypy-pymbolic.*] -ignore_missing_imports = True - -[mypy-pyopencl.*] -ignore_missing_imports = True - -[mypy-jax.*] -ignore_missing_imports = True - -[mypy-pygments.*] -ignore_missing_imports = True - -[mypy-mako.*] -ignore_missing_imports = True diff --git a/test/test_apps.py b/test/test_apps.py index 1d5b7575a..2715436ac 100644 --- a/test/test_apps.py +++ b/test/test_apps.py @@ -143,7 +143,7 @@ def test_trace_fft(ctx_factory): prg = pt.generate_loopy(result).program x = np.random.randn(n).astype(np.complex128) - evt, (result,) = prg(queue, x=x) + _evt, (result,) = prg(queue, x=x) ref_result = fft(x) diff --git a/test/test_codegen.py b/test/test_codegen.py index 545e431e1..66d62c31c 100755 --- a/test/test_codegen.py +++ b/test/test_codegen.py @@ -670,7 +670,7 @@ def test_passing_bound_arguments_raises(ctx_factory): prg = pt.generate_loopy(42*x) with pytest.raises(ValueError): - evt, (out2,) = prg(queue, x=np.random.rand(10)) + prg(queue, x=np.random.rand(10)) @pytest.mark.parametrize("shape1, shape2", ( @@ -700,9 +700,9 @@ def test_broadcasting(ctx_factory, shape1, shape2): prg = pt.generate_loopy(x+y) if "n" in prg.kernel.arg_dict: - evt, (out,) = prg(queue, n=n) + _evt, (out,) = prg(queue, n=n) else: - evt, (out,) = prg(queue) + _evt, (out,) = prg(queue) np.testing.assert_allclose(out, x_in+y_in) @@ -750,7 +750,7 @@ def test_call_loopy(ctx_factory): loopyfunc = call_loopy(knl, bindings={"Y": y}, entrypoint="callee") z = loopyfunc["Z"] - evt, (z_out, ) = pt.generate_loopy(2*z)(queue, x=x_in) + _evt, (z_out, ) = pt.generate_loopy(2*z)(queue, x=x_in) assert (z_out == 40*(x_in.sum(axis=1))).all() @@ -779,7 +779,7 @@ def test_call_loopy_with_same_callee_names(ctx_factory): out = pt.make_dict_of_named_arrays({"cuatro_u": cuatro_u, "nueve_u": nueve_u}) - evt, out_dict = pt.generate_loopy(out, + _evt, out_dict = pt.generate_loopy(out, options=lp.Options(return_dict=True))(queue) np.testing.assert_allclose(out_dict["cuatro_u"], 4*u_in) np.testing.assert_allclose(out_dict["nueve_u"], 9*u_in) @@ -791,7 +791,7 @@ def test_exprs_with_named_arrays(ctx_factory): x = pt.make_data_wrapper(x_in) y1y2 = pt.make_dict_of_named_arrays({"y1": 2*x, "y2": 3*x}) res = 21*y1y2["y1"] - evt, (out,) = pt.generate_loopy(res)(queue) + _evt, (out,) = pt.generate_loopy(res)(queue) np.testing.assert_allclose(out, 42*x_in) @@ -818,7 +818,7 @@ def test_call_loopy_with_parametric_sizes(ctx_factory): loopyfunc = call_loopy(knl, bindings={"Y": y, "m": m, "n": n}) z = loopyfunc["Z"] - evt, (z_out, ) = pt.generate_loopy(2*z)(queue, x=x_in) + _evt, (z_out, ) = pt.generate_loopy(2*z)(queue, x=x_in) np.testing.assert_allclose(z_out, 42*(x_in.sum(axis=1))) @@ -841,7 +841,7 @@ def test_call_loopy_with_scalar_array_inputs(ctx_factory): x = pt.make_placeholder(name="x", shape=(), dtype=float) y = call_loopy(knl, {"x": 3*x})["y"] - evt, (out,) = pt.generate_loopy(y)(queue, x=x_in) + _evt, (out,) = pt.generate_loopy(y)(queue, x=x_in) np.testing.assert_allclose(out, 6*x_in) @@ -860,7 +860,7 @@ def test_reductions(ctx_factory, axis, redn, shape): pt_func = getattr(pt, redn) prg = pt.generate_loopy(pt_func(x, axis=axis)) - evt, (out,) = prg(queue) + _evt, (out,) = prg(queue) assert np.all(abs(1 - out/np_func(x_in, axis)) < 1e-14) @@ -1225,7 +1225,7 @@ def test_broadcast_to(ctx_factory): x_in = rng.random(input_shape, dtype=np.float32) x = pt.make_data_wrapper(x_in) - evt, (x_brdcst,) = pt.generate_loopy( + _evt, (x_brdcst,) = pt.generate_loopy( pt.broadcast_to(x, broadcasted_shape))(queue) np.testing.assert_allclose(np.broadcast_to(x_in, broadcasted_shape), @@ -1248,23 +1248,23 @@ def test_advanced_indexing_with_broadcasting(ctx_factory): idx2 = pt.make_data_wrapper(idx2_in) # Case 1 - evt, (pt_out,) = pt.generate_loopy(x[:, ::-1, idx1, idx2])(cq) + _evt, (pt_out,) = pt.generate_loopy(x[:, ::-1, idx1, idx2])(cq) np.testing.assert_allclose(pt_out, x_in[:, ::-1, idx1_in, idx2_in]) # Case 2 - evt, (pt_out,) = pt.generate_loopy(x[-4:4:-1, idx1, idx2, :])(cq) + _evt, (pt_out,) = pt.generate_loopy(x[-4:4:-1, idx1, idx2, :])(cq) np.testing.assert_allclose(pt_out, x_in[-4:4:-1, idx1_in, idx2_in, :]) # Case 3 - evt, (pt_out,) = pt.generate_loopy(x[idx1, idx2, -2::-1, :])(cq) + _evt, (pt_out,) = pt.generate_loopy(x[idx1, idx2, -2::-1, :])(cq) np.testing.assert_allclose(pt_out, x_in[idx1_in, idx2_in, -2::-1, :]) # Case 4 (non-contiguous advanced indices) - evt, (pt_out,) = pt.generate_loopy(x[:, idx1, -2::-1, idx2])(cq) + _evt, (pt_out,) = pt.generate_loopy(x[:, idx1, -2::-1, idx2])(cq) np.testing.assert_allclose(pt_out, x_in[:, idx1_in, -2::-1, idx2_in]) # Case 5 (non-contiguous advanced indices with ellipsis) - evt, (pt_out,) = pt.generate_loopy(x[idx1, ..., idx2])(cq) + _evt, (pt_out,) = pt.generate_loopy(x[idx1, ..., idx2])(cq) np.testing.assert_allclose(pt_out, x_in[idx1_in, ..., idx2_in]) @@ -1319,7 +1319,7 @@ def test_advanced_indexing_fuzz(ctx_factory): else pt.make_data_wrapper(idx) for idx in np_indices] - evt, (pt_out,) = pt.generate_loopy(x[tuple(pt_indices)])(cq) + _evt, (pt_out,) = pt.generate_loopy(x[tuple(pt_indices)])(cq) np.testing.assert_allclose(pt_out, x_in[tuple(np_indices)], err_msg=(f"input_shape={input_shape}, " @@ -1476,7 +1476,7 @@ def map_remainder(self, expr): for insn in pt_prg.program.default_entrypoint.instructions: OnRemainderRaiser()(insn.expression) - evt, (out,) = pt_prg(cq) + _evt, (out,) = pt_prg(cq) np.testing.assert_allclose(out, a_np[b_np]) @@ -1565,7 +1565,7 @@ def test_scalars_are_typed(ctx_factory): x_in = np.random.rand(3, 3) x = pt.make_data_wrapper(x_in) - evt, (pt_out,) = pt.generate_loopy(x * 3.14j)(cq) + _evt, (pt_out,) = pt.generate_loopy(x * 3.14j)(cq) np_out = x_in * 3.14j assert pt_out.dtype == np_out.dtype np.testing.assert_allclose(pt_out, np_out) @@ -1745,7 +1745,7 @@ def test_two_rolls(ctx_factory): x = pt.make_placeholder(name="x", shape=(n, n), dtype=np.float64) x_in = np.arange(1., 10.).reshape(3, 3) - evt, (pt_out,) = pt.generate_loopy(pt.roll(x, -2, 0) + pt.roll(x, -1, 0) + _evt, (pt_out,) = pt.generate_loopy(pt.roll(x, -2, 0) + pt.roll(x, -1, 0) )(cq, x=x_in) np_out = np.roll(x_in, -2, 0) + np.roll(x_in, -1, 0) np.testing.assert_allclose(np_out, pt_out) @@ -1839,12 +1839,12 @@ def _get_masking_array_for_test_pad(array, pad_widths): def _get_mask_array_idx(*idxs): return np.where( - sum([((idx < pad_width[0]) + sum(((idx < pad_width[0]) | (idx >= (pad_width[0]+axis_len)) ).astype(np.int32) for idx, axis_len, pad_width in zip(idxs, array.shape, - pad_widths)]) > 1, + pad_widths)) > 1, 0*idxs[0], 0*idxs[0] + 1) @@ -1937,7 +1937,7 @@ def h(x, y): def build_expression(tracer): x = pt.arange(500, dtype=np.float32) twice_x = tracer(f, x) - twice_x_2, thrice_x_2 = tracer(partial(g, tracer), x) + twice_x_2, _thrice_x_2 = tracer(partial(g, tracer), x) result = tracer(h, x, 2*x) twice_x_3 = result["twice"] diff --git a/test/test_distributed.py b/test/test_distributed.py index 3da30de97..82f6eab9f 100644 --- a/test/test_distributed.py +++ b/test/test_distributed.py @@ -889,7 +889,7 @@ def test_number_symbolic_tags_bare_classes(ctx_factory): outputs = pt.make_dict_of_named_arrays({"out": res}) partition = pt.find_distributed_partition(comm, outputs) - (distp, next_tag) = pt.number_distributed_tags(comm, partition, base_tag=4242) + (_distp, next_tag) = pt.number_distributed_tags(comm, partition, base_tag=4242) assert next_tag == 4244 diff --git a/test/testlib.py b/test/testlib.py index 5cd1342d3..73daf101f 100644 --- a/test/testlib.py +++ b/test/testlib.py @@ -65,7 +65,7 @@ def assert_allclose_to_numpy(expr: Array, queue: cl.CommandQueue, np_result = NumpyBasedEvaluator(np, parameters)(expr) prog = pt.generate_loopy(expr) - evt, (pt_result,) = prog(queue, **{placeholder.name: data + _evt, (pt_result,) = prog(queue, **{placeholder.name: data for placeholder, data in parameters.items()}) assert pt_result.shape == np_result.shape