Skip to content

Commit

Permalink
Add convenience methods to monkey patch __repr__ methods
Browse files Browse the repository at this point in the history
  • Loading branch information
alugowski committed Aug 24, 2023
1 parent 6c0aa4d commit f6a5cee
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 1 deletion.
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ To one of these:
└ ┘
```

`mprint(A)`. Use `to_str()` for the string.
`mprint(A)`, `to_str()` for the string, or simply `A` if using monkey patching as below.

### HTML
![HTML](doc/images/html.png)
Expand Down Expand Up @@ -90,6 +90,36 @@ If you prefer LaTeX:
import matrepr.jupyter_latex
```

## Monkey Patching `__repr__`

Unlike Jupyter, the Python REPL does not have a nice way to register a formatter.

We can monkey patch a `__repl__` method into supported matrix classes for a similar effect.

This is implemented in the [matrepr.patch](matrepr/patch) module. Simply import the patch you want:

* `import matrepr.patch.scipy`
* `import matrepr.patch.graphblas`

Example:

```
>>> a = scipy.sparse.random(4, 4, density=0.5)
>>> a
<4x4 sparse matrix of type '<class 'numpy.float64'>'
with 8 stored elements in COOrdinate format>
>>> import matrepr.patch.scipy
>>> a
4×4, 8 'float64' elements, coo
0 1 2 3
┌ ┐
0 │ 0.4016 0.4412 │
1 │ 0.309 0.8055 │
2 │ 0.1982 │
3 │ 0.7438 0.6938 0.2215 │
└ ┘
```

## Arguments

All methods take the same arguments. Apart from the matrix itself:
Expand Down
3 changes: 3 additions & 0 deletions matrepr/patch/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Copyright (C) 2023 Adam Lugowski.
# Use of this source code is governed by the BSD 2-clause license found in the LICENSE.txt file.
# SPDX-License-Identifier: BSD-2-Clause
22 changes: 22 additions & 0 deletions matrepr/patch/graphblas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright (C) 2023 Adam Lugowski.
# Use of this source code is governed by the BSD 2-clause license found in the LICENSE.txt file.
# SPDX-License-Identifier: BSD-2-Clause
"""
Import this module to automatically print python-graphblas `Matrix` and `Vector` using MatRepr.
A `__repr__` method that calls MatRepr is inserted to those classes.
Also includes all public methods from the matrepr module for convenient one-line imports.
"""

from .. import *

import graphblas


def _str_(mat):
return to_str(mat)


graphblas.Matrix.__repr__ = _str_
graphblas.Vector.__repr__ = _str_
41 changes: 41 additions & 0 deletions matrepr/patch/scipy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright (C) 2023 Adam Lugowski.
# Use of this source code is governed by the BSD 2-clause license found in the LICENSE.txt file.
# SPDX-License-Identifier: BSD-2-Clause
"""
Import this module to automatically print SciPy sparse matrices using MatRepr.
A `__repr__` method that calls MatRepr is inserted to all SciPy sparse matrix classes.
Also includes all public methods from the matrepr module for convenient one-line imports.
"""

from .. import *

import scipy.sparse


def _str_(mat):
return to_str(mat)


try:
scipy.sparse.bsr_matrix.__repr__ = _str_
scipy.sparse.coo_matrix.__repr__ = _str_
scipy.sparse.csc_matrix.__repr__ = _str_
scipy.sparse.csr_matrix.__repr__ = _str_
scipy.sparse.dia_matrix.__repr__ = _str_
scipy.sparse.dok_matrix.__repr__ = _str_
scipy.sparse.lil_matrix.__repr__ = _str_
except AttributeError:
pass

Check warning on line 30 in matrepr/patch/scipy.py

View check run for this annotation

Codecov / codecov/patch

matrepr/patch/scipy.py#L29-L30

Added lines #L29 - L30 were not covered by tests

try:
scipy.sparse.bsr_array.__repr__ = _str_
scipy.sparse.coo_array.__repr__ = _str_
scipy.sparse.csc_array.__repr__ = _str_
scipy.sparse.csr_array.__repr__ = _str_
scipy.sparse.dia_array.__repr__ = _str_
scipy.sparse.dok_array.__repr__ = _str_
scipy.sparse.lil_array.__repr__ = _str_
except AttributeError:
pass
18 changes: 18 additions & 0 deletions tests/test_graphblas.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,23 @@ def test_truncate(self):
self.assertIn(f"<td>{value}</td>", res)


@unittest.skipIf(not have_gb, "python-graphblas not installed")
class PatchGraphBLASTests(unittest.TestCase):
def test_patch_graphblas(self):
mat = gb.Matrix.from_coo([0, 1], [0, 1], [111, 222], nrows=5, ncols=5),
vec = gb.Vector.from_coo([0, 1], [111, 222], size=8)

# noinspection PyUnresolvedReferences
import matrepr.patch.graphblas

res = repr(mat)
self.assertIn("222", res)
self.assertIn("┌", res) # a character used by MatRepr

res = repr(vec)
self.assertIn("222", res)
self.assertIn("┌", res) # a character used by MatRepr


if __name__ == '__main__':
unittest.main()
14 changes: 14 additions & 0 deletions tests/test_scipy.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,19 @@ def test_formats(self):
self.assertEqual(expected[i], res)


class PatchSciPyTests(unittest.TestCase):
def test_patch_scipy(self):
source_mat = scipy.sparse.coo_matrix(([111, 222], ([0, 1], [0, 1])), shape=(10, 10))

# noinspection PyUnresolvedReferences
import matrepr.patch.scipy

for fmt in ["coo", "csr", "csc", "dok", "lil"]:
mat = source_mat.asformat(fmt)
res = repr(mat)
self.assertIn("222", res)
self.assertIn("┌", res) # a character used by MatRepr


if __name__ == '__main__':
unittest.main()

0 comments on commit f6a5cee

Please sign in to comment.