diff --git a/issues/issue63_client.py b/issues/issue63_client.py new file mode 100644 index 00000000..3db9e5a8 --- /dev/null +++ b/issues/issue63_client.py @@ -0,0 +1,25 @@ +import rpyc +import time + +count = 0 + +def callbackFunc(x): + global count + count += 1 + print x, time.time() + +if __name__ == "__main__": + conn = rpyc.connect("localhost", 12000) + #rpyc.BgServingThread.SERVE_INTERVAL = 0.01 + rpyc.BgServingThread.SLEEP_INTERVAL = 0.0001 + bgsrv = rpyc.BgServingThread(conn) + + test = conn.root.RemoteCallbackTest(callbackFunc) + print test + test.start() + print "doing other things while the callback is being called" + while count < 100: + time.sleep(0.1) + print "done" + + diff --git a/issues/issue63_server.py b/issues/issue63_server.py new file mode 100644 index 00000000..dba09a81 --- /dev/null +++ b/issues/issue63_server.py @@ -0,0 +1,27 @@ +import threading +import rpyc + + +def run_something(callback): + for i in range(100): + callback(i) + +class MyService(rpyc.Service): + def on_connect(self): + print "hi", self._conn._config["endpoints"][1] + def on_disconnect(self): + print "bye", self._conn._config["endpoints"][1] + + class exposed_RemoteCallbackTest(object): + def __init__(self, callback): + self.callback = callback + def start(self): + thd = threading.Thread(target = run_something, args = (self.callback,)) + thd.start() + + +if __name__ == "__main__": + from rpyc.utils.server import ThreadedServer + myServerObj = ThreadedServer(MyService, port=12000, protocol_config={"allow_public_attrs":True}) + myServerObj.start() + diff --git a/rpyc/core/brine.py b/rpyc/core/brine.py index 7054663c..3cfd20ed 100644 --- a/rpyc/core/brine.py +++ b/rpyc/core/brine.py @@ -348,11 +348,11 @@ def load(data): return _load(stream) if is_py3k: - simple_types = frozenset([type(None), int, bool, float, bytes, str, - slice, complex, type(NotImplemented), type(Ellipsis)]) + simple_types = frozenset([type(None), int, bool, float, bytes, str, complex, + type(NotImplemented), type(Ellipsis)]) else: - simple_types = frozenset([type(None), int, long, bool, float, str, unicode, - slice, complex, type(NotImplemented), type(Ellipsis)]) + simple_types = frozenset([type(None), int, long, bool, float, str, unicode, complex, + type(NotImplemented), type(Ellipsis)]) def dumpable(obj): """Indicates whether the given object is *dumpable* by brine @@ -364,6 +364,8 @@ def dumpable(obj): return True if type(obj) in (tuple, frozenset): return all(dumpable(item) for item in obj) + if type(obj) is slice: + return dumpable(obj.start) and dumpable(obj.stop) and dumpable(obj.step) return False diff --git a/rpyc/core/netref.py b/rpyc/core/netref.py index 25783900..cd8db094 100644 --- a/rpyc/core/netref.py +++ b/rpyc/core/netref.py @@ -64,9 +64,11 @@ def syncreq(proxy, handler, *args): :raises: any exception raised by the operation will be raised :returns: the result of the operation """ - conn = object.__getattribute__(proxy, "____conn__") + conn = object.__getattribute__(proxy, "____conn__")() + if not conn: + raise ReferenceError('weakly-referenced object no longer exists') oid = object.__getattribute__(proxy, "____oid__") - return conn().sync_request(handler, oid, *args) + return conn.sync_request(handler, oid, *args) def asyncreq(proxy, handler, *args): """Performs an asynchronous request on the given proxy object. @@ -80,12 +82,11 @@ def asyncreq(proxy, handler, *args): :returns: an :class:`AsyncResult ` representing the operation """ - conn = object.__getattribute__(proxy, "____conn__") - connection = conn() - if not connection: + conn = object.__getattribute__(proxy, "____conn__")() + if not conn: raise ReferenceError('weakly-referenced object no longer exists') oid = object.__getattribute__(proxy, "____oid__") - return connection.async_request(handler, oid, *args) + return conn.async_request(handler, oid, *args) class NetrefMetaclass(type): """A *metaclass* used to customize the ``__repr__`` of ``netref`` classes. diff --git a/rpyc/core/protocol.py b/rpyc/core/protocol.py index 9c948f80..6f42fbf9 100644 --- a/rpyc/core/protocol.py +++ b/rpyc/core/protocol.py @@ -54,6 +54,7 @@ class PingError(Exception): allow_pickle = False, connid = None, credentials = None, + endpoints = None, ) """ The default configuration dictionary of the protocol. You can override these parameters @@ -64,7 +65,7 @@ class PingError(Exception): to repeat parameters whose values remain unchanged. ===================================== ================ ===================================================== -Parameter Default value Description +Parameter Default value Description ===================================== ================ ===================================================== ``allow_safe_attrs`` ``True`` Whether to allow the use of *safe* attributes (only those listed as ``safe_attrs``) @@ -87,15 +88,22 @@ class PingError(Exception): ``import_custom_exceptions`` ``False`` Whether to allow importing of exceptions from not-yet-imported modules ``instantiate_oldstyle_exceptions`` ``False`` Whether to allow instantiation of exceptions - which don't derive from ``Exception`` + which don't derive from ``Exception``. This + is not applicable for Python 3 and later. ``propagate_SystemExit_locally`` ``False`` Whether to propagate ``SystemExit`` - locally or to the other party + locally (kill the server) or to the other + party (kill the client) ``connid`` ``None`` **Runtime**: the RPyC connection ID (used mainly for debugging purposes) ``credentials`` ``None`` **Runtime**: the credentails object that was returned by the server's :ref:`authenticator ` or ``None`` +``endpoints`` ``None`` **Runtime**: The connection's endpoints. This is a tuple + made of the local socket endpoint (``getsockname``) and the + remote one (``getpeername``). This is set by the server + upon accepting a connection; client side connections + do no have this configuration option set. ===================================== ================ ===================================================== """ diff --git a/rpyc/core/vinegar.py b/rpyc/core/vinegar.py index a66745ce..68d7724d 100644 --- a/rpyc/core/vinegar.py +++ b/rpyc/core/vinegar.py @@ -91,21 +91,18 @@ class simply doesn't exist on the local machine), a :class:`GenericException` details. :param val: the dumped exception - :param import_custom_exceptions: whether to allow this function to import - custom modules (imposes a security risk) - :param instantiate_custom_exceptions: whether to allow this function to - instantiate "custom exceptions" (i.e., - not one of the built-in exceptions, + :param import_custom_exceptions: whether to allow this function to import custom modules + (imposes a security risk) + :param instantiate_custom_exceptions: whether to allow this function to instantiate "custom + exceptions" (i.e., not one of the built-in exceptions, such as ``ValueError``, ``OSError``, etc.) - :param instantiate_oldstyle_exceptions: whether to allow this function to - instantiate exception classes that - do not derive from ``BaseException``. - This is required to support old-style - exceptions. + :param instantiate_oldstyle_exceptions: whether to allow this function to instantiate exception + classes that do not derive from ``BaseException``. + This is required to support old-style exceptions. + Not applicable for Python 3 and above. :returns: A throwable exception object """ - if val == consts.EXC_STOP_ITERATION: return StopIteration # optimization if type(val) is str: @@ -121,8 +118,6 @@ class simply doesn't exist on the local machine), a :class:`GenericException` if instantiate_custom_exceptions: if modname in sys.modules: cls = getattr(sys.modules[modname], clsname, None) - elif not is_py3k and modname == "builtins": - cls = getattr(exceptions_module, clsname, None) else: cls = None elif modname == exceptions_module.__name__: @@ -130,12 +125,16 @@ class simply doesn't exist on the local machine), a :class:`GenericException` else: cls = None - if not isinstance(cls, (type, ClassType)): - cls = None - elif issubclass(cls, ClassType) and not instantiate_oldstyle_exceptions: - cls = None - elif not issubclass(cls, BaseException): - cls = None + if is_py3k: + if not isinstance(cls, type) or not issubclass(cls, BaseException): + cls = None + else: + if not isinstance(cls, (type, ClassType)): + cls = None + elif issubclass(cls, ClassType) and not instantiate_oldstyle_exceptions: + cls = None + elif not issubclass(cls, BaseException): + cls = None if cls is None: fullname = "%s.%s" % (modname, clsname) diff --git a/rpyc/utils/server.py b/rpyc/utils/server.py index fb64298f..93dab6ca 100644 --- a/rpyc/utils/server.py +++ b/rpyc/utils/server.py @@ -183,7 +183,8 @@ def _serve_client(self, sock, credentials): else: self.logger.info("welcome [%s]:%s", h, p) try: - config = dict(self.protocol_config, credentials = credentials) + config = dict(self.protocol_config, credentials = credentials, + endpoints = (sock.getsockname(), addrinfo)) conn = Connection(self.service, Channel(SocketStream(sock)), config = config, _lazy = True) conn._init_service()