# Copyright (C) 2012- Takafumi Arakaki # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . from .py3compat import Queue from .utils import ThreadedIterator, newthread from .core import EPCCore from .handler import ThreadingEPCHandler class EPCClientHandler(ThreadingEPCHandler): # In BaseRequestHandler, everything happen in `.__init__()`. # Let's defer it to `.start()`. def __init__(self, *args): self._args = args self._ready = Queue.Queue() def start(self): ThreadingEPCHandler.__init__(self, *self._args) def setup(self): ThreadingEPCHandler.setup(self) self._ready.put(True) def wait_until_ready(self): self._ready.get() def _recv(self): self._recv_iter = ThreadedIterator(ThreadingEPCHandler._recv(self)) return self._recv_iter class EPCClient(EPCCore): """ EPC client class to call remote functions and serve Python functions. >>> client = EPCClient() >>> client.connect(('localhost', 9999)) #doctest: +SKIP >>> client.call_sync('echo', [111, 222, 333]) #doctest: +SKIP [111, 222, 333] To serve Python functions, you can use :meth:`register_function`. >>> client.register_function(str.upper) :meth:`register_function` can be used as a decorator. >>> @client.register_function ... def add(x, y): ... return x + y Also, you can initialize client and connect to the server by one line. >>> client = EPCClient(('localhost', 9999)) #doctest: +SKIP .. method:: call Alias of :meth:`epc.server.EPCHandler.call`. .. method:: call_sync Alias of :meth:`epc.server.EPCHandler.call_sync`. .. method:: methods Alias of :meth:`epc.server.EPCHandler.methods`. .. method:: methods_sync Alias of :meth:`epc.server.EPCHandler.methods_sync`. """ thread_daemon = True def __init__(self, socket_or_address=None, debugger=None, log_traceback=False): if socket_or_address is not None: self.connect(socket_or_address) EPCCore.__init__(self, debugger, log_traceback) def connect(self, socket_or_address): """ Connect to server and start serving registered functions. :type socket_or_address: tuple or socket object :arg socket_or_address: A ``(host, port)`` pair to be passed to `socket.create_connection`, or a socket object. """ if isinstance(socket_or_address, tuple): import socket self.socket = socket.create_connection(socket_or_address) else: self.socket = socket_or_address # This is what BaseServer.finish_request does: address = None # it is not used, so leave it empty self.handler = EPCClientHandler(self.socket, address, self) self.call = self.handler.call self.call_sync = self.handler.call_sync self.methods = self.handler.methods self.methods_sync = self.handler.methods_sync self.handler_thread = newthread(self, target=self.handler.start) self.handler_thread.daemon = self.thread_daemon self.handler_thread.start() self.handler.wait_until_ready() def close(self): """Close connection.""" try: self.handler._recv_iter.stop() except AttributeError: # Do not fail to close even if the client is never used. pass def _ignore(*_): """"Do nothing method for `EPCHandler`.""" add_client = _ignore remove_client = _ignore