Skip to content

Commit

Permalink
Merge pull request #427 from Jongy/fix-builtins-not-in-module
Browse files Browse the repository at this point in the history
Lookup classes in normalized builtins before trying sys.modules
  • Loading branch information
comrumino committed Jan 11, 2021
2 parents 2bfa739 + 108ff8e commit 6bc3bf0
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 14 deletions.
35 changes: 21 additions & 14 deletions rpyc/core/netref.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
]) | DELETED_ATTRS

"""a list of types considered built-in (shared between connections)
TODO: with the deprecation of py2 support, why not iterate the members of the builtins module?
this is needed because iterating the members of the builtins module is not enough,
some types (e.g NoneType) are not members of the builtins module.
TODO: this list is not complete.
"""
_builtin_types = [
type, object, bool, complex, dict, float, int, list, slice, str, tuple, set,
Expand Down Expand Up @@ -301,19 +303,24 @@ def class_factory(id_pack, methods):
name_pack = id_pack[0]
class_descriptor = None
if name_pack is not None:
# attempt to resolve __class__ using sys.modules (i.e. builtins and imported modules)
_module = None
cursor = len(name_pack)
while cursor != -1:
_module = sys.modules.get(name_pack[:cursor])
if _module is None:
cursor = name_pack[:cursor].rfind('.')
continue
_class_name = name_pack[cursor + 1:]
_class = getattr(_module, _class_name, None)
if _class is not None and hasattr(_class, '__class__'):
class_descriptor = NetrefClass(_class)
break
# attempt to resolve __class__ using normalized builtins first
_builtin_class = _normalized_builtin_types.get(name_pack)
if _builtin_class is not None:
class_descriptor = NetrefClass(_builtin_class)
# then by imported modules (this also tries all builtins under "builtins")
else:
_module = None
cursor = len(name_pack)
while cursor != -1:
_module = sys.modules.get(name_pack[:cursor])
if _module is None:
cursor = name_pack[:cursor].rfind('.')
continue
_class_name = name_pack[cursor + 1:]
_class = getattr(_module, _class_name, None)
if _class is not None and hasattr(_class, '__class__'):
class_descriptor = NetrefClass(_class)
break
ns['__class__'] = class_descriptor
netref_name = class_descriptor.owner.__name__ if class_descriptor is not None else name_pack
# create methods that must perform a syncreq
Expand Down
13 changes: 13 additions & 0 deletions tests/test_netref_hierachy.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ def exposed_getmeta(self):
def exposed_instance(self, inst, cls):
return isinstance(inst, cls)

def exposed_getnonetype(self):
return type(None)


class Test_Netref_Hierarchy(unittest.TestCase):

Expand Down Expand Up @@ -97,6 +100,16 @@ def test_instancecheck_list(self):
self.assertTrue(conn.root.instance(remote_list, list))
conn.close()

def test_instancecheck_none(self):
"""
test for the regression reported in https://github.com/tomerfiliba-org/rpyc/issues/426
"""
service = MyService()
conn = rpyc.connect_thread(remote_service=service)
remote_NoneType = conn.root.getnonetype()
self.assertTrue(isinstance(None, remote_NoneType))
conn.close()

def test_StandardError(self):
_builtins = self.conn.modules.builtins if rpyc.lib.compat.is_py_3k else self.conn.modules.__builtin__
self.assertTrue(isinstance(_builtins.Exception(), _builtins.BaseException))
Expand Down

0 comments on commit 6bc3bf0

Please sign in to comment.