async def _handle_client_invoke(ws, cmd, impl): id_ = cmd['id'] path = cmd['path'] args = cmd['args'] func = impl[path] result = { 'type': 'invoke_result', 'id': id_, } try: val = await _safe_invoke(func, *args) result['val'] = val except SerializeIface as s: sub_obj = s.obj sub_whitelist = s.whitelist_method_names sub_name = path + '.' + str(uuid.uuid4()) sub_iface, sub_impl = serialize_interface( sub_obj, name=sub_name, whitelist_method_names=sub_whitelist) impl.update(sub_impl) result['iface'] = sub_iface except Exception as e: result['exception'] = str( e ) # TODO: Serialize the exception more fully so it can be stacktraced on the other side. result_buf = pack(result) await ws.send(result_buf)
async def unsubscribe_func(): subscriptions[channel].remove(callback) if len(subscriptions[channel]) == 0: del subscriptions[channel] await ws.send( pack({ 'type': 'unsubscribe', 'channel': channel, }))
async def handle_client(ws, path): root = root_factory() whitelist_method_names = () if isinstance(root, tuple): root, whitelist_method_names = root iface, impl = serialize_interface( root, name='root', whitelist_method_names=whitelist_method_names) iface_buf = pack(iface) await ws.send(iface_buf) await ws.send(channels_buf) return await _handle_client(root, ws, impl, pubsub, subscribers)
async def publish_func(channel, payload, wait=False): client_list = subscribers.get(channel, []) if client_list: message = { 'type': 'publish', 'channel': channel, 'payload': payload, } message_buf = pack(message) tasks = [] for client in client_list: task = asyncio.create_task(client.send(message_buf)) tasks.append(task) if wait: await asyncio.wait(tasks)
def _build_client_handler(root_factory, pubsub, subscribers): channels = pubsub['channels'] if pubsub is not None else [] channels_buf = pack(channels) async def handle_client(ws, path): root = root_factory() whitelist_method_names = () if isinstance(root, tuple): root, whitelist_method_names = root iface, impl = serialize_interface( root, name='root', whitelist_method_names=whitelist_method_names) iface_buf = pack(iface) await ws.send(iface_buf) await ws.send(channels_buf) return await _handle_client(root, ws, impl, pubsub, subscribers) return handle_client
async def subscribe_func(channel, callback): if channel not in subscriptions: subscriptions[channel] = {callback} await ws.send(pack({ 'type': 'subscribe', 'channel': channel, })) else: subscriptions[channel].add(callback) async def unsubscribe_func(): subscriptions[channel].remove(callback) if len(subscriptions[channel]) == 0: del subscriptions[channel] await ws.send( pack({ 'type': 'unsubscribe', 'channel': channel, })) return unsubscribe_func
async def impl_transport(path, args): nonlocal id_ id_here = id_ id_ += 1 event = asyncio.Event() invoke_events[id_here] = { 'event': event, } cmd = { 'type': 'invoke', 'id': id_here, 'path': path, 'args': args, } cmd = pack(cmd) await ws.send(cmd) await event.wait() message = invoke_events[id_here]['result_message'] del invoke_events[id_here] if 'val' in message: return message['val'] # Standard return value elif 'exception' in message: error_text = message['exception'] raise Exception( error_text) # TODO: Rebuild the exception more precisely. elif 'iface' in message: sub_iface = message['iface'] _, sub_proxy_iface = build_interface(sub_iface, impl_transport) return sub_proxy_iface else: raise Exception('Unknown response message...') return val