class BaseService(object): """ Implements basic functional for services: * all asio stuff * perform_sync method for synchronous operations You should reimplement _on_message function - this is callback for decoder, so this function is called with every incoming decoded message """ def __init__(self, name, endpoint="127.0.0.1", port=10053, init_args=sys.argv, **kwargs): """ It: * goes to Locator and get service api (put into self._service_api) * initializes event loop and all asio elements (write/read streams) * initializes session counter * registers callback on epoll READ event and\ binds callback to decoder (_on_message) """ if '--locator' in init_args: try: port = int(init_args[init_args.index('--locator') + 1]) except ValueError as err: port = 10053 except IndexError as err: port = 10053 self._try_reconnect = kwargs.get("reconnect_once", True) self._counter = 1 self.loop = ev.Loop() self._locator_host = endpoint self._locator_port = port self.servicename = name self._init_endpoint() # initialize pipe self.decoder = Decoder() self.decoder.bind(self._on_message) self.w_stream = WritableStream(self.loop, self.pipe) self.r_stream = ReadableStream(self.loop, self.pipe) self.r_stream.bind(self.decoder.decode) self.loop.register_read_event(self.r_stream._on_event, self.pipe.fileno()) def _init_endpoint(self): locator = Locator() self.service_endpoint, _, service_api = locator.resolve(self.servicename, self._locator_host, self._locator_port) self._service_api = service_api # msgpack convert in list or tuple depend on version - make it tuple self.pipe = Pipe(tuple(self.service_endpoint), self.reconnect if self._try_reconnect else None) self.loop.bind_on_fd(self.pipe.fileno()) def reconnect(self): self.loop.stop_listening(self.pipe.fileno()) #try: # self.pipe.sock.close() #except Exception as err: # print(str(err)) try: self._init_endpoint() except LocatorResolveError as err: pass else: self.w_stream.reconnect(self.pipe) self.r_stream.reconnect(self.pipe) def perform_sync(self, method, *args, **kwargs): """ Do not use the service synchronously after treatment to him asynchronously! Use for these purposes the other instance of the service! """ timeout = kwargs.get("timeout", 5) # Get number of current method try: number = (_num for _num, _name in self._service_api.iteritems() if _name == method).next() except StopIteration as err: raise ServiceError(self.servicename, "method %s is not available" % method, -100) try: # DO IT SYNC self.pipe.settimeout(timeout) self.pipe.writeall(packb([number, self._counter, args])) self._counter += 1 u = Unpacker() msg = None # If we receive rpc::error, put ServiceError here, # and raise this error instead of StopIteration on rpc::choke, # because after rpc::error we always receive choke. _error = None while True: response = self.pipe.recv(4096) u.feed(response) for _data in u: msg = Message.initialize(_data) if msg is None: continue if msg.id == message.RPC_CHUNK: yield unpackb(msg.data) elif msg.id == message.RPC_CHOKE: raise _error or StopIteration elif msg.id == message.RPC_ERROR: _error = ServiceError(self.servicename, msg.message, msg.code) finally: self.pipe.settimeout(0) # return to non-blocking mode def _on_message(self, args): raise NotImplementedError() @property def connected(self): return self.pipe.connected