def raise_event(event_name, device=None, shelf=None): """Raises an Event, running its sync and async Actions. This function runs async Actions as tasks, but sync ones serially, accumulating changes from each action. Supply either a device or shelf arg, but not both. Args: event_name: str, the name of the Event. device: a Device model. shelf: a Shelf model. Returns: The original model, optionally modified by sync actions. Raises: base_action.MissingModelError: if this method is called with neither device nor shelf. base_action.RedundantModelError: if this method is called with both device and shelf. """ event_actions = get_actions_for_event(event_name) actions_dict = action_loader.load_actions() model = device or shelf if not event_actions: logging.warn(_NO_ACTIONS_MSG, event_name) else: action_kwargs = {} if device: action_kwargs['device'] = device if shelf: action_kwargs['shelf'] = shelf if not action_kwargs: raise base_action.MissingModelError( 'No model passed to raise_event. You must supply either a device or ' 'shelf arg.') if len(action_kwargs) > 1: raise base_action.RedundantModelError( 'Redundant models passed to raise_event. You must supply either a ' 'device or a shelf, but not both.') for action in event_actions: if action in actions_dict[base_action.ActionType.SYNC]: try: model = actions_dict[base_action.ActionType.SYNC][action].run( **action_kwargs) except base_action.Error as error: logging.error( 'Skipping Action "%s" because it raised an exception: %s', action, str(error)) if action in actions_dict[base_action.ActionType.ASYNC]: action_kwargs['action_name'] = action taskqueue.add( queue_name='process-action', payload=pickle.dumps(action_kwargs), target='default') return model
def initialize(self, *args, **kwargs): # pylint: disable=arguments-differ """Overridden initializer that imports all available Actions.""" super(ProcessActionHandler, self).initialize(*args, **kwargs) self.actions = action_loader.load_actions() logging.info( 'ProcessActionHandler loaded %d actions: %s', len(self.actions), str(self.actions.keys()))
def test_import( self, mock_impimporter, mock_getmembers, mock_logwarning): """Tests importing modules and actions with success.""" mock_action_loader = mock.Mock() mock_impimporter.return_value = mock_action_loader mock_module_loader = mock.Mock() mock_action_loader.find_module = mock_module_loader mock_action_loader.iter_modules.return_value = [ ('module1', 'fake_module'), ('module2', 'fake_module'), ('module3', 'fake_module'), ('module4', 'fake_module'), ('module5', 'fake_module'), ('module6', 'fake_module'), ('module1_test', 'fake_module'), # Skipped test module. ('base_action', 'fake_module'), # Skipped base_action module. ('module7', 'fake_module') ] mock_getmembers.side_effect = [ (('WorkingActionClass1', WorkingActionClass1), ('NonClassObject', 'not-a-class')), (('WorkingActionClass2', WorkingActionClass2), ('NonClassObject', 'not-a-class')), (('NonWorkingActionClass3', NonWorkingActionClass3),), (('ActionClassWithoutActionName4', ActionClassWithoutActionName4),), (('ActionClassWithoutFriendlyName5', ActionClassWithoutFriendlyName5),), (('ActionClassWithoutRunMethod6', ActionClassWithoutRunMethod6),), (('DuplicateActionClass', DuplicateActionClass),), ] test_action_dict = action_loader.load_actions() test_device = device_model.Device() self.assertEqual( test_action_dict[base_action.ActionType.SYNC]['action1'].run( device=test_device), test_device) self.assertIsNone( test_action_dict[base_action.ActionType.ASYNC]['action2'].run()) mock_logwarning.assert_has_calls([ mock.call(action_loader._INSTANTIATION_ERROR_MSG % ( 'ActionClassWithoutActionName4', 'module4', base_action._NO_ACTION_NAME_MSG % ( 'ActionClassWithoutActionName4'))), mock.call(action_loader._INSTANTIATION_ERROR_MSG % ( 'ActionClassWithoutFriendlyName5', 'module5', base_action._NO_FRIENDLY_NAME_MSG % ( 'ActionClassWithoutFriendlyName5'))), mock.call(action_loader._INSTANTIATION_ERROR_MSG % ( 'ActionClassWithoutRunMethod6', 'module6', base_action._NO_RUN_METHOD_MSG % ( 'ActionClassWithoutRunMethod6'))), ])
def setUp(self): """Checks imported modules for an action module and includes class.""" super(ActionTestCase, self).setUp() try: self.testing_action except AttributeError: raise EnvironmentError( 'Create a TestCase setUp method that sets a variable named ' 'self.testing_action containing the name of the action module you ' 'wish to test, then runs the superclass setUp method.') actions = action_loader.load_actions([self.testing_action]) # pylint: disable=no-member if not actions: raise EnvironmentError( 'The unit test must import at least one valid action module. Verify ' 'that self.testing_action is a string that is the name of a module ' 'in the actions directory.') self.action = actions.get(self.testing_action)
def raise_event(event_name, device=None, shelf=None): """Raises an Event, running its sync and async Actions. This function runs sync Actions serially, accumulating changes from each action, and then kicks off async Actions via a single task that will spawn more tasks for additional actions. Supply either a device or shelf arg, but not both. Args: event_name: str, the name of the Event. device: a Device model. shelf: a Shelf model. Returns: The original model, optionally modified by sync actions. Raises: base_action.MissingModelError: if this method is called with neither device nor shelf. base_action.RedundantModelError: if this method is called with both device and shelf. EventActionsError: if the actions raised an error. """ event_actions = get_actions_for_event(event_name) event_actions.sort() event_async_actions = [] actions_dict = action_loader.load_actions() model = device or shelf if not event_actions: logging.info(_NO_ACTIONS_MSG, event_name) else: action_kwargs = {} if device: action_kwargs['device'] = device if shelf: action_kwargs['shelf'] = shelf if not action_kwargs: raise base_action.MissingModelError( 'No model passed to raise_event. You must supply either a device or ' 'shelf arg.') if len(action_kwargs) > 1: raise base_action.RedundantModelError( 'Redundant models passed to raise_event. You must supply either a ' 'device or a shelf, but not both.') errors = [] for action in event_actions: if action in actions_dict[base_action.ActionType.SYNC]: try: model = actions_dict[base_action.ActionType. SYNC][action].run(**action_kwargs) except base_action.Error as error: errors.append(error) logging.error( 'Skipping Action "%s" because it raised an exception: %s', action, str(error)) elif action in actions_dict[base_action.ActionType.ASYNC]: event_async_actions.append(action) else: logging.error( 'Skipping Action %r because it is not loaded in the application.', action) if errors: raise EventActionsError(errors) if event_async_actions: action_kwargs['async_actions'] = event_async_actions taskqueue.add(queue_name='process-action', payload=pickle.dumps(action_kwargs), target='default') return model