def __do_request(self, request_type, _uri=None, callback=None, subscription=None, **kwargs): """ Create and forward a generic WAMP request to the decoupler :type request_type: WampRequestType :type _uri: str | None :type callback: (*Any) -> None | None :type subscription: Subscription | None :return: Result from WampRequest, None if request failed. :rtype: dict | None """ if not self._client_thread.is_alive(): return # Make sure the current thread has the event loop set asyncio.set_event_loop(self._loop) async def _async_request(future): """ :type future: asyncio.Future """ request = WampRequest(request_type, _uri, kwargs, callback, subscription, future) await self._decoupler.put_request(request) await future # The client worker is responsible for completing the future forwarded_future = asyncio.Future() concurrent_future = asyncio.run_coroutine_threadsafe(_async_request(forwarded_future), self._loop) self._decoupler.set_caller_future(concurrent_future) concurrent_future.result() self._decoupler.set_caller_future(None) # If the decoupled client worker never set the future, it failed and/or died and we return None if forwarded_future.done(): return forwarded_future.result()
def __init__(self, url=None, allow_exception=False): """ :param url: URL of the Wwise Authoring API WAMP server, defaults to ws://127.0.0.1:8080/waapi :type: str :param allow_exception: Allow errors on call and subscribe to throw an exception. Default is False. :type allow_exception: bool :raises: CannotConnectToWaapiException """ super(WaapiClient, self).__init__() self._allow_exception = allow_exception self._url = url or "ws://127.0.0.1:8080/waapi" self._client_thread = None """:type: Thread""" self._loop = asyncio.get_event_loop() if not self._loop.is_running(): if not self._loop.is_closed(): self._loop.close() if platform == 'win32': # Prefer the ProactorEventLoop event loop on Windows self._loop = asyncio.ProactorEventLoop() else: self._loop = asyncio.new_event_loop() asyncio.set_event_loop(self._loop) self._decoupler = None """:type: AutobahnClientDecoupler""" self._subscriptions = set() """:type: set[EventHandler]""" # Connect on instantiation (RAII idiom) if not self.__connect(): raise CannotConnectToWaapiException("Could not connect to " + self._url)
def run(self): try: asyncio.set_event_loop(self._loop) txaio.use_asyncio() txaio.config.loop = self._loop # Info is too verbose, use error by default # TODO: Make logging level for autobahn configurable txaio.start_logging(level='error') # create a WAMP-over-WebSocket transport client factory transport_factory = WampWebSocketClientFactory( lambda: self._akcomponent_factory(self._decoupler, self. _callback_executor, self. _allow_exception), url=self._url) # Basic settings with most features disabled transport_factory.setProtocolOptions(failByDrop=False, openHandshakeTimeout=5., closeHandshakeTimeout=1.) isSecure, host, port, _, _, _ = parse_ws_url(self._url) transport, protocol = self._loop.run_until_complete( self._loop.create_connection(transport_factory, host, port, ssl=isSecure)) try: self._loop.run_forever() except KeyboardInterrupt: # wait until we send Goodbye if user hit ctrl-c # (done outside this except so SIGTERM gets the same handling) pass # give Goodbye message a chance to go through, if we still # have an active session if protocol._session: self._loop.run_until_complete(protocol._session.leave()) self._loop.close() except Exception as e: errorStr = pformat(e) stderr.write(errorStr + "\n") # Wake the caller, this thread will terminate right after so the # error can be detected by checking if the thread is alive self._decoupler.set_joined() self._decoupler.unblock_caller()
def disconnect(self): """ Gracefully disconnect from the Waapi server. :return: True if the call caused a successful disconnection, False otherwise. :rtype: bool """ if self.is_connected() and self.__do_request(WampRequestType.STOP): # Wait for the runner thread to gracefully exit and the asyncio loop to close if self._client_thread.is_alive(): self._client_thread.join() self._subscriptions.clear() # No need to unsubscribe, subscriptions will be dropped anyways # Create a new loop for upcoming uses if asyncio.get_event_loop().is_closed(): asyncio.set_event_loop(asyncio.new_event_loop()) return True # Only the caller that truly caused the disconnection return True return False
def disconnect(self): """ Gracefully disconnect from the Waapi server. :return: True if successfully disconnected, False otherwise. :rtype: bool """ if not self.is_connected(): return False self.__do_request(WampRequestType.STOP) self._subscriptions.clear( ) # No need to unsubscribe, subscriptions will be dropped anyways # Wait for the runner thread to gracefully exit and the asyncio loop to close self._client_thread.join() assert (asyncio.get_event_loop().is_closed()) # Create a new loop for upcoming uses asyncio.set_event_loop(asyncio.new_event_loop()) return True
def run(self): try: asyncio.set_event_loop(self._loop) # Start the loop ourselves to skip the sigterm signal handler since # it is not supported from a different thread coro = self._runner.run( lambda config: self._akcomponent_factory(config, self._decoupler, self._allow_exception), start_loop=False ) transport, protocol = self._loop.run_until_complete(coro) # Info is too verbose, use error by default # TODO: Make logging level for autobahn configurable txaio.start_logging(level='error') try: self._loop.run_forever() except KeyboardInterrupt: # wait until we send Goodbye if user hit ctrl-c # (done outside this except so SIGTERM gets the same handling) pass # give Goodbye message a chance to go through, if we still # have an active session if protocol._session: self._loop.run_until_complete(protocol._session.leave()) self._loop.close() except Exception as e: pprint(e) # Wake the caller, this thread will terminate right after so the # error can be detected by checking if the thread is alive self._decoupler.set_joined() self._decoupler.unblock_caller()