diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ed79fce05e340b0284f9ff564a6a24c74d200794..55bb930ab675568b790796aa45864a6540ea7275 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,20 +8,16 @@ 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' + - uses: actions/setup-python@v5 - 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 gen_wrap.py examples + pipx install ruff + ruff check pytest: name: Pytest Linux on Py${{ matrix.python-version }} diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f90ee4522d88623f1cc10333818332b2ee91e6d3..c433953020f01137da38d37370d809693529cbd4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -82,9 +82,11 @@ Documentation: only: - main -Flake8: +Ruff: script: - - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/main/prepare-and-run-flake8.sh - - . ./prepare-and-run-flake8.sh "$CI_PROJECT_NAME" test gen_wrap.py examples + - pipx install ruff + - ruff check tags: - - python3 + - docker-runner + except: + - tags diff --git a/configure.py b/configure.py index 85c9841d9f995805d8b64bb8aae976b6ecc43ba3..b07fc86b813847cd7abdc078c7e31ebca499b021 100755 --- a/configure.py +++ b/configure.py @@ -1,4 +1,6 @@ #! /usr/bin/env python3 from aksetup_helper import configure_frontend + + configure_frontend() diff --git a/doc/conf.py b/doc/conf.py index 0fad4b8d49dfa6d88e07dc5159ed3e3c6c0d5da8..ec9d8ab9046434fecb5831ef0daa6a034b623315 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,13 +1,13 @@ import re - from urllib.request import urlopen + _conf_url = \ "https://raw.githubusercontent.com/inducer/sphinxconfig/main/sphinxconfig.py" with urlopen(_conf_url) as _inf: exec(compile(_inf.read(), _conf_url, "exec"), globals()) -extensions.remove("sphinx.ext.linkcode") +extensions.remove("sphinx.ext.linkcode") # noqa: F821 copyright = "2011-21, Andreas Kloeckner" @@ -23,6 +23,7 @@ intersphinx_mapping = { "python": ("https://docs.python.org/3/", None), } + def autodoc_process_signature(app, what, name, obj, options, signature, return_annotation): from inspect import ismethod @@ -38,6 +39,7 @@ def autodoc_process_signature(app, what, name, obj, options, signature, return (signature, return_annotation) + def autodoc_process_docstring(app, what, name, obj, options, lines): # clear out redundant pybind-generated member list if any("Members" in ln for ln in lines): @@ -61,7 +63,7 @@ def autodoc_process_docstring(app, what, name, obj, options, lines): doc_match = arg_list_re.match(meth_obj.__doc__) if doc_match is None: - #print(f"'{meth_obj.__doc__}' did not match arg list RE") + # print(f"'{meth_obj.__doc__}' did not match arg list RE") return result arg_list = doc_match.group(2).split(", ") diff --git a/examples/demo.py b/examples/demo.py index 4750af120eb85886256fb7ebb701e0da742ef244..26ae09853bbff269ca484b2dcc479609531e8ddb 100644 --- a/examples/demo.py +++ b/examples/demo.py @@ -1,5 +1,6 @@ import islpy as isl + space = isl.Space.create_from_names(isl.DEFAULT_CONTEXT, set=["x", "y"]) bset = (isl.BasicSet.universe(space) @@ -53,8 +54,8 @@ def plot_basic_set(bset, *args, **kwargs): if plot_vert: pt.plot(vertex_pts[:, 0], vertex_pts[:, 1], "o") - import matplotlib.path as mpath import matplotlib.patches as mpatches + import matplotlib.path as mpath Path = mpath.Path # noqa @@ -76,7 +77,7 @@ plot_basic_set(bset2, facecolor="green", edgecolor="black", alpha=0.2) pt.grid() pt.xlim([-1, 6]) pt.ylim([-1, 8]) -#pt.show() +# pt.show() pt.savefig("before-union.png", dpi=50) plot_basic_set(union, facecolor="blue", edgecolor="yellow", diff --git a/gen_wrap.py b/gen_wrap.py index e30100beee364c1f1060b8e5b1585fa2a86b6433..3044d89191de367907bcda3cf2dc5a3acc9e6a88 100644 --- a/gen_wrap.py +++ b/gen_wrap.py @@ -20,12 +20,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -from dataclasses import dataclass +import os import re import sys -import os +from dataclasses import dataclass from os.path import join -from typing import Sequence, List +from typing import ClassVar, List, Mapping, Sequence + SEM_TAKE = "take" SEM_GIVE = "give" @@ -53,19 +54,19 @@ def for lambda try """.split() -class Retry(RuntimeError): +class Retry(RuntimeError): # noqa: N818 pass -class BadArg(ValueError): +class BadArg(ValueError): # noqa: N818 pass -class Undocumented(ValueError): +class Undocumented(ValueError): # noqa: N818 pass -class SignatureNotSupported(ValueError): +class SignatureNotSupported(ValueError): # noqa: N818 pass @@ -184,7 +185,7 @@ PART_TO_CLASSES = { # others "ctx", - "printer", "val", "multi_val", "vec", "mat", "fixed_box", + "printer", "val", "multi_val", "vec", "mat", "fixed_box", "aff", "pw_aff", "union_pw_aff", "multi_aff", "multi_pw_aff", "pw_multi_aff", "union_pw_multi_aff", "multi_union_pw_aff", @@ -264,9 +265,9 @@ MACRO_ENUMS = [ # }}} -SAFE_TYPES = list(ENUMS) + ["int", "unsigned", "uint32_t", "size_t", "double", - "long", "unsigned long", "isl_size"] -SAFE_IN_TYPES = SAFE_TYPES + ["const char *", "char *"] +SAFE_TYPES = [*list(ENUMS), "int", "unsigned", "uint32_t", "size_t", "double", "long", + "unsigned long", "isl_size"] +SAFE_IN_TYPES = [*SAFE_TYPES, "const char *", "char *"] # {{{ parser helpers @@ -381,10 +382,13 @@ def parse_arg(arg): def preprocess_with_macros(macro_header_contents, code): try: from pcpp.preprocessor import ( - Preprocessor as PreprocessorBase, OutputDirective, Action) - except ImportError: + Action, + OutputDirective, + Preprocessor as PreprocessorBase, + ) + except ImportError as err: raise RuntimeError("pcpp was not found. Please install pcpp before " - "installing islpy. 'pip install pcpp' should do the job.") + "installing islpy. 'pip install pcpp' should do the job.") from err class MacroExpandingCPreprocessor(PreprocessorBase): def on_directive_handle(self, directive, toks, ifpassthru, precedingtoks): @@ -420,7 +424,7 @@ def preprocess_with_macros(macro_header_contents, code): class FunctionData: - INVALID_PY_IDENTIFIER_RENAMING_MAP = { + INVALID_PY_IDENTIFIER_RENAMING_MAP: ClassVar[Mapping[str, str]] = { "2exp": "two_exp" } @@ -458,11 +462,11 @@ class FunctionData: return h.hexdigest() preprocessed_dir = "preproc-headers" - macro_headers = ["isl/multi.h", "isl/list.h"] + macro_headers: ClassVar[Sequence[str]] = ["isl/multi.h", "isl/list.h"] def get_preprocessed_header(self, fname): header_hash = self.get_header_hashes( - self.macro_headers + [fname]) + [*self.macro_headers, fname]) # cache preprocessed headers to avoid install-time # dependency on pcpp @@ -490,7 +494,7 @@ class FunctionData: prepro_header = preprocess_with_macros( macro_header_contents, self.get_header_contents(fname)) - with open(prepro_fname, "wt") as outf: + with open(prepro_fname, "w") as outf: outf.write(prepro_header) return prepro_header @@ -1347,9 +1351,8 @@ def write_wrapper(outf, meth): inputs=", ".join(input_args), body="\n".join(body))) - docs = (["{}({})".format(meth.name, ", ".join(arg_names)), ""] - + docs - + [f":return: {ret_descr}"]) + docs = (["{}({})".format(meth.name, ", ".join(arg_names)), + "", *docs, f":return: {ret_descr}"]) return arg_names, "\n".join(docs) @@ -1376,7 +1379,7 @@ def write_exposer(outf, meth, arg_names, doc_str): if meth.name == "get_hash" and len(meth.args) == 1: py_name = "__hash__" - #if meth.is_static: + # if meth.is_static: # doc_str = "(static method)\n" + doc_str if not meth.is_exported: @@ -1421,7 +1424,7 @@ def write_wrappers(expf, wrapf, methods): undoc = [] for meth in methods: - #print "TRY_WRAP:", meth + # print "TRY_WRAP:", meth if meth.name.endswith("_si") or meth.name.endswith("_ui"): val_versions = [ meth2 @@ -1517,7 +1520,7 @@ def add_upcasts(basic_class, special_class, fmap, expf): def gen_wrapper(include_dirs, include_barvinok=False, isl_version=None): - fdata = FunctionData(["."] + include_dirs) + fdata = FunctionData([".", *include_dirs]) fdata.read_header("isl/ctx.h") fdata.read_header("isl/id.h") fdata.read_header("isl/space.h") @@ -1550,8 +1553,8 @@ def gen_wrapper(include_dirs, include_barvinok=False, isl_version=None): fdata.read_header("barvinok/isl.h") for part, classes in PART_TO_CLASSES.items(): - expf = open(f"src/wrapper/gen-expose-{part}.inc", "wt") - wrapf = open(f"src/wrapper/gen-wrap-{part}.inc", "wt") + expf = open(f"src/wrapper/gen-expose-{part}.inc", "w") + wrapf = open(f"src/wrapper/gen-wrap-{part}.inc", "w") classes = [ cls diff --git a/islpy/__init__.py b/islpy/__init__.py index 6bcdbc41a9a1f2a9b8c86606a71b455bb8cfa57e..063c0366f1c104fb300f6a41715510ab984562a7 100644 --- a/islpy/__init__.py +++ b/islpy/__init__.py @@ -20,11 +20,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -from typing import Optional, TypeVar, cast, Callable, Any +from typing import Any, Callable, Optional, TypeVar, cast import islpy._isl as _isl from islpy.version import VERSION, VERSION_TEXT # noqa + __version__ = VERSION_TEXT # {{{ copied verbatim from pytools to avoid numpy/pytools dependency @@ -51,7 +52,7 @@ def _memoize_on_first_arg(function: F, cache_dict_name: Optional[str] = None) -> def wrapper(obj, *args, **kwargs): if kwargs: - key = (_HasKwargs, frozenset(kwargs.items())) + args + key = (_HasKwargs, frozenset(kwargs.items()), *args) else: key = args diff --git a/pyproject.toml b/pyproject.toml index 9b4860b2d06bc9c1f3214efd378292f4c15824c1..64a88b8979c28f927789196d62574a11f3b3dd16 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,54 @@ requires = [ ] build-backend = "setuptools.build_meta" +[tool.inducer-ci-support] +disable-editable-pip-install = true + +[tool.ruff] +preview = true +exclude = [ + "isl", + "aksetup_helper.py" +] + +[tool.ruff.lint] +extend-select = [ + "B", # flake8-bugbear + "C", # flake8-comprehensions + "E", # pycodestyle + "F", # pyflakes + "G", # flake8-logging-format + "I", # flake8-isort + "N", # pep8-naming + "NPY", # numpy + "Q", # flake8-quotes + "UP", # pyupgrade + "RUF", # ruff + "W", # pycodestyle +] +extend-ignore = [ + "C90", # McCabe complexity + "E221", # multiple spaces before operator + "E226", # missing whitespace around arithmetic operator + "E402", # module-level import not at top of file + "UP006", # updated annotations due to __future__ import + "UP007", # updated annotations due to __future__ import + "UP031", # use f-strings instead of % + "UP032", # use f-strings instead of .format +] + +[tool.ruff.lint.flake8-quotes] +docstring-quotes = "double" +inline-quotes = "double" +multiline-quotes = "double" + +[tool.ruff.lint.isort] +combine-as-imports = true +known-local-folder = [ + "islpy", +] +lines-after-imports = 2 + [tool.cibuildwheel] # nanobind does not support Py<3.8 skip = ["*-win-*", "[cp]p3[67]-*"] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 3c9d627463f5eec3b3597f54c2e925276fd83a90..0000000000000000000000000000000000000000 --- a/setup.cfg +++ /dev/null @@ -1,17 +0,0 @@ -# Editable is broken with py>=3.12 because of -# https://github.com/scikit-build/scikit-build/issues/981 -# disable-editable-pip-install - -[flake8] -ignore = E126,E127,E128,E123,E226,E241,E242,E265,W503,E402 -max-line-length=85 - -inline-quotes = " -docstring-quotes = """ -multiline-quotes = """ - -# enable-flake8-bugbear - -[metadata] -license_files = - doc/misc.rst diff --git a/setup.py b/setup.py index c54a796603538db43d90476feee6ebd7cf6bc1ad..d2f5f34ccf950df14e092d8d2dfde26ce2855304 100644 --- a/setup.py +++ b/setup.py @@ -27,14 +27,20 @@ THE SOFTWARE. import sys from typing import List, Sequence + # Needed for aksetup to be found sys.path.extend(["."]) def get_config_schema(): - from aksetup_helper import (ConfigSchema, - IncludeDir, LibraryDir, Libraries, - Switch, StringListOption) + from aksetup_helper import ( + ConfigSchema, + IncludeDir, + Libraries, + LibraryDir, + StringListOption, + Switch, + ) default_cxxflags = [ # Required for pybind11: @@ -146,18 +152,18 @@ def _get_isl_sources(use_shipped_imath: bool, use_imath_sio: bool) -> Sequence[s "isl/imath/imath.c", "isl/imath/imrat.c", "isl/imath/gmp_compat.c", - #"isl/imath_wrap/imath.c", - #"isl/imath_wrap/imrat.c", - #"isl/imath_wrap/gmp_compat.c", + # "isl/imath_wrap/imath.c", + # "isl/imath_wrap/imrat.c", + # "isl/imath_wrap/gmp_compat.c", ]) return extra_objects def main(): - from skbuild import setup import nanobind # noqa: F401 from setuptools import find_packages + from skbuild import setup # {{{ import aksetup_helper bits @@ -165,7 +171,7 @@ def main(): # FIXME skbuild seems to remove this. Why? sys.path.append(".") - from aksetup_helper import get_config, check_git_submodules + from aksetup_helper import check_git_submodules, get_config from gen_wrap import gen_wrapper sys.path = prev_path @@ -203,7 +209,7 @@ def main(): if conf["USE_SHIPPED_ISL"]: cmake_args.append("-DUSE_SHIPPED_ISL:bool=1") - isl_inc_dirs = ["isl-supplementary", "isl/include", "isl"] + isl_inc_dirs = ["isl-supplementary", "isl/include", "isl"] if conf["USE_SHIPPED_IMATH"]: cmake_args.append("-DUSE_IMATH_FOR_MP:bool=1") @@ -223,8 +229,8 @@ def main(): cmake_args.append(f"-DISL_SOURCES:list={';'.join(extra_objects)}") with open("isl/configure.ac") as inf: - isl_version_line, = [ln for ln in inf - if ln.strip().startswith("versioninfo")] + isl_version_line, = (ln for ln in inf + if ln.strip().startswith("versioninfo")) _, isl_version = isl_version_line.strip().split("=") isl_version = isl_version.replace(":", ".") @@ -241,7 +247,7 @@ def main(): cmake_args.append(f"-DISL_LIB_NAMES={';'.join(conf['ISL_LIBNAME'])}") - cmake_args.append('-DISL_SOURCES:list=') + cmake_args.append("-DISL_SOURCES:list=") isl_inc_dirs = conf["ISL_INC_DIR"] diff --git a/test/test_isl.py b/test/test_isl.py index e5a733223338dbacf7c8344948666167c73a67c1..9dc43c4031a021b2d49bf5df713d9570dc5eea4b 100644 --- a/test/test_isl.py +++ b/test/test_isl.py @@ -20,9 +20,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -import islpy as isl import pytest +import islpy as isl + def test_basics(): dt = isl.dim_type @@ -211,7 +212,7 @@ def test_schedule(): access = build.call_from_pw_multi_aff(aff) return isl.AstNode.alloc_user(access) - build, callback_handle = build.set_at_each_domain(callback) + build, _callback_handle = build.set_at_each_domain(callback) ast = build.ast_from_schedule(schedule) @@ -226,8 +227,8 @@ def test_schedule(): return printer opts = isl.AstPrintOptions.alloc(isl.DEFAULT_CONTEXT) - opts, cb_print_user_handle = opts.set_print_user(cb_print_user) - opts, cb_print_for_handle = opts.set_print_for(cb_print_for) + opts, _cb_print_user_handle = opts.set_print_user(cb_print_user) + opts, _cb_print_for_handle = opts.set_print_for(cb_print_for) printer = isl.Printer.to_str(isl.DEFAULT_CONTEXT) printer = printer.set_output_format(isl.format.C) @@ -461,7 +462,7 @@ def test_remove_map_if_callback_exc(): umap = isl.UnionMap.read_from_str(ctx, "{A[0] -> [1]; B[1] -> [2]}") def callback_throws_exception(m): - 1/0 + raise AssertionError() with pytest.raises(isl.Error): umap3 = umap.remove_map_if(callback_throws_exception)