Skip to content

Commit

Permalink
Some work towards prototyping a fix for #355. Found error in isinstan…
Browse files Browse the repository at this point in the history
…ce logic for Foo/Bar. Still a lot of cleanup and thought required for changes being made to NetrefMetaclass and BaseNetref that impact type checking. Even so, the direction looks promising

    - Run actions for develop branch too.
  • Loading branch information
comrumino committed Jan 27, 2024
1 parent 63ee434 commit 6a24242
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 5 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ name: RPyC

on:
push:
branches: [ master ]
branches:
- master
- develop
pull_request:
branches: [ master ]

Expand Down
42 changes: 40 additions & 2 deletions rpyc/core/netref.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,37 @@ def __repr__(self):
else:
return f"<netref class '{self.__name__}'>"

def __instancecheck__(cls, inst):
"""Implement isinstance(inst, cls)."""
# if 'Error' in str(type(cls)) or 'Error' in str(type(inst)): breakpoint()
if not cls.__subclass__ and type(inst).__class__ is NetrefMetaclass:
# Cases....
# protocol.py: isinstance(obj, netref.BaseNetref) and obj.____conn__ is self
return True
res = any(cls.__subclasscheck__(c) for c in {type(inst), object.__getattribute__(inst, "__class__")})
return res

def __subclasscheck__(cls, sub):
"""Implement issubclass(sub, cls)."""
candidates = type(cls).__dict__.get("__subclass__", set())
#sub_candidates = type(sub).__dict__.get("__subclass__", set())
#cls_candidates = cls.__class__
#cls_sub_candidates = sub.__class__
#if cls is BaseNetref and sub in (NetrefMetaclass, NetrefMetaclass, object, type)
if type(sub) is BaseNetref or type(sub) is NetrefMetaclass:
#sub_candidates = type(sub).__dict__.get("__subclass__", set())
if not candidates:
return False
else:
breakpoint()
#return any([sub_ in candidates for sub_ in sub_candidates])
#elif sub is type:
# return True
else:
return sub in candidates #any(c in candidates for c in sub.mro())

class BaseNetref(object, metaclass=NetrefMetaclass):

class BaseNetref(metaclass=NetrefMetaclass):
"""The base netref class, from which all netref classes derive. Some netref
classes are "pre-generated" and cached upon importing this module (those
defined in the :data:`_builtin_types`), and they are shared between all
Expand All @@ -110,6 +139,7 @@ class BaseNetref(object, metaclass=NetrefMetaclass):
remote-instance-id := id object instance (hits or misses on proxy cache)
id_pack is usually created by rpyc.lib.get_id_pack
"""
__subclass__ = set()
__slots__ = ["____conn__", "____id_pack__", "__weakref__", "____refcount__"]

def __init__(self, conn, id_pack):
Expand Down Expand Up @@ -214,6 +244,8 @@ def __instancecheck__(self, other):
return False
elif other.____id_pack__[2] != 0:
return True
elif type(self).__subclass__ and type(other).__subclass__:
return any([ssub in sub.mro() for sub in type(other).__subclass__ for ssub in type(self).__subclass__])
else:
# seems dubious if each netref proxies to a different address spaces
return syncreq(self, consts.HANDLE_INSTANCECHECK, other.____id_pack__)
Expand Down Expand Up @@ -329,7 +361,13 @@ def class_factory(id_pack, methods):
# only create methods that won't shadow BaseNetref during merge for mro
if name not in LOCAL_ATTRS: # i.e. `name != __class__`
ns[name] = _make_method(name, doc)
netref_cls = type(name_pack, (BaseNetref, ), ns)
class NetrefVirtual(NetrefMetaclass):
pass
if ns['__class__'] is not None:
ns['__subclass__'] = {ns['__class__'].instance}
else:
ns['__subclass__'] = set()
netref_cls = type(name_pack, (BaseNetref,), ns)
return netref_cls


Expand Down
7 changes: 5 additions & 2 deletions tests/test_netref_hierachy.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from rpyc.utils.server import ThreadedServer
from rpyc import SlaveService
from rpyc.core import netref
import test_magic
import unittest


Expand Down Expand Up @@ -114,7 +115,8 @@ def test_instancecheck_across_connections(self):
bar = self.conn.modules.test_magic.Bar()
self.assertTrue(isinstance(foo, self.conn.modules.test_magic.Foo))
self.assertTrue(isinstance(bar, self.conn2.modules.test_magic.Bar))
self.assertFalse(isinstance(bar, self.conn.modules.test_magic.Foo))
self.assertTrue(isinstance(test_magic.Bar(), test_magic.Foo))
self.assertTrue(isinstance(bar, self.conn.modules.test_magic.Foo))
with self.assertRaises(TypeError):
isinstance(self.conn.modules.test_magic.Foo, bar)

Expand Down Expand Up @@ -183,9 +185,10 @@ def test_modules(self):
self.assertIs(type(self.conn.modules.unittest.__class__), type)

def test_proxy_instancecheck(self):
""" #355 """
self.assertIsInstance(self.conn.modules.builtins.RuntimeError(), Exception)
# TODO: below should pass
# self.assertIsInstance(self.conn.modules.builtins.RuntimeError(), self.conn.modules.builtins.Exception)
self.assertIsInstance(self.conn.modules.builtins.RuntimeError(), self.conn.modules.builtins.Exception)


if __name__ == '__main__':
Expand Down

0 comments on commit 6a24242

Please sign in to comment.