From bef2a9f7c75973cca4f83139509ecaf4d2a698d0 Mon Sep 17 00:00:00 2001 From: Simon Senkl Date: Fri, 15 Feb 2019 10:26:36 +0100 Subject: [PATCH 1/5] added abstract detection to executer `register_package` --- rasa_core_sdk/executor.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rasa_core_sdk/executor.py b/rasa_core_sdk/executor.py index 2e668f424..07b79b2c1 100644 --- a/rasa_core_sdk/executor.py +++ b/rasa_core_sdk/executor.py @@ -149,8 +149,11 @@ def register_package(self, package): actions = utils.all_subclasses(Action) for action in actions: + meta = action.__class__.__dict__.get('Meta', False) + abstract = getattr(meta, 'abstract', False) if (not action.__module__.startswith("rasa_core.") and - not action.__module__.startswith("rasa_core_sdk.")): + not action.__module__.startswith("rasa_core_sdk.") and + not abstract): self.register_action(action) @staticmethod From 8ec6169f8dc8a1ce20b2962b2382c84691fe3a94 Mon Sep 17 00:00:00 2001 From: Simon Senkl Date: Fri, 15 Feb 2019 11:19:13 +0100 Subject: [PATCH 2/5] adding debug logs --- rasa_core_sdk/executor.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rasa_core_sdk/executor.py b/rasa_core_sdk/executor.py index 07b79b2c1..ca804ce2b 100644 --- a/rasa_core_sdk/executor.py +++ b/rasa_core_sdk/executor.py @@ -102,6 +102,7 @@ def register_action(self, action): else: action = action() if isinstance(action, Action): + logger.debug("Registering {}".format(action.__class__.__name__)) self.register_function(action.name(), action.run) else: raise Exception("You can only register instances or subclasses of " @@ -149,8 +150,15 @@ def register_package(self, package): actions = utils.all_subclasses(Action) for action in actions: + logger.debug("Action: {}".format(action.__name__)) + logger.debug(action.__dict__) meta = action.__class__.__dict__.get('Meta', False) + if meta: + logger.debug("Meta: {}".format(meta.__dict__)) + else: + logger.debug("Meta: {}".format(meta)) abstract = getattr(meta, 'abstract', False) + logger.debug("Abstract: {}".format(abstract)) if (not action.__module__.startswith("rasa_core.") and not action.__module__.startswith("rasa_core_sdk.") and not abstract): From 80a969776944ed5d44fe5111f180593c4df431ea Mon Sep 17 00:00:00 2001 From: Simon Senkl Date: Fri, 15 Feb 2019 12:40:43 +0100 Subject: [PATCH 3/5] added tests --- rasa_core_sdk/executor.py | 10 +--------- tests/test_actions.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 tests/test_actions.py diff --git a/rasa_core_sdk/executor.py b/rasa_core_sdk/executor.py index ca804ce2b..668559785 100644 --- a/rasa_core_sdk/executor.py +++ b/rasa_core_sdk/executor.py @@ -102,7 +102,6 @@ def register_action(self, action): else: action = action() if isinstance(action, Action): - logger.debug("Registering {}".format(action.__class__.__name__)) self.register_function(action.name(), action.run) else: raise Exception("You can only register instances or subclasses of " @@ -150,15 +149,8 @@ def register_package(self, package): actions = utils.all_subclasses(Action) for action in actions: - logger.debug("Action: {}".format(action.__name__)) - logger.debug(action.__dict__) - meta = action.__class__.__dict__.get('Meta', False) - if meta: - logger.debug("Meta: {}".format(meta.__dict__)) - else: - logger.debug("Meta: {}".format(meta)) + meta = action.__dict__.get('Meta', False) abstract = getattr(meta, 'abstract', False) - logger.debug("Abstract: {}".format(abstract)) if (not action.__module__.startswith("rasa_core.") and not action.__module__.startswith("rasa_core_sdk.") and not abstract): diff --git a/tests/test_actions.py b/tests/test_actions.py new file mode 100644 index 000000000..3297bdb14 --- /dev/null +++ b/tests/test_actions.py @@ -0,0 +1,33 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from rasa_core_sdk import Action +from rasa_core_sdk.executor import ActionExecutor + + +def test_abstract_action(): + class CustomActionBase(Action): + @classmethod + def name(cls): + # Name method needed to test if base action was registered + return "base_action" + + class Meta: + abstract = True + + def run(self, dispatcher, tracker, domain): + # custom run method + return [] + + class CustomAction(CustomActionBase): + + @classmethod + def name(cls): + return "custom_action" + + executor = ActionExecutor() + executor.register_package('tests') + assert CustomAction.name() in executor.actions + assert CustomActionBase.name() not in executor.actions From ba336911ec1f09dfba5e253c122950650878a0bb Mon Sep 17 00:00:00 2001 From: Simon Senkl Date: Fri, 15 Feb 2019 12:55:52 +0100 Subject: [PATCH 4/5] finalized feature --- tests/test_actions.py | 50 ++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/tests/test_actions.py b/tests/test_actions.py index 3297bdb14..328ce4c92 100644 --- a/tests/test_actions.py +++ b/tests/test_actions.py @@ -3,31 +3,47 @@ from __future__ import print_function from __future__ import unicode_literals -from rasa_core_sdk import Action -from rasa_core_sdk.executor import ActionExecutor +from rasa_core_sdk import Action, Tracker +from rasa_core_sdk.events import SlotSet +from rasa_core_sdk.executor import ActionExecutor, CollectingDispatcher -def test_abstract_action(): - class CustomActionBase(Action): - @classmethod - def name(cls): - # Name method needed to test if base action was registered - return "base_action" +class CustomActionBase(Action): + @classmethod + def name(cls): + # Name method needed to test if base action was registered + return "base_action" + + class Meta: + abstract = True + + @staticmethod + def some_common_feature(): + return "test" + + def run(self, dispatcher, tracker, domain): + raise NotImplementedError - class Meta: - abstract = True - def run(self, dispatcher, tracker, domain): - # custom run method - return [] +class CustomAction(CustomActionBase): - class CustomAction(CustomActionBase): + @classmethod + def name(cls): + return "custom_action" - @classmethod - def name(cls): - return "custom_action" + def run(self, dispatcher, tracker, domain): + return [SlotSet('test', self.some_common_feature())] + +def test_abstract_action(): executor = ActionExecutor() executor.register_package('tests') assert CustomAction.name() in executor.actions assert CustomActionBase.name() not in executor.actions + + dispatcher = CollectingDispatcher() + tracker = Tracker('test', {}, {}, [], False, None, {}, 'listen') + domain = {} + + events = CustomAction().run(dispatcher, tracker, domain) + assert events == [SlotSet('test', "test")] From 1cb8bacf66f8b8ae0db3e21ce124de471c835034 Mon Sep 17 00:00:00 2001 From: Simon Senkl Date: Fri, 15 Feb 2019 12:59:06 +0100 Subject: [PATCH 5/5] updated changelog --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d6728b421..0d1934edb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,7 @@ Added - add optional `validate_{slot}` methods to `FormAction` - forms can now be deactivated during the validation function by returning `self.deactivate()` +- Abstract Actions can now be subclassed Removed -------