From f340a309a6e855384459f74615c60dc3b43c36b0 Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Thu, 2 Feb 2023 11:22:25 -0600 Subject: [PATCH 1/3] `.chain` return `self` so it can be used with method-chaining --- .pre-commit-config.yaml | 6 +++--- graphblas/core/matrix.py | 1 + graphblas/core/operator.py | 6 +++--- graphblas/core/scalar.py | 1 + graphblas/core/vector.py | 1 + graphblas/tests/test_matrix.py | 6 ++++++ graphblas/tests/test_vector.py | 2 +- pyproject.toml | 1 + 8 files changed, 17 insertions(+), 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dc514793f..3db82696b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,7 +25,7 @@ repos: - id: validate-pyproject name: Validate pyproject.toml - repo: https://github.com/myint/autoflake - rev: v2.0.0 + rev: v2.0.1 hooks: - id: autoflake args: [--in-place] @@ -44,7 +44,7 @@ repos: - id: auto-walrus args: [--line-length, "100"] - repo: https://github.com/psf/black - rev: 22.12.0 + rev: 23.1.0 hooks: - id: black - id: black-jupyter @@ -71,7 +71,7 @@ repos: additional_dependencies: [tomli] files: ^(graphblas|docs)/ - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.237 + rev: v0.0.239 hooks: - id: ruff - repo: https://github.com/sphinx-contrib/sphinx-lint diff --git a/graphblas/core/matrix.py b/graphblas/core/matrix.py index 9dbfd2320..803f87708 100644 --- a/graphblas/core/matrix.py +++ b/graphblas/core/matrix.py @@ -735,6 +735,7 @@ def wait(self, how="materialize"): else: raise ValueError(f'`how` argument must be "materialize" or "complete"; got {how!r}') call("GrB_Matrix_wait", [self, mode]) + return self def get(self, row, col, default=None): """Get an element at (``row``, ``col``) indices as a Python scalar. diff --git a/graphblas/core/operator.py b/graphblas/core/operator.py index 6a726cc0c..02f9bff52 100644 --- a/graphblas/core/operator.py +++ b/graphblas/core/operator.py @@ -1313,7 +1313,7 @@ def _initialize(cls): op._typed_ops[dtype] = typed_op op.coercions[dtype] = target_type # Allow some functions to work on UDTs - for (unop, func) in [ + for unop, func in [ (unary.identity, _identity), (unary.one, _one), ]: @@ -2287,7 +2287,7 @@ def _initialize(cls): # If the inputs are FP32, we use DIV_FP32; use DIV_FP64 for all other input dtypes truediv = binary.truediv = op.truediv = BinaryOp("truediv") rtruediv = binary.rtruediv = op.rtruediv = BinaryOp("rtruediv") - for (new_op, builtin_op) in [(truediv, binary.cdiv), (rtruediv, binary.rdiv)]: + for new_op, builtin_op in [(truediv, binary.cdiv), (rtruediv, binary.rdiv)]: for dtype in builtin_op.types: if dtype.name in {"FP32", "FC32", "FC64"}: orig_dtype = dtype @@ -2420,7 +2420,7 @@ def _initialize(cls): left._semiring_commutes_to = right right._semiring_commutes_to = left # Allow some functions to work on UDTs - for (binop, func) in [ + for binop, func in [ (binary.first, _first), (binary.second, _second), (binary.pair, _pair), diff --git a/graphblas/core/scalar.py b/graphblas/core/scalar.py index cc34e27e2..56064534d 100644 --- a/graphblas/core/scalar.py +++ b/graphblas/core/scalar.py @@ -465,6 +465,7 @@ def wait(self, how="materialize"): raise ValueError(f'`how` argument must be "materialize" or "complete"; got {how!r}') if not self._is_cscalar: call("GrB_Scalar_wait", [self, mode]) + return self def get(self, default=None): """Get the internal value of the Scalar as a Python scalar. diff --git a/graphblas/core/vector.py b/graphblas/core/vector.py index e0d55cc99..ff053e93a 100644 --- a/graphblas/core/vector.py +++ b/graphblas/core/vector.py @@ -624,6 +624,7 @@ def wait(self, how="materialize"): else: raise ValueError(f'`how` argument must be "materialize" or "complete"; got {how!r}') call("GrB_Vector_wait", [self, mode]) + return self def get(self, index, default=None): """Get an element at ``index`` as a Python scalar. diff --git a/graphblas/tests/test_matrix.py b/graphblas/tests/test_matrix.py index 57bf6e5e3..8f2d7881f 100644 --- a/graphblas/tests/test_matrix.py +++ b/graphblas/tests/test_matrix.py @@ -4231,3 +4231,9 @@ def test_ss_descriptors(A): else: with pytest.raises(ValueError, match="escriptor"): (A @ A).new(nthreads=4, axb_method="dot", sort=True) + + +@autocompute +def test_wait_chains(A): + result = A.wait().T.wait().reduce_rowwise().wait().reduce().wait() + assert result == 47 diff --git a/graphblas/tests/test_vector.py b/graphblas/tests/test_vector.py index c7f90c10c..0c41729bd 100644 --- a/graphblas/tests/test_vector.py +++ b/graphblas/tests/test_vector.py @@ -1316,7 +1316,7 @@ def import_func(x, import_name, **kwargs): w(w.S) << 1 w_orig = w.dup() format = "full" - for (raw, import_format, give_ownership, take_ownership, import_name) in itertools.product( + for raw, import_format, give_ownership, take_ownership, import_name in itertools.product( [False, True], [format, None], [False, True], diff --git a/pyproject.toml b/pyproject.toml index d88f1bdc8..fc3877193 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -242,6 +242,7 @@ ignore = [ # Intentionally ignored "COM812", # Trailing comma missing "D203", # 1 blank line required before class docstring (Note: conflicts with D211, which is preferred) + "PLR0913", # Too many arguments to function call "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) From 4d1577279fd5fba52e069dfe8811f8be955b0067 Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Fri, 3 Feb 2023 21:29:43 -0600 Subject: [PATCH 2/3] Also return `self` with `build/clear/resize` --- .pre-commit-config.yaml | 2 +- graphblas/core/matrix.py | 5 ++++- graphblas/core/scalar.py | 3 ++- graphblas/core/vector.py | 5 ++++- pyproject.toml | 3 +++ 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3db82696b..4eb2db4d0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -71,7 +71,7 @@ repos: additional_dependencies: [tomli] files: ^(graphblas|docs)/ - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.239 + rev: v0.0.241 hooks: - id: ruff - repo: https://github.com/sphinx-contrib/sphinx-lint diff --git a/graphblas/core/matrix.py b/graphblas/core/matrix.py index 803f87708..76e0671b1 100644 --- a/graphblas/core/matrix.py +++ b/graphblas/core/matrix.py @@ -426,6 +426,7 @@ def clear(self): After the call, :attr:`nvals` will return 0. The :attr:`shape` will not change. """ call("GrB_Matrix_clear", [self]) + return self def resize(self, nrows, ncols): """In-place operation which changes the :attr:`shape`. @@ -439,6 +440,7 @@ def resize(self, nrows, ncols): call("GrB_Matrix_resize", [self, nrows, ncols]) self._nrows = nrows.value self._ncols = ncols.value + return self def to_values(self, dtype=None, *, rows=True, columns=True, values=True, sort=True): """Extract the indices and values as a 3-tuple of numpy arrays @@ -605,7 +607,7 @@ def build(self, rows, columns, values, *, dup_op=None, clear=False, nrows=None, ncols = self._ncols self.resize(nrows, ncols) if n == 0: - return + return self dup_op_given = dup_op is not None if not dup_op_given: @@ -631,6 +633,7 @@ def build(self, rows, columns, values, *, dup_op=None, clear=False, nrows=None, # Check for duplicates when dup_op was not provided if not dup_op_given and self._nvals < n: raise ValueError("Duplicate indices found, must provide `dup_op` BinaryOp") + return self def dup(self, dtype=None, *, clear=False, mask=None, name=None, **opts): """Create a duplicate of the Matrix. diff --git a/graphblas/core/scalar.py b/graphblas/core/scalar.py index 56064534d..85fbf2ff2 100644 --- a/graphblas/core/scalar.py +++ b/graphblas/core/scalar.py @@ -278,11 +278,12 @@ def clear(self): After the call, :attr:`nvals` will return 0. """ if self._is_empty: - return + return self if self._is_cscalar: self._empty = True else: call("GrB_Scalar_clear", [self]) + return self @property def is_empty(self): diff --git a/graphblas/core/vector.py b/graphblas/core/vector.py index ff053e93a..72f368188 100644 --- a/graphblas/core/vector.py +++ b/graphblas/core/vector.py @@ -396,6 +396,7 @@ def clear(self): After the call, :attr:`nvals` will return 0. The :attr:`size` will not change. """ call("GrB_Vector_clear", [self]) + return self def resize(self, size): """In-place operation which changes the :attr:`size`. @@ -406,6 +407,7 @@ def resize(self, size): size = _as_scalar(size, _INDEX, is_cscalar=True) call("GrB_Vector_resize", [self, size]) self._size = size.value + return self def to_values(self, dtype=None, *, indices=True, values=True, sort=True): """Extract the indices and values as a 2-tuple of numpy arrays. @@ -515,7 +517,7 @@ def build(self, indices, values, *, dup_op=None, clear=False, size=None): if size is not None: self.resize(size) if n == 0: - return + return self dup_op_given = dup_op is not None if not dup_op_given: @@ -541,6 +543,7 @@ def build(self, indices, values, *, dup_op=None, clear=False, size=None): # Check for duplicates when dup_op was not provided if not dup_op_given and self._nvals < n: raise ValueError("Duplicate indices found, must provide `dup_op` BinaryOp") + return self def dup(self, dtype=None, *, clear=False, mask=None, name=None, **opts): """Create a duplicate of the Vector. diff --git a/pyproject.toml b/pyproject.toml index fc3877193..d23d2079a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -214,6 +214,8 @@ select = [ "PLR", # pylint Refactor "PLW", # pylint Warning "TRY", # tryceratops + # "RSE", # flake8-raise + # "SLF", # flake8-self "RUF", # ruff-specific rules ] external = [ @@ -243,6 +245,7 @@ ignore = [ "COM812", # Trailing comma missing "D203", # 1 blank line required before class docstring (Note: conflicts with D211, which is preferred) "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) From 18380f1d0cb0780437537401bcb9bec51a4593e4 Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Fri, 3 Feb 2023 21:34:25 -0600 Subject: [PATCH 3/3] undo --- graphblas/core/matrix.py | 5 +---- graphblas/core/scalar.py | 3 +-- graphblas/core/vector.py | 5 +---- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/graphblas/core/matrix.py b/graphblas/core/matrix.py index 76e0671b1..803f87708 100644 --- a/graphblas/core/matrix.py +++ b/graphblas/core/matrix.py @@ -426,7 +426,6 @@ def clear(self): After the call, :attr:`nvals` will return 0. The :attr:`shape` will not change. """ call("GrB_Matrix_clear", [self]) - return self def resize(self, nrows, ncols): """In-place operation which changes the :attr:`shape`. @@ -440,7 +439,6 @@ def resize(self, nrows, ncols): call("GrB_Matrix_resize", [self, nrows, ncols]) self._nrows = nrows.value self._ncols = ncols.value - return self def to_values(self, dtype=None, *, rows=True, columns=True, values=True, sort=True): """Extract the indices and values as a 3-tuple of numpy arrays @@ -607,7 +605,7 @@ def build(self, rows, columns, values, *, dup_op=None, clear=False, nrows=None, ncols = self._ncols self.resize(nrows, ncols) if n == 0: - return self + return dup_op_given = dup_op is not None if not dup_op_given: @@ -633,7 +631,6 @@ def build(self, rows, columns, values, *, dup_op=None, clear=False, nrows=None, # Check for duplicates when dup_op was not provided if not dup_op_given and self._nvals < n: raise ValueError("Duplicate indices found, must provide `dup_op` BinaryOp") - return self def dup(self, dtype=None, *, clear=False, mask=None, name=None, **opts): """Create a duplicate of the Matrix. diff --git a/graphblas/core/scalar.py b/graphblas/core/scalar.py index 85fbf2ff2..56064534d 100644 --- a/graphblas/core/scalar.py +++ b/graphblas/core/scalar.py @@ -278,12 +278,11 @@ def clear(self): After the call, :attr:`nvals` will return 0. """ if self._is_empty: - return self + return if self._is_cscalar: self._empty = True else: call("GrB_Scalar_clear", [self]) - return self @property def is_empty(self): diff --git a/graphblas/core/vector.py b/graphblas/core/vector.py index 72f368188..ff053e93a 100644 --- a/graphblas/core/vector.py +++ b/graphblas/core/vector.py @@ -396,7 +396,6 @@ def clear(self): After the call, :attr:`nvals` will return 0. The :attr:`size` will not change. """ call("GrB_Vector_clear", [self]) - return self def resize(self, size): """In-place operation which changes the :attr:`size`. @@ -407,7 +406,6 @@ def resize(self, size): size = _as_scalar(size, _INDEX, is_cscalar=True) call("GrB_Vector_resize", [self, size]) self._size = size.value - return self def to_values(self, dtype=None, *, indices=True, values=True, sort=True): """Extract the indices and values as a 2-tuple of numpy arrays. @@ -517,7 +515,7 @@ def build(self, indices, values, *, dup_op=None, clear=False, size=None): if size is not None: self.resize(size) if n == 0: - return self + return dup_op_given = dup_op is not None if not dup_op_given: @@ -543,7 +541,6 @@ def build(self, indices, values, *, dup_op=None, clear=False, size=None): # Check for duplicates when dup_op was not provided if not dup_op_given and self._nvals < n: raise ValueError("Duplicate indices found, must provide `dup_op` BinaryOp") - return self def dup(self, dtype=None, *, clear=False, mask=None, name=None, **opts): """Create a duplicate of the Vector.