Skip to content

Auto-generate code via e.g. python -m grblas._automethods #157

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/test_and_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ jobs:
# Test (and cover) automatic initialization
coverage run -a --branch grblas/tests/test_auto_init.py
coverage run -a --branch grblas/tests/test_external_init.py
- name: Auto-generated code check
run: |
source "$CONDA/etc/profile.d/conda.sh"
conda activate grblas
coverage run -a --branch -m grblas._automethods
coverage run -a --branch -m grblas._infixmethods
git diff --exit-code
- name: Coverage
# if: (! contains(matrix.testopts, 'pygraphblas')) || (matrix.pyver != 3.9)
env:
Expand Down
316 changes: 174 additions & 142 deletions grblas/_automethods.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,147 +4,8 @@

To automatically create the functions, run:

```python

common = {
"_name_html",
"_nvals",
"gb_obj",
"isclose",
"isequal",
"name",
"nvals",
"to_pygraphblas",
"wait",
}
scalar = {
"__array__",
"__bool__",
"__complex__",
"__eq__",
"__float__",
"__index__",
"__int__",
"__invert__",
"__neg__",
"is_empty",
"value",
}
vector_matrix = {
"S",
"V",
"__and__",
"__contains__",
"__getitem__",
"__iter__",
"__matmul__",
"__or__",
"__rand__",
"__rmatmul__",
"__ror__",
"_carg",
"apply",
"ewise_add",
"ewise_mult",
"ss",
"to_values",
}
vector = {
"inner",
"outer",
"reduce",
"vxm",
}
matrix = {
"T",
"kronecker",
"mxm",
"mxv",
"reduce_columns",
"reduce_rows",
"reduce_columnwise",
"reduce_rowwise",
"reduce_scalar",
}
common_raises = set()
scalar_raises = {
"__and__",
"__matmul__",
"__or__",
"__rand__",
"__rmatmul__",
"__ror__",
}
vector_matrix_raises = {
"__array__",
"__bool__",
}
has_defaults = {
"__eq__",
}
# no inplace math for expressions
bad_sugar = {
"__iadd__",
"__ifloordiv__",
"__imod__",
"__imul__",
"__ipow__",
"__isub__",
"__itruediv__",
"__ixor__",
"__ior__",
"__iand__",
"__imatmul__",
}
# Copy the result of this below
for name in sorted(common | scalar | vector_matrix | vector | matrix):
print(f"def {name}(self):")
if name in has_defaults:
print(f" return self._get_value({name!r}, default{name})\n\n")
else:
print(f" return self._get_value({name!r})\n\n")
for name in sorted(bad_sugar):
print(f"def {name}(self, other):")
print(f' raise TypeError(f"{name!r} not supported for {{type(self).__name__}}")\n\n')


# Copy to scalar.py and infix.py
print(" _get_value = _automethods._get_value")
for name in sorted(common | scalar):
print(f" {name} = wrapdoc(Scalar.{name})(property(_automethods.{name}))")
if name == "name":
print(" name = name.setter(_automethods._set_name)")
print(" # These raise exceptions")
for name in sorted(common_raises | scalar_raises):
print(f" {name} = wrapdoc(Scalar.{name})(Scalar.{name})")
print()

# Copy to vector.py and infix.py
print(" _get_value = _automethods._get_value")
for name in sorted(common | vector_matrix | vector):
print(f" {name} = wrapdoc(Vector.{name})(property(_automethods.{name}))")
if name == "name":
print(" name = name.setter(_automethods._set_name)")
print(" # These raise exceptions")
for name in sorted(common_raises | vector_matrix_raises):
print(f" {name} = wrapdoc(Vector.{name})(Vector.{name})")
for name in sorted(bad_sugar):
print(f" {name} = _automethods.{name}")
print()

# Copy to matrix.py and infix.py
print(" _get_value = _automethods._get_value")
for name in sorted(common | vector_matrix | matrix):
print(f" {name} = wrapdoc(Matrix.{name})(property(_automethods.{name}))")
if name == "name":
print(" name = name.setter(_automethods._set_name)")
print(" # These raise exceptions")
for name in sorted(common_raises | vector_matrix_raises):
print(f" {name} = wrapdoc(Matrix.{name})(Matrix.{name})")
for name in sorted(bad_sugar):
print(f" {name} = _automethods.{name}")

```
$ python -m grblas._automethods

"""
from . import config

Expand Down Expand Up @@ -180,7 +41,7 @@ def default__eq__(self, other):
)


# Paste here
# Begin auto-generated code
def S(self):
return self._get_value("S")

Expand Down Expand Up @@ -423,3 +284,174 @@ def __itruediv__(self, other):

def __ixor__(self, other):
raise TypeError(f"'__ixor__' not supported for {type(self).__name__}")


# End auto-generated code
if __name__ == "__main__":
import os

from .utils import _autogenerate_code

common = {
"_name_html",
"_nvals",
"gb_obj",
"isclose",
"isequal",
"name",
"nvals",
"to_pygraphblas",
"wait",
}
scalar = {
"__array__",
"__bool__",
"__complex__",
"__eq__",
"__float__",
"__index__",
"__int__",
"__invert__",
"__neg__",
"is_empty",
"value",
}
vector_matrix = {
"S",
"V",
"__and__",
"__contains__",
"__getitem__",
"__iter__",
"__matmul__",
"__or__",
"__rand__",
"__rmatmul__",
"__ror__",
"_carg",
"apply",
"ewise_add",
"ewise_mult",
"ss",
"to_values",
}
vector = {
"inner",
"outer",
"reduce",
"vxm",
}
matrix = {
"T",
"kronecker",
"mxm",
"mxv",
"reduce_columns",
"reduce_rows",
"reduce_columnwise",
"reduce_rowwise",
"reduce_scalar",
}
common_raises = set()
scalar_raises = {
"__and__",
"__matmul__",
"__or__",
"__rand__",
"__rmatmul__",
"__ror__",
}
vector_matrix_raises = {
"__array__",
"__bool__",
}
has_defaults = {
"__eq__",
}
# no inplace math for expressions
bad_sugar = {
"__iadd__",
"__ifloordiv__",
"__imod__",
"__imul__",
"__ipow__",
"__isub__",
"__itruediv__",
"__ixor__",
"__ior__",
"__iand__",
"__imatmul__",
}
# Copy the result of this above
lines = []
for name in sorted(common | scalar | vector_matrix | vector | matrix): # noqa
lines.append(f"def {name}(self):")
if name in has_defaults:
lines.append(f" return self._get_value({name!r}, default{name})\n\n")
else:
lines.append(f" return self._get_value({name!r})\n\n")
for name in sorted(bad_sugar):
lines.append(f"def {name}(self, other):")
lines.append(
f' raise TypeError(f"{name!r} not supported for {{type(self).__name__}}")\n\n'
)

_autogenerate_code(__file__, "\n".join(lines))

# Copy to scalar.py and infix.py
lines = []
lines.append(" _get_value = _automethods._get_value")
for name in sorted(common | scalar):
lines.append(f" {name} = wrapdoc(Scalar.{name})(property(_automethods.{name}))")
if name == "name":
lines.append(" name = name.setter(_automethods._set_name)")
lines.append(" # These raise exceptions")
for name in sorted(common_raises | scalar_raises):
lines.append(f" {name} = wrapdoc(Scalar.{name})(Scalar.{name})")

thisdir = os.path.dirname(__file__)
infix_exclude = {"_get_value"}

def get_name(line):
return line.strip().split(" ", 1)[0]

text = "\n".join(lines) + "\n "
_autogenerate_code(os.path.join(thisdir, "scalar.py"), text, "Scalar")
text = "\n".join(line for line in lines if get_name(line) not in infix_exclude) + "\n "
_autogenerate_code(os.path.join(thisdir, "infix.py"), text, "Scalar")

# Copy to vector.py and infix.py
lines = []
lines.append(" _get_value = _automethods._get_value")
for name in sorted(common | vector_matrix | vector):
lines.append(f" {name} = wrapdoc(Vector.{name})(property(_automethods.{name}))")
if name == "name":
lines.append(" name = name.setter(_automethods._set_name)")
lines.append(" # These raise exceptions")
for name in sorted(common_raises | vector_matrix_raises):
lines.append(f" {name} = wrapdoc(Vector.{name})(Vector.{name})")
for name in sorted(bad_sugar):
lines.append(f" {name} = _automethods.{name}")

text = "\n".join(lines) + "\n "
_autogenerate_code(os.path.join(thisdir, "vector.py"), text, "Vector")
text = "\n".join(line for line in lines if get_name(line) not in infix_exclude) + "\n "
_autogenerate_code(os.path.join(thisdir, "infix.py"), text, "Vector")

# Copy to matrix.py and infix.py
lines = []
lines.append(" _get_value = _automethods._get_value")
for name in sorted(common | vector_matrix | matrix):
lines.append(f" {name} = wrapdoc(Matrix.{name})(property(_automethods.{name}))")
if name == "name":
lines.append(" name = name.setter(_automethods._set_name)")
lines.append(" # These raise exceptions")
for name in sorted(common_raises | vector_matrix_raises):
lines.append(f" {name} = wrapdoc(Matrix.{name})(Matrix.{name})")
for name in sorted(bad_sugar):
lines.append(f" {name} = _automethods.{name}")

text = "\n".join(lines) + "\n "
_autogenerate_code(os.path.join(thisdir, "matrix.py"), text, "Matrix")
text = "\n".join(line for line in lines if get_name(line) not in infix_exclude) + "\n "
_autogenerate_code(os.path.join(thisdir, "infix.py"), text, "Matrix")
Loading