Skip to content

Commit

Permalink
Stop using pybind's implicit __hash__ (closes gh-102)
Browse files Browse the repository at this point in the history
  • Loading branch information
inducer committed Dec 30, 2022
1 parent cefffb5 commit 19162fa
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 4 deletions.
16 changes: 15 additions & 1 deletion gen_wrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -1370,6 +1370,10 @@ def write_wrapper(outf, meth):

# {{{ exposer generator

def is_hash(meth):
return meth.name == "get_hash" and len(meth.args) == 1


def write_exposer(outf, meth, arg_names, doc_str):
func_name = f"isl::{meth.cls}_{meth.name}"
py_name = meth.name
Expand All @@ -1385,7 +1389,7 @@ def write_exposer(outf, meth, arg_names, doc_str):
if meth.name == "size" and len(meth.args) == 1:
py_name = "__len__"

if meth.name == "get_hash" and len(meth.args) == 1:
if is_hash(meth):
py_name = "__hash__"

extra_py_names = []
Expand Down Expand Up @@ -1509,6 +1513,16 @@ def gen_wrapper(include_dirs, include_barvinok=False, isl_version=None):
for cls in classes
for meth in fdata.classes_to_methods.get(cls, [])])

for cls in classes:
has_isl_hash = any(
is_hash(meth) for meth in fdata.classes_to_methods.get(cls, []))

if not has_isl_hash:
# pybind11's C++ object base class has an object identity
# __hash__ that everyone inherits automatically. We don't
# want that.
expf.write(f'wrap_{cls}.attr("__hash__") = py::none();\n')

expf.close()
wrapf.close()

Expand Down
14 changes: 11 additions & 3 deletions islpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,6 @@ def generic_repr(self):
cls.__str__ = generic_str
cls.__repr__ = generic_repr

if not hasattr(cls, "__hash__"):
raise AssertionError(f"not hashable: {cls}")

# }}}

# {{{ Python set-like behavior
Expand Down Expand Up @@ -599,15 +596,26 @@ def basic_obj_get_constraints(self):
self.foreach_constraint(result.append)
return result


# {{{ BasicSet

def basic_set_hash(self):
return hash((type(self), Set.from_basic_set(self)))

BasicSet.get_constraints = basic_obj_get_constraints
assert BasicSet.__hash__ is None
BasicSet.__hash__ = basic_set_hash

# }}}

# {{{ BasicMap

def basic_map_hash(self):
return hash((type(self), Map.from_basic_map(self)))

BasicMap.get_constraints = basic_obj_get_constraints
assert BasicMap.__hash__ is None
BasicMap.__hash__ = basic_map_hash

# }}}

Expand Down
9 changes: 9 additions & 0 deletions test/test_isl.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,15 @@ def test_sched_constraints_set_validity():
assert str(validity) == str(validity2)


def test_basicset_hash():
# https://github.com/inducer/islpy/issues/102
# isl does not currently (2022-12-30) offer hashing for BasicSet.

a1 = isl.BasicSet("{[i]: 0<=i<512}")
a2 = isl.BasicSet("{[i]: 0<=i<512}")
assert hash(a1) == hash(a2)


if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
Expand Down

0 comments on commit 19162fa

Please sign in to comment.