Skip to content

Commit

Permalink
Can pass Service as instance or class!
Browse files Browse the repository at this point in the history
This allows

- sharing the same service object among multiple clients on a server
  (as was the expectation by the user in #198)

- using an pre-initialized instance when connecting a client or hosting
  a oneshot server (partial resolution for #244)
  • Loading branch information
coldfix committed Dec 21, 2017
1 parent 9ceb3f9 commit 4328d4e
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 8 deletions.
19 changes: 15 additions & 4 deletions rpyc/core/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@
from rpyc.core.protocol import Connection


class hybridmethod(object):
"""Decorator for hybrid instance/class methods that will act like a normal
method if accessed via an instance, but act like classmethod if accessed
via the class."""
def __init__(self, func):
self.func = func
def __get__(self, obj, cls):
return self.func.__get__(cls if obj is None else obj, obj)
def __set__(self, obj, val):
raise AttributeError("Cannot overwrite method")


class Service(object):
"""The service base-class. Derive from this class to implement custom RPyC
services:
Expand Down Expand Up @@ -87,8 +99,11 @@ def get_service_name(cls):
exposed_get_service_aliases = get_service_aliases
exposed_get_service_name = get_service_name

@hybridmethod
def connect(self, channel, config={}):
"""Setup a connection via the given channel."""
if isinstance(self, type): # autovivify if accessed as class method
self = self()
conn = self._protocol(self, channel, config)
self.on_connect(conn)
return conn
Expand Down Expand Up @@ -164,7 +179,6 @@ class FakeSlaveService(VoidService):
"""VoidService that can be used for connecting to peers that operate a
:class:`MasterService`, :class:`ClassicService`, or the old
``SlaveService`` (pre v3.5) without exposing any functionality to them."""
__slots__ = ()
exposed_namespace = None
exposed_execute = None
exposed_eval = None
Expand All @@ -176,7 +190,6 @@ class MasterService(Service):
"""Peer for a new-style (>=v3.5) :class:`SlaveService`. Use this service
if you want to connect to a ``SlaveService`` without exposing any
functionality to them."""
__slots__ = ()

def on_connect(self, conn):
super(MasterService, self).on_connect(conn)
Expand All @@ -194,10 +207,8 @@ def on_connect(self, conn):
class ClassicService(MasterService, SlaveService):
"""Full duplex master/slave service, i.e. both parties have full control
over the other. Must be used by both parties."""
__slots__ = ()

class ClassicClient(MasterService, FakeSlaveService):
"""MasterService that can be used for connecting to peers that operate a
:class:`MasterService`, :class:`ClassicService`, or the old
``SlaveService`` (pre v3.5) without exposing any functionality to them."""
__slots__ = ()
4 changes: 2 additions & 2 deletions rpyc/utils/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def connect_channel(channel, service = VoidService, config = {}):
:returns: an RPyC connection
"""
return service().connect(channel, config)
return service.connect(channel, config)

def connect_stream(stream, service = VoidService, config = {}):
"""creates a connection over a given stream
Expand Down Expand Up @@ -182,7 +182,7 @@ def ssh_connect(remote_machine, remote_port, service = VoidService, config = {})
tun = remote_machine.tunnel(loc_port, remote_port)
stream = TunneledSocketStream.connect("localhost", loc_port)
stream.tun = tun
return service().connect(Channel(stream), config=config)
return service.connect(Channel(stream), config=config)

def discover(service_name, host = None, registrar = None, timeout = 2):
"""
Expand Down
4 changes: 2 additions & 2 deletions rpyc/utils/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ def _serve_client(self, sock, credentials):
try:
config = dict(self.protocol_config, credentials = credentials,
endpoints = (sock.getsockname(), addrinfo), logger = self.logger)
conn = self.service().connect(
conn = self.service.connect(
Channel(SocketStream(sock)),
config)
self._handle_connection(conn)
Expand Down Expand Up @@ -477,7 +477,7 @@ def _authenticate_and_build_connection(self, sock):
h, p = sock.getpeername()
config = dict(self.protocol_config, credentials=credentials, connid="%s:%d"%(h, p),
endpoints=(sock.getsockname(), (h, p)))
return self.service().connect(
return self.service.connect(
Channel(SocketStream(sock)), config)

def _accept_method(self, sock):
Expand Down

0 comments on commit 4328d4e

Please sign in to comment.