diff --git a/.github/workflows/test_and_build.yml b/.github/workflows/test_and_build.yml index d3f193c51..ea82052f8 100644 --- a/.github/workflows/test_and_build.yml +++ b/.github/workflows/test_and_build.yml @@ -162,7 +162,7 @@ jobs: npver=$(python -c 'import random ; print(random.choice(["=1.23", ""]))') spver=$(python -c 'import random ; print(random.choice(["=1.9", "=1.10", ""]))') pdver=$(python -c 'import random ; print(random.choice(["=1.5", ""]))') - akver=$(python -c 'import random ; print(random.choice(["=1.10", "=2.0.5", "=2.0.6", "=2.0.7", ""]))') + akver=$(python -c 'import random ; print(random.choice(["=1.10", "=2.0.5", "=2.0.6", "=2.0.7", "=2.0.8", ""]))') fi if [[ ${{ steps.sourcetype.outputs.selected }} == "source" || ${{ steps.sourcetype.outputs.selected }} == "upstream" ]]; then # TODO: there are currently issues with some numpy versions when diff --git a/environment.yml b/environment.yml index 71891745e..90675527c 100644 --- a/environment.yml +++ b/environment.yml @@ -23,6 +23,7 @@ dependencies: - pandas # For I/O - awkward + # - fast_matrix_market # Coming soon... - networkx - scipy - sparse @@ -77,6 +78,7 @@ dependencies: # - lxml # - make # - memory_profiler + # - nbqa # - netcdf4 # - networkit # - nxviz diff --git a/graphblas/core/operator.py b/graphblas/core/operator.py index 68883cbab..eca7c9d75 100644 --- a/graphblas/core/operator.py +++ b/graphblas/core/operator.py @@ -599,7 +599,7 @@ def __init__(self, name, anonymous): self.__call__ = lru_cache(maxsize=1024)(method) def _call(self, *args, **kwargs): - raise NotImplementedError() + raise NotImplementedError class ParameterizedUnaryOp(ParameterizedUdf): @@ -1671,7 +1671,7 @@ def _from_indexunary(cls, iop): ) if not all(x == BOOL for x in iop.types.values()): raise ValueError("SelectOp must have BOOL return type") - for type, t in iop._typed_ops.items(): + for type_, t in iop._typed_ops.items(): if iop.orig_func is not None: op = cls._typed_user_class( obj, @@ -1691,8 +1691,8 @@ def _from_indexunary(cls, iop): ) # type is not always equal to t.type, so can't use op._add # but otherwise perform the same logic - obj._typed_ops[type] = op - obj.types[type] = op.return_type + obj._typed_ops[type_] = op + obj.types[type_] = op.return_type return obj @classmethod diff --git a/graphblas/core/recorder.py b/graphblas/core/recorder.py index b7cb567f2..455166544 100644 --- a/graphblas/core/recorder.py +++ b/graphblas/core/recorder.py @@ -140,7 +140,7 @@ def _repr_html_(self): # pragma: no cover try: from IPython.display import Code except ImportError as exc: - raise NotImplementedError() from exc + raise NotImplementedError from exc lines = self._get_repr_lines() code = Code("\n".join(lines), language="C") head, tail = self._repr_base_() diff --git a/graphblas/core/utils.py b/graphblas/core/utils.py index 83fa15cd5..e0df29db4 100644 --- a/graphblas/core/utils.py +++ b/graphblas/core/utils.py @@ -278,12 +278,12 @@ def __init__(self, member_property, classval, exceptional=False): self.member_property = member_property self.exceptional = exceptional - def __get__(self, obj, type=None): + def __get__(self, instance, owner=None): if self.exceptional: raise AttributeError(self.classval) - if obj is None: + if instance is None: return self.classval - return self.member_property.__get__(obj, type) + return self.member_property.__get__(instance, owner) @property def __set__(self): # pylint: disable=unexpected-special-method-signature diff --git a/graphblas/io.py b/graphblas/io.py index 0aeb174e8..32a5f3f0d 100644 --- a/graphblas/io.py +++ b/graphblas/io.py @@ -467,6 +467,7 @@ def to_awkward(A, format=None): """ try: # awkward version 1 + # MAINT: we can probably drop awkward v1 at the end of 2024 or 2025 import awkward._v2 as ak from awkward._v2.forms.listoffsetform import ListOffsetForm from awkward._v2.forms.numpyform import NumpyForm diff --git a/graphblas/monoid/numpy.py b/graphblas/monoid/numpy.py index 702151551..475266d5c 100644 --- a/graphblas/monoid/numpy.py +++ b/graphblas/monoid/numpy.py @@ -91,6 +91,7 @@ ): # Incorrect behavior was introduced in numba 0.56.2 and numpy 1.23 # See: https://github.com/numba/numba/issues/8478 + # MAINT: we may be able to remove the behavior-based check above in 2025 _monoid_identities["fmax"].update( { "BOOL": False, diff --git a/graphblas/tests/test_vector.py b/graphblas/tests/test_vector.py index f373e39a9..0fda52508 100644 --- a/graphblas/tests/test_vector.py +++ b/graphblas/tests/test_vector.py @@ -1727,7 +1727,7 @@ def test_ss_random(v): elif r.isequal(r4): seen.add("r4") else: # pragma: no cover (sanity) - raise AssertionError() + raise AssertionError if len(seen) == 4: break for k in range(1, v.nvals + 1): diff --git a/pyproject.toml b/pyproject.toml index 3eba0f076..7dea49e98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -138,10 +138,11 @@ filterwarnings = [ # See: https://docs.python.org/3/library/warnings.html#describing-warning-filters # and: https://docs.pytest.org/en/7.2.x/how-to/capture-warnings.html#controlling-warnings "error", + # MAINT: we can drop support for sparse <0.13 at any time "ignore:`np.bool` is a deprecated alias:DeprecationWarning:sparse._umath", # sparse <0.13 # setuptools v67.3.0 deprecated `pkg_resources.declare_namespace` on 13 Feb 2023. See: # https://setuptools.pypa.io/en/latest/history.html#v67-3-0 - # TODO: check if this is still necessary in 2025 + # MAINT: check if this is still necessary in 2025 "ignore:Deprecated call to `pkg_resources.declare_namespace:DeprecationWarning:pkg_resources", ] @@ -177,42 +178,44 @@ select = [ "F", # pyflakes "E", # pycodestyle Error "W", # pycodestyle Warning - # "C90", # mccabe - # "I", # isort - # "N", # pep8-naming + # "C90", # mccabe (Too strict, but maybe we should make things less complex) + # "I", # isort (Should we replace `isort` with this?) + "N", # pep8-naming "D", # pydocstyle "UP", # pyupgrade "YTT", # flake8-2020 - # "ANN", # flake8-annotations - "S", # bandit - # "BLE", # flake8-blind-except - # "FBT", # flake8-boolean-trap + # "ANN", # flake8-annotations (We don't use annotations yet) + "S", # x. bandit + # "BLE", # flake8-blind-except (Maybe consider) + # "FBT", # flake8-boolean-trap (Why?) "B", # flake8-bugbear - # "A", # flake8-builtins + "A", # flake8-builtins "COM", # flake8-commas "C4", # flake8-comprehensions "DTZ", # flake8-datetimez "T10", # flake8-debugger - # "DJ", # flake8-django - # "EM", # flake8-errmsg + # "DJ", # flake8-django (We don't use django) + # "EM", # xx. flake8-errmsg (Perhaps nicer, but too much work) "EXE", # flake8-executable "ISC", # flake8-implicit-str-concat - # "ICN", # flake8-import-conventions + # "ICN", # flake8-import-conventions (Doesn't allow "_" prefix such as `_np`) "G", # flake8-logging-format - # "INP", # flake8-no-pep420 + "INP", # flake8-no-pep420 "PIE", # flake8-pie "T20", # flake8-print - # "PYI", # flake8-pyi + # "PYI", # flake8-pyi (We don't have stub files yet) "PT", # flake8-pytest-style - "Q", # flake8-quotes - # "RET", # flake8-return + "Q", # xxx. flake8-quotes + "RSE", # flake8-raise + "RET", # flake8-return + # "SLF", # flake8-self (We can use our own private variables--sheesh!) "SIM", # flake8-simplify - # "TID", # flake8-tidy-imports + # "TID", # flake8-tidy-imports (Rely on isort and our own judgement) "TCH", # flake8-type-checking - # "ARG", # flake8-unused-arguments - # "PTH", # flake8-use-pathlib - # "ERA", # eradicate - # "PD", # pandas-vet + # "ARG", # flake8-unused-arguments (Sometimes helpful, but too strict) + # "PTH", # flake8-use-pathlib (Is there a strong argument for this?) + # "ERA", # eradicate (We like code in comments!) + # "PD", # xl. pandas-vet (Intended for scripts that use pandas, not libraries) "PGH", # pygrep-hooks "PL", # pylint "PLC", # pylint Convention @@ -220,8 +223,6 @@ select = [ "PLR", # pylint Refactor "PLW", # pylint Warning "TRY", # tryceratops - # "RSE", # flake8-raise - # "SLF", # flake8-self "NPY", # NumPy-specific rules "RUF", # ruff-specific rules ] @@ -252,14 +253,21 @@ ignore = [ "COM812", # Trailing comma missing "D203", # 1 blank line required before class docstring (Note: conflicts with D211, which is preferred) "D400", # First line should end with a period (Note: prefer D415, which also allows "?" and "!") + "N801", # Class name ... should use CapWords convention (Note:we have a few exceptions to this) + "N802", # Function name ... should be lowercase + "N803", # Argument name ... should be lowercase (Maybe okay--except in tests) + "N806", # Variable ... in function should be lowercase + "N807", # Function name should not start and end with `__` + "N818", # Exception name ... should be named with an Error suffix (Note: good advice) "PLR0911", # Too many return statements "PLR0912", # Too many branches "PLR0913", # Too many arguments to function call "PLR0915", # Too many statements "PLR2004", # Magic number used in comparison, consider replacing magic with a constant variable - "PT001", # Use `@pytest.fixture()` over `@pytest.fixture` (Note: why?) "PT003", # `scope='function'` is implied in `@pytest.fixture()` (Note: no harm in being explicit) - "PT023", # Use `@pytest.mark.slow()` over `@pytest.mark.slow` (Note: why?) + "RET502", # Do not implicitly `return None` in function able to return non-`None` value + "RET503", # Missing explicit `return` at the end of function able to return non-`None` value + "RET504", # Unnecessary variable assignment before `return` statement "S110", # `try`-`except`-`pass` detected, consider logging the exception (Note: good advice, but we don't log) "S112", # `try`-`except`-`continue` detected, consider logging the exception (Note: good advice, but we don't log) "SIM102", # Use a single `if` statement instead of nested `if` statements (Note: often necessary) @@ -275,7 +283,19 @@ ignore = [ "graphblas/tests/test_dtype.py" = ["UP003"] "graphblas/tests/test_formatting.py" = ["E501"] "graphblas/**/__init__.py" = ["F401"] +"scripts/*.py" = ["INP001"] "scripts/create_pickle.py" = ["F403", "F405"] +"docs/*.py" = ["INP001"] + +[tool.ruff.flake8-builtins] +builtins-ignorelist = ["copyright", "format", "min", "max"] + +[tool.ruff.flake8-pytest-style] +fixture-parentheses = false +mark-parentheses = false + +[tool.ruff.pydocstyle] +convention = "numpy" [tool.pylint.messages_control] # To run a single check, do: pylint graphblas --disable E,W,R,C,I --enable assignment-from-no-return diff --git a/scripts/check_versions.sh b/scripts/check_versions.sh index 14f39f18c..ff7c88b32 100755 --- a/scripts/check_versions.sh +++ b/scripts/check_versions.sh @@ -7,7 +7,7 @@ conda search 'numpy[channel=conda-forge]>=1.24.2' conda search 'pandas[channel=conda-forge]>=1.5.3' conda search 'scipy[channel=conda-forge]>=1.10.0' conda search 'networkx[channel=conda-forge]>=3.0' -conda search 'awkward[channel=conda-forge]>=2.0.7' +conda search 'awkward[channel=conda-forge]>=2.0.8' conda search 'numba[channel=conda-forge]>=0.56.4' conda search 'pyyaml[channel=conda-forge]>=6.0' conda search 'flake8-comprehensions[channel=conda-forge]>=3.10.1'