Skip to content

Commit

Permalink
Merge branch 'fix-hashing'
Browse files Browse the repository at this point in the history
  • Loading branch information
coldfix committed Sep 6, 2018
2 parents 59efbf2 + 31e103f commit 39f386e
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 8 deletions.
22 changes: 16 additions & 6 deletions rpyc/core/netref.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@

_local_netref_attrs = frozenset([
'____conn__', '____oid__', '____refcount__', '__class__', '__cmp__', '__del__', '__delattr__',
'__dir__', '__doc__', '__getattr__', '__getattribute__', '__methods__',
'__dir__', '__doc__', '__getattr__', '__getattribute__', '__hash__',
'__init__', '__metaclass__', '__module__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__slots__', '__str__',
'__weakref__', '__dict__', '__members__', '__exit__',
'__weakref__', '__dict__', '__members__', '__methods__', '__exit__',
'__eq__', '__ne__', '__lt__', '__gt__', '__le__', '__ge__',
]) | _deleted_netref_attrs
"""the set of attributes that are local to the netref object"""

Expand Down Expand Up @@ -145,8 +146,6 @@ def __getattribute__(self, name):
raise AttributeError()
else:
return object.__getattribute__(self, name)
elif name == "__hash__":
return object.__getattribute__(self, "__hash__")
elif name == "__call__": # IronPython issue #10
return object.__getattribute__(self, "__call__")
elif name == "__array__":
Expand Down Expand Up @@ -174,7 +173,19 @@ def __dir__(self):
def __hash__(self):
return syncreq(self, consts.HANDLE_HASH)
def __cmp__(self, other):
return syncreq(self, consts.HANDLE_CMP, other)
return syncreq(self, consts.HANDLE_CMP, other, '__cmp__')
def __eq__(self, other):
return syncreq(self, consts.HANDLE_CMP, other, '__eq__')
def __ne__(self, other):
return syncreq(self, consts.HANDLE_CMP, other, '__ne__')
def __lt__(self, other):
return syncreq(self, consts.HANDLE_CMP, other, '__lt__')
def __gt__(self, other):
return syncreq(self, consts.HANDLE_CMP, other, '__gt__')
def __le__(self, other):
return syncreq(self, consts.HANDLE_CMP, other, '__le__')
def __ge__(self, other):
return syncreq(self, consts.HANDLE_CMP, other, '__ge__')
def __repr__(self):
return syncreq(self, consts.HANDLE_REPR)
def __str__(self):
Expand Down Expand Up @@ -280,4 +291,3 @@ def class_factory(clsname, modname, methods):
for cls in _builtin_types:
builtin_classes_cache[cls.__name__, cls.__module__] = class_factory(
cls.__name__, cls.__module__, inspect_methods(cls))

4 changes: 2 additions & 2 deletions rpyc/core/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,11 +577,11 @@ def _handle_repr(self, obj):
return repr(obj)
def _handle_str(self, obj):
return str(obj)
def _handle_cmp(self, obj, other):
def _handle_cmp(self, obj, other, op='__cmp__'):
# cmp() might enter recursive resonance... yet another workaround
#return cmp(obj, other)
try:
return type(obj).__cmp__(obj, other)
return getattr(type(obj), op)(obj, other)
except (AttributeError, TypeError):
return NotImplemented
def _handle_hash(self, obj):
Expand Down
69 changes: 69 additions & 0 deletions tests/test_magic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import sys
import rpyc
import unittest

is_py3 = sys.version_info >= (3,)

class Meta(type):

def __hash__(self):
return 4321

Base = Meta('Base', (object,), {})

class Foo(Base):
def __hash__(self):
return 1234

class Bar(Foo):
pass

class Mux(Foo):
def __eq__(self, other):
return True


class TestContextManagers(unittest.TestCase):
def setUp(self):
self.conn = rpyc.classic.connect_thread()

def tearDown(self):
self.conn.close()

def test_hash_class(self):
hesh = self.conn.builtins.hash
mod = self.conn.modules.test_magic
self.assertEqual(hash(mod.Base), 4321)
self.assertEqual(hash(mod.Foo), 4321)
self.assertEqual(hash(mod.Bar), 4321)
self.assertEqual(hash(mod.Base().__class__), 4321)
self.assertEqual(hash(mod.Foo().__class__), 4321)
self.assertEqual(hash(mod.Bar().__class__), 4321)

basecl_ = mod.Foo().__class__.__mro__[1]
object_ = mod.Foo().__class__.__mro__[2]
self.assertEqual(hash(basecl_), hesh(basecl_))
self.assertEqual(hash(object_), hesh(object_))
self.assertEqual(hash(object_), hesh(self.conn.builtins.object))

def test_hash_obj(self):
hesh = self.conn.builtins.hash
mod = self.conn.modules.test_magic
obj = mod.Base()

self.assertNotEqual(hash(obj), 1234)
self.assertNotEqual(hash(obj), 4321)
self.assertEqual(hash(obj), hesh(obj))

self.assertEqual(hash(mod.Foo()), 1234)
self.assertEqual(hash(mod.Bar()), 1234)
if is_py3:
# py3 implicitly adds '__hash__=None' during class construction
# if '__eq__ is defined:
self.assertRaises(TypeError, lambda: hash(mod.Mux()))
else:
self.assertEqual(hash(mod.Mux()), 1234)


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

0 comments on commit 39f386e

Please sign in to comment.