def __enter__(self): self.dispatcher = CallbackDispatcher(self.messagebus, self.return_result) self.dispatcher.register([CONNECTIONCHECK_RUN_CHECK], [MOCK_CONNECTION_CHECK_ENDPOINT], [self.entity]) return self
def register_dispatchers(self, messagebus): if self._enabled: self._messagebus = messagebus self._start_blocking_dispatcher = SequentialDispatcher( messagebus, self.start_blocking) self._start_blocking_dispatcher.register( [START_BLOCKING_ON_MESSAGE], [BLOCKER_ENDPOINT]) self._after_command_dispatcher = CallbackDispatcher( messagebus, self.after_command) self._after_command_dispatcher.register([AFTER_COMMAND], [APPLICATION_ENDPOINT]) if self._init_enabled: print('INITING BLOCKER') self._start_blocking(StartBlockingInfo( BEFORE_COMMAND, APPLICATION_ENDPOINT, entity=None, timeout=self._init_timeout), id='init', priority=100) if self._exit_enabled: print('EXITING BLOCKER') self._start_blocking(StartBlockingInfo( AFTER_COMMAND, APPLICATION_ENDPOINT, entity=None, timeout=self._exit_timeout), id='exit', priority=100)
class MockConnectionCheck(object): def __init__(self, messagebus, success, required, entity): self.messagebus = messagebus self.success = success self.required = required self.entity = entity self.dispatcher = None self.executed = False def __enter__(self): self.dispatcher = CallbackDispatcher(self.messagebus, self.return_result) self.dispatcher.register([CONNECTIONCHECK_RUN_CHECK], [MOCK_CONNECTION_CHECK_ENDPOINT], [self.entity]) return self def __exit__(self, exc_type, exc_val, exc_tb): if self.dispatcher: self.dispatcher.destroy() def return_result(self, message): self.executed = True return ConnectionCheckResult('mockcc', self.success, self.required, 'message')
def register_dispatchers(self, messagebus): self.messagebus = messagebus self._generate_metrics_report_dispatcher = CallbackDispatcher( messagebus, self._handle_generate_metrics_report_wrapper) self._generate_metrics_report_dispatcher.register( [GENERATE_METRICS_REPORT])
class PrintRunQueueAfterEachTest(AbstractExtension): def register_dispatchers(self, messagebus): self._messagebus = messagebus self._dispatcher = CallbackDispatcher( messagebus, self.log_run_queue_on_test_case_finished) self._dispatcher.register([TEST_CASE_FINISHED], [RUNNER_ENDPOINT]) def destroy(self): self._dispatcher.destroy() def log_run_queue_on_test_case_finished(self, message): fs = self._messagebus.send_request(GET_CURRENT_RUN_QUEUE) print('Run queue: {run_queue}'.format(run_queue=fs[0].result()))
class Extension(AbstractExtension): def register_dispatchers(self, messagebus): self._dispatcher = CallbackDispatcher( messagebus, self.my_manual_dispatcher) self._dispatcher.register(None) def my_manual_dispatcher(self): pass @concurrent_dispatcher(None) def my_decorated_dispatcher(self): pass def destroy(self): self._dispatcher.destroy()
def create_harness(test_cases): harness = ExtensionTestHarness(TestScheduler, endpoints_and_messages={ FINDER_ENDPOINT: [FIND_TEST_CASES], RUNNER_ENDPOINT: [ABORT], K2_APPLICATION_ENDPOINT: [ABORT, CRITICAL_ABORT], }) def find_test_cases(message): return test_cases finder_dispatcher = CallbackDispatcher(harness.messagebus, find_test_cases) finder_dispatcher.register([FIND_TEST_CASES], [FINDER_ENDPOINT]) return harness
def test_triggers_sut_recovery_perform_on_failed_health_check(self): with TestHealthMonitor.create_harness() as harness: def handler(message): raise HealthCheckError('Nope!') dispatcher = CallbackDispatcher(harness.messagebus, handler) harness.messagebus.register_dispatcher(dispatcher, [PERFORM_HEALTH_CHECK], entities=['mysut']) with LocalMessageQueue(harness.messagebus, message_ids=[SUT_RECOVERY_PERFORM], endpoint_ids=[MOCK_ENDPOINT], entities=['mysut' ]) as sut_recovery_perform_queue: data = Mock() data.verdict = Verdict.FAILED harness.trigger_event(TEST_CASE_FINISHED, MOCK_ENDPOINT, data=data, entity='mysut') harness.messagebus.wait_for_not_active() assert not sut_recovery_perform_queue.empty()
def test_that_run_queue_is_empty_is_triggered(self): with create_harness([]) as harness: global run_queue_empty_called run_queue_empty_called = False def handle_run_queue_empty(message): global run_queue_empty_called run_queue_empty_called = True run_queue_empty_dispatcher = CallbackDispatcher( harness.messagebus, handle_run_queue_empty) run_queue_empty_dispatcher.register([RUN_QUEUE_EMPTY], [SCHEDULER_ENDPOINT]) harness.send_request(SCHEDULE_NEXT_TEST, SCHEDULER_ENDPOINT).wait()[0].result() assert run_queue_empty_called
def test_that_its_possible_to_add_tests_when_dispatching_scheduling_next_test_in_callback( self): with create_harness([]) as harness: def handle_scheduling_next_test(message): harness.send_request(ADD_TEST_CASES, SCHEDULER_ENDPOINT, data=[test1]).wait()[0].result() run_queue_empty_dispatcher = CallbackDispatcher( harness.messagebus, handle_scheduling_next_test) run_queue_empty_dispatcher.register([SCHEDULING_NEXT_TEST], [SCHEDULER_ENDPOINT]) actual_test = harness.send_request( SCHEDULE_NEXT_TEST, SCHEDULER_ENDPOINT).wait()[0].result() self.assertEqual(actual_test, test1)
def test_that_scheduling_next_test_is_triggered(self): with create_harness([]) as harness: global scheduling_next_test_count scheduling_next_test_count = 0 def handle_scheduling_next_test(message): global scheduling_next_test_count scheduling_next_test_count += 1 run_queue_empty_dispatcher = CallbackDispatcher( harness.messagebus, handle_scheduling_next_test) run_queue_empty_dispatcher.register([SCHEDULING_NEXT_TEST], [SCHEDULER_ENDPOINT]) harness.send_request(SCHEDULE_NEXT_TEST, SCHEDULER_ENDPOINT).wait()[0].result() assert run_queue_empty_called
def test_that_run_queue_initialized_is_triggered(self): with create_harness([test1, test2]) as harness: global run_queue run_queue = None def handle_run_queue_initialized(message): global run_queue run_queue = message.data run_queue_empty_dispatcher = CallbackDispatcher( harness.messagebus, handle_run_queue_initialized) run_queue_empty_dispatcher.register([RUN_QUEUE_INITIALIZED], [SCHEDULER_ENDPOINT]) harness.send_request(SCHEDULE_NEXT_TEST, SCHEDULER_ENDPOINT).wait()[0].result() self.assertEqual(run_queue, [test1, test2])
def test_that_run_queue_modified_is_triggered_from_remove_test_cases(self): with create_harness([test1, test2]) as harness: global run_queue run_queue = None def handle_run_queue_modified(message): global run_queue run_queue = message.data run_queue_empty_dispatcher = CallbackDispatcher( harness.messagebus, handle_run_queue_modified) run_queue_empty_dispatcher.register([RUN_QUEUE_MODIFIED], [SCHEDULER_ENDPOINT]) harness.send_request(REMOVE_TEST_CASES, SCHEDULER_ENDPOINT, data=[test1]).wait()[0].result() self.assertEqual(run_queue, [test2])
class AbstractMetricsReportExtension(AbstractExtension, metaclass=abc.ABCMeta): def __init__(self, config, instances): self._generate_metrics_report_dispatcher = None def register_dispatchers(self, messagebus): self.messagebus = messagebus self._generate_metrics_report_dispatcher = CallbackDispatcher( messagebus, self._handle_generate_metrics_report_wrapper) self._generate_metrics_report_dispatcher.register( [GENERATE_METRICS_REPORT]) def _handle_generate_metrics_report_wrapper(self, message): try: self.handle_generate_metrics_report(message) except Exception: msg = 'Could not generate metrics report: {reporter}'.format( reporter=str(self)) logger.debug(msg, exc_info=True) logger.warning(msg) @abc.abstractmethod def handle_generate_metrics_report(self, message): pass def destroy(self): if self._generate_metrics_report_dispatcher is not None: self._generate_metrics_report_dispatcher.destroy() self._generate_metrics_report_dispatcher = None def parse_regex_option(self, config, option): regex_string = config.get(option) if regex_string is None: return None else: try: return re.compile(regex_string) except re.error: raise ConfigException( "Error parsing regex '{regex}' for config option '{option}'" .format(regex=config.get(option), option=option.key))
def test_additional_options_from_other_extensions_are_included_in_run_command( self): with _create_harness(env=self.env_mock) as harness, \ patch('tempfile.TemporaryDirectory', return_value=self.tempdir), \ patch('os.makedirs'), \ patch('shutil.copy'): CallbackDispatcher(harness.messagebus, lambda m: 'additional options').register( [GET_ASCIIDOCTOR_OPTIONS], [OPTIONS_ENDPOINT]) futures = harness.messagebus.send_request( GENERATE_DOC, ASCIIDOCTOR_ENDPOINT, data=AsciidoctorCommand('output_path')) futures.wait(timeout=1) self.assertIn('additional options', self.env_mock.run.call_args[0][0])
def register_dispatchers(self, messagebus): self._messagebus = messagebus if self._loopduration or self._looprepeats: self._run_queue_initialized_dispatcher = CallbackDispatcher( messagebus, self.handle_initialized) self._run_queue_initialized_dispatcher.register( [RUN_QUEUE_INITIALIZED]) if self._loopduration: self._test_run_started_dispatcher = CallbackDispatcher( messagebus, self.handle_started) self._test_run_started_dispatcher.register([TEST_RUN_STARTED]) self._run_queue_empty_dispatcher = CallbackDispatcher( messagebus, self.handle_empty) self._run_queue_empty_dispatcher.register([RUN_QUEUE_EMPTY])
def _start_blocking(self, blocking_info, id=None): logger.debug('Starting to block on {data}'.format(data=blocking_info)) def blocking_callback(id, timeout, message): """Block until STOP_BLOCKING_ON_MESSAGE is received in the blocker LocalMessageQueue.""" logger.debug( 'Blocker received message {name}, waiting for stop blocking for {timeout} seconds' .format(name=message.message_id.name, timeout=timeout)) self._messagebus.trigger_event(BLOCKING_STARTED, BLOCKER_ENDPOINT, entity=id) blocker_state = self._ongoing_blockers[id] blocker_state.set_started() try: blocker_state.blocker.get(timeout=timeout) logger.debug( 'Blocker no longer blocks on message {name}'.format( name=message.message_id.name)) self._messagebus.trigger_event(BLOCKING_COMPLETED, BLOCKER_ENDPOINT, entity=id) except Exception: logger.warning( 'Blocker timed out when blocking on message {name}'.format( name=message.message_id.name)) self._messagebus.trigger_event(BLOCKING_TIMED_OUT, BLOCKER_ENDPOINT, entity=id) finally: blocker_state.cleanup() del self._ongoing_blockers[id] # Creates an ID for the specific instance of blocking. Is used in all messages communication. if id is None: id = str(uuid4()) # Creates a blocking message queue. # This can be used to block until a STOP_BLOCKING_ON_MESSAGE is received with entity=id. blocker = LocalMessageQueue(self._messagebus, [STOP_BLOCKING_ON_MESSAGE], entities=[id]) # Starts the blocker message queue blocker.__enter__() # Creates the blocking dispatcher that listens to the message described in the received StartBlockingInfo blocking_dispatcher = CallbackDispatcher( self._messagebus, functools.partial(blocking_callback, id, blocking_info.timeout)) blocking_dispatcher.register( message_ids=[blocking_info.message_id], endpoint_ids=[blocking_info.endpoint_id] if blocking_info.endpoint_id else None, entities=[blocking_info.entity] if blocking_info.entity else None) self._ongoing_blockers[id] = BlockerState(id, blocker, blocking_dispatcher) # Return the id so that the user can use it when sending STOP_BLOCKING_ON_MESSAGE # or to listen for BLOCKING_STARTED, BLOCKING_COMPLETED and/or BLOCKING_TIMED_OUT return id
def register_dispatchers(self, messagebus): self._messagebus = messagebus self._dispatcher = CallbackDispatcher( messagebus, self.log_run_queue_on_test_case_finished) self._dispatcher.register([TEST_CASE_FINISHED], [RUNNER_ENDPOINT])
def register_dispatchers(self, messagebus): self._messagebus = messagebus self._run_dispatcher = CallbackDispatcher(messagebus, self._run) self._run_dispatcher.register(message_ids=[TEST_SUBRUN], endpoint_ids=[MULTI_RUNNER_ENDPOINT])
class GtestBinaryRunner(AbstractExtension): def __init__(self, config, instances): self._entity = instances[GTEST_binaryid] self._binary = config.get(GTEST_BINARY_PATH) self._timeout = config.get(GTEST_TIMEOUT) self._xml_report_path = config.get(GTEST_XML_REPORT_PATH) self._filter = config.get(GTEST_FILTER) self._messagebus = None self._run_dispatcher = None self._serial = config.get(GTEST_USE_SERIAL) self._endmark = config.get(GTEST_SERIAL_ENDMARK) self._create_run() def _create_run(self): if self._serial: can = ['serial'] params = {'prefix_output': False, 'endmark': self._endmark} else: can = ['telnet'] params = {} @requires(exec='Exec', can=can, scope='session') @requires(messagebus='MessageBus') def _run(message, exec, messagebus): output = self._run_gtest_binary_on_sut(exec, self._binary, self._filter, params) self._log_output(output) if self._xml_report_path is not None: self._write_gtest_xml_report( self._collect_gtest_xml_report(exec).strip()) return None else: return self._parse_gtest_standard_output(messagebus, output) self._run = _run def register_dispatchers(self, messagebus): self._messagebus = messagebus self._run_dispatcher = CallbackDispatcher(messagebus, self._run) self._run_dispatcher.register(message_ids=[TEST_SUBRUN], endpoint_ids=[MULTI_RUNNER_ENDPOINT]) def destroy(self): if self._run_dispatcher is not None: self._run_dispatcher.destroy() self._run_dispatcher = None def _write_gtest_xml_report(self, report): os.makedirs(os.path.dirname(self._xml_report_path), exist_ok=True) with open(self._xml_report_path, 'w') as f: f.write(report) def _collect_gtest_xml_report(self, exec): try: return exec.send_line('cat /dev/shm/report.xml') finally: exec.send_line('rm /dev/shm/report.xml') def _parse_gtest_standard_output(self, messagebus, output): run_verdict = Verdict.PASSED for name, error_message, verdict in self._parse_test_cases(output): id = uuid.uuid1() messagebus.trigger_event(TEST_CASE_STARTED, MULTI_RUNNER_ENDPOINT, data=TestCaseStarted( id, name, name, datetime.now())) messagebus.trigger_event(TEST_CASE_FINISHED, MULTI_RUNNER_ENDPOINT, data=TestCaseFinished( id, name, datetime.now(), verdict, stacktrace=error_message)) run_verdict = run_verdict.combine(verdict) return run_verdict def _parse_test_cases(self, output): test_regex = re.compile( r""" ^\[\sRUN\s+\]\s(?P<name>\S+)\s*$\s # start of test and name (?P<message>.*?)? # optional multiline message \[\s+(?P<verdict>OK|FAILED)\s+\] # verdict """, re.VERBOSE | re.MULTILINE | re.DOTALL) for match in test_regex.finditer(output): yield match.group('name'), match.group( 'message'), self._map_verdict(match.group('verdict')) def _log_output(self, output): logger.info('---- gtest output begins ----') for line in output.split('\n'): logger.info(line) logger.info('---- gtest output ends ----') def _run_gtest_binary_on_sut(self, exec, binary, filter, params): xml_report_flag = '' if self._xml_report_path is not None: xml_report_flag = '--gtest_output=xml:/dev/shm/report.xml' filter_flag = '' if not filter else '--gtest_filter={filter}'.format( filter=filter) command = '{binary} --gtest_color=no --gtest_print_time=0 {filter} {xml_report_flag}'.format( binary=binary, filter=filter_flag, xml_report_flag=xml_report_flag, ) logger.info('Executing gtest: {command}'.format(command=command)) return exec.send_line(command, timeout=self._timeout, **params) def _map_verdict(self, gtest_verdict): if gtest_verdict == 'OK': return Verdict.PASSED elif gtest_verdict == 'FAILED': return Verdict.FAILED return Verdict.ERROR
def register_dispatchers(self, messagebus): if self._enabled: self.dispatcher = CallbackDispatcher(messagebus, self.run_check) self.dispatcher.register([CONNECTIONCHECK_RUN_CHECK], [SERIAL_CONNECTION_CHECK_ENDPOINT], entities=self._suts)
class SerialConnectionCheck(AbstractExtension): """Serial connection check.""" def __init__(self, config, instances): self._enabled = config.get(SERIAL_ENABLED) and config.get( SERIAL_CONNECTION_CHECK_ENABLED) self._required = config.get(SERIAL_CONNECTION_CHECK_REQUIRED) self._entity = instances.get(SERIAL_PORT_IDS) self._timeout = config.get(SERIAL_TIMEOUT) self._suts = [ sut for sut in config.get(SUT) if self._entity in config.get(SUT_SERIAL_PORTS, entity=sut) ] if self._enabled and not self._suts: msg = "Error: connection check enabled for '{port}' but no suts who listens".format( port=self._entity) raise MissingConditionalConfigOption(msg) def register_dispatchers(self, messagebus): if self._enabled: self.dispatcher = CallbackDispatcher(messagebus, self.run_check) self.dispatcher.register([CONNECTIONCHECK_RUN_CHECK], [SERIAL_CONNECTION_CHECK_ENDPOINT], entities=self._suts) @requires(messagebus='MessageBus') def run_check(self, message, messagebus): logger.info( 'Running serial connection check for entity {entity}'.format( entity=self._entity)) client = SerialClient(messagebus, self._entity, timeout=self._timeout) result = self._perform_check(client) if result.success: return result try: logger.debug( 'Trigger reconnect of serial connection for entity {entity}.'. format(entity=self._entity)) messagebus.send_request(SERIAL_RECONNECT, SERIAL_ENDPOINT, entity=self._entity).wait( self._timeout)[0].result() except Exception as e: logger.debug( 'Reconnect of serial connection failed for entity {entity}.'. format(entity=self._entity), exc_info=True) return ConnectionCheckResult( self.name, success=False, required=self._required, message= 'Serial connection check failed to reconnect serial connection: {error}' .format(error=str(e))) return self._perform_check(client) def _perform_check(self, client): try: client.send_line('echo test') logger.info( 'Serial connection check for port {entity} was successful'. format(entity=self._entity)) return ConnectionCheckResult(self.name, success=True, required=self._required, message='') except Exception as e: self._check_if_member_of_dialout_group() msg = 'Serial connection check failed for {entity}: {error}'.format( entity=self._entity, error=str(e)) logger.debug(msg, exc_info=True) return ConnectionCheckResult(self.name, success=False, required=self._required, message=msg) def _check_if_member_of_dialout_group(self): try: dialout_gid = grp.getgrnam('dialout').gr_gid except KeyError: # The dialout group does not exist. # Do nothing, as we are most likely not running on a Debian style system. return if dialout_gid not in os.getgroups(): msg = ( 'The user running this K2 process is not a member of the "dialout" group. ' 'This will most likely lead to the serial port being inaccessible. ' 'Make sure to add the current user to the "dialout" group to remedy this issue.' ) logger.warning(msg)
class Looper(AbstractExtension): """Repeats test case executions to meet duration or repetition configuration.""" def __init__(self, config, instances): self._loopduration = parse_time(config.get(LOOP_DURATION)) self._looprepeats = config.get(LOOP_REPEATS) if self._loopduration and self._looprepeats: raise MutuallyExclusiveConfigOptions( 'schedule.duration and schedule.repeats cannot be combined') self._messagebus = None self._run_queue_empty_dispatcher = None self._test_run_started_dispatcher = None self._run_queue_initialized_dispatcher = None self._original_run_queue = None self._cycle_run_queue = None self._start_time = None def register_dispatchers(self, messagebus): self._messagebus = messagebus if self._loopduration or self._looprepeats: self._run_queue_initialized_dispatcher = CallbackDispatcher( messagebus, self.handle_initialized) self._run_queue_initialized_dispatcher.register( [RUN_QUEUE_INITIALIZED]) if self._loopduration: self._test_run_started_dispatcher = CallbackDispatcher( messagebus, self.handle_started) self._test_run_started_dispatcher.register([TEST_RUN_STARTED]) self._run_queue_empty_dispatcher = CallbackDispatcher( messagebus, self.handle_empty) self._run_queue_empty_dispatcher.register([RUN_QUEUE_EMPTY]) def destroy(self): if self._run_queue_empty_dispatcher: self._run_queue_empty_dispatcher.destroy() if self._run_queue_initialized_dispatcher: self._run_queue_initialized_dispatcher.destroy() if self._test_run_started_dispatcher: self._test_run_started_dispatcher.destroy() def handle_initialized(self, message): self._original_run_queue = message.data self._cycle_run_queue = itertools.cycle(message.data) if self._looprepeats: self.schedule_repeats() def handle_started(self, message): self._start_time = time.time() def schedule_repeats(self): if self._looprepeats > 1: self._messagebus.send_request(ADD_TEST_CASES, SCHEDULER_ENDPOINT, data=self._original_run_queue * (self._looprepeats - 1)).wait() def handle_empty(self, message): if time.time() - self._start_time < self._loopduration: self._messagebus.send_request(ADD_TEST_CASES, SCHEDULER_ENDPOINT, data=[next(self._cycle_run_queue) ]).wait()
def register_dispatchers(self, messagebus): self._dispatcher = CallbackDispatcher( messagebus, self.my_manual_dispatcher) self._dispatcher.register(None)
class Blocker(AbstractExtension): """Blocker implementation.""" def __init__(self, config, instances): self._enabled = config.get(BLOCKER_ENABLED) self._init_enabled = config.get(BLOCKER_INIT_ENABLED) self._init_timeout = config.get(BLOCKER_INIT_TIMEOUT) self._ongoing_blockers = {} self._start_blocking_dispatcher = None self._after_command_dispatcher = None def register_dispatchers(self, messagebus): if self._enabled: self._messagebus = messagebus self._start_blocking_dispatcher = SequentialDispatcher( messagebus, self.start_blocking) self._start_blocking_dispatcher.register( [START_BLOCKING_ON_MESSAGE], [BLOCKER_ENDPOINT]) self._after_command_dispatcher = CallbackDispatcher( messagebus, self.after_command) self._after_command_dispatcher.register([AFTER_COMMAND], [APPLICATION_ENDPOINT]) if self._init_enabled: print('INITING BLOCKER') self._start_blocking(StartBlockingInfo( BEFORE_COMMAND, APPLICATION_ENDPOINT, entity=None, timeout=self._init_timeout), id='init') def start_blocking(self, message): return self._start_blocking(blocking_info=message.data) def _start_blocking(self, blocking_info, id=None): logger.debug('Starting to block on {data}'.format(data=blocking_info)) def blocking_callback(id, timeout, message): """Block until STOP_BLOCKING_ON_MESSAGE is received in the blocker LocalMessageQueue.""" logger.debug( 'Blocker received message {name}, waiting for stop blocking for {timeout} seconds' .format(name=message.message_id.name, timeout=timeout)) self._messagebus.trigger_event(BLOCKING_STARTED, BLOCKER_ENDPOINT, entity=id) blocker_state = self._ongoing_blockers[id] blocker_state.set_started() try: blocker_state.blocker.get(timeout=timeout) logger.debug( 'Blocker no longer blocks on message {name}'.format( name=message.message_id.name)) self._messagebus.trigger_event(BLOCKING_COMPLETED, BLOCKER_ENDPOINT, entity=id) except Exception: logger.warning( 'Blocker timed out when blocking on message {name}'.format( name=message.message_id.name)) self._messagebus.trigger_event(BLOCKING_TIMED_OUT, BLOCKER_ENDPOINT, entity=id) finally: blocker_state.cleanup() del self._ongoing_blockers[id] # Creates an ID for the specific instance of blocking. Is used in all messages communication. if id is None: id = str(uuid4()) # Creates a blocking message queue. # This can be used to block until a STOP_BLOCKING_ON_MESSAGE is received with entity=id. blocker = LocalMessageQueue(self._messagebus, [STOP_BLOCKING_ON_MESSAGE], entities=[id]) # Starts the blocker message queue blocker.__enter__() # Creates the blocking dispatcher that listens to the message described in the received StartBlockingInfo blocking_dispatcher = CallbackDispatcher( self._messagebus, functools.partial(blocking_callback, id, blocking_info.timeout)) blocking_dispatcher.register( message_ids=[blocking_info.message_id], endpoint_ids=[blocking_info.endpoint_id] if blocking_info.endpoint_id else None, entities=[blocking_info.entity] if blocking_info.entity else None) self._ongoing_blockers[id] = BlockerState(id, blocker, blocking_dispatcher) # Return the id so that the user can use it when sending STOP_BLOCKING_ON_MESSAGE # or to listen for BLOCKING_STARTED, BLOCKING_COMPLETED and/or BLOCKING_TIMED_OUT return id def after_command(self, message): self.destroy() def destroy(self): try: if self._start_blocking_dispatcher: self._start_blocking_dispatcher.destroy() if self._after_command_dispatcher: self._after_command_dispatcher.destroy() for id, blocker_state in self._ongoing_blockers.items(): if blocker_state.started: # Just puts something in the blockers queue to make it stop # If a blocker is running at destroy time we don't want to raise exceptions logger.debug( 'Deregister blocker dispatcher by adding stop message') blocker_state.blocker.handle_message('stop') else: blocker_state.cleanup() del self._ongoing_blockers[id] finally: self._start_blocking_dispatcher = None self._after_command_dispatcher = None self._ongoing_blockers = {}