From e905fb0674d8cc3641025ff0c5e9c9f783795874 Mon Sep 17 00:00:00 2001 From: Christina Koss Date: Fri, 8 May 2020 14:04:16 +0200 Subject: [PATCH 1/4] make sure 'collect' functions don't alter their inputs --- rasa/core/domain.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rasa/core/domain.py b/rasa/core/domain.py index f497bc5f286c..0b070407c855 100644 --- a/rasa/core/domain.py +++ b/rasa/core/domain.py @@ -259,6 +259,8 @@ def collect_slots(slot_dict: Dict[Text, Any]) -> List[Slot]: # it is super important to sort the slots here!!! # otherwise state ordering is not consistent slots = [] + # make a copy to not alter the input dictionary + slot_dict = copy.deepcopy(slot_dict) for slot_name in sorted(slot_dict): slot_class = Slot.resolve_by_type(slot_dict[slot_name].get("type")) if "type" in slot_dict[slot_name]: @@ -332,6 +334,8 @@ def collect_intent_properties( Returns: The intent properties to be stored in the domain. """ + # make a copy to not alter the input argument + intents = copy.deepcopy(intents) intent_properties = {} duplicates = set() for intent in intents: From 14ea5ac9dd5d5c03434e36065549d48dd1ab4f50 Mon Sep 17 00:00:00 2001 From: Christina Koss Date: Fri, 8 May 2020 14:17:37 +0200 Subject: [PATCH 2/4] add changelog entry --- changelog/5794.improvement.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog/5794.improvement.rst diff --git a/changelog/5794.improvement.rst b/changelog/5794.improvement.rst new file mode 100644 index 000000000000..4204a23fdae4 --- /dev/null +++ b/changelog/5794.improvement.rst @@ -0,0 +1,3 @@ +Creating a ``Domain`` using ``Domain.fromDict`` can no longer alter the input dictionary. +Previously, there could be problems when the input dictionary was re-used for other +things after creating the ``Domain`` from it. From f61d3725eac5aeb6463c91f7ecdb0bf1e9abdd46 Mon Sep 17 00:00:00 2001 From: Christina Koss Date: Tue, 12 May 2020 12:34:04 +0200 Subject: [PATCH 3/4] add test --- rasa/core/domain.py | 1 + tests/core/test_domain.py | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/rasa/core/domain.py b/rasa/core/domain.py index 0b070407c855..1e220a15b690 100644 --- a/rasa/core/domain.py +++ b/rasa/core/domain.py @@ -134,6 +134,7 @@ def from_yaml(cls, yaml: Text) -> "Domain": @classmethod def from_dict(cls, data: Dict) -> "Domain": + data = copy.deepcopy(data) utter_templates = cls.collect_templates(data.get("responses", {})) if "templates" in data: raise_warning( diff --git a/tests/core/test_domain.py b/tests/core/test_domain.py index b8351d978072..c62a6f14d557 100644 --- a/tests/core/test_domain.py +++ b/tests/core/test_domain.py @@ -1,3 +1,4 @@ +import copy import json from pathlib import Path @@ -802,3 +803,12 @@ def test_domain_utterance_actions_deprecated_templates(): old_domain = Domain.from_yaml(old_yaml) new_domain = Domain.from_yaml(new_yaml) assert hash(old_domain) == hash(new_domain) + + +def test_domain_from_dict_does_not_change_input(): + path = DEFAULT_DOMAIN_PATH_WITH_SLOTS + input_before = io_utils.read_yaml(io_utils.read_file(path)) + input_after = copy.deepcopy(input_before) + + Domain.from_dict(input_after) + assert input_after == input_before From 1c7aadf637af68c1b7a951103d40e9a301d4cced Mon Sep 17 00:00:00 2001 From: Christina Koss Date: Thu, 14 May 2020 14:03:30 +0200 Subject: [PATCH 4/4] include review feedback --- rasa/core/domain.py | 1 - tests/core/test_domain.py | 25 ++++++++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/rasa/core/domain.py b/rasa/core/domain.py index 1e220a15b690..0b070407c855 100644 --- a/rasa/core/domain.py +++ b/rasa/core/domain.py @@ -134,7 +134,6 @@ def from_yaml(cls, yaml: Text) -> "Domain": @classmethod def from_dict(cls, data: Dict) -> "Domain": - data = copy.deepcopy(data) utter_templates = cls.collect_templates(data.get("responses", {})) if "templates" in data: raise_warning( diff --git a/tests/core/test_domain.py b/tests/core/test_domain.py index c62a6f14d557..4953d7282374 100644 --- a/tests/core/test_domain.py +++ b/tests/core/test_domain.py @@ -681,8 +681,6 @@ def test_clean_domain_deprecated_templates(): def test_add_knowledge_base_slots(default_domain): - import copy - # don't modify default domain as it is used in other tests test_domain = copy.deepcopy(default_domain) @@ -806,9 +804,26 @@ def test_domain_utterance_actions_deprecated_templates(): def test_domain_from_dict_does_not_change_input(): - path = DEFAULT_DOMAIN_PATH_WITH_SLOTS - input_before = io_utils.read_yaml(io_utils.read_file(path)) - input_after = copy.deepcopy(input_before) + input_before = { + "intents": [ + {"greet": {USE_ENTITIES_KEY: ["name"]}}, + {"default": {IGNORE_ENTITIES_KEY: ["unrelated_recognized_entity"]}}, + {"goodbye": {USE_ENTITIES_KEY: None}}, + {"thank": {USE_ENTITIES_KEY: False}}, + {"ask": {USE_ENTITIES_KEY: True}}, + {"why": {USE_ENTITIES_KEY: []}}, + "pure_intent", + ], + "entities": ["name", "unrelated_recognized_entity", "other"], + "slots": {"name": {"type": "text"}}, + "responses": { + "utter_greet": [{"text": "hey there {name}!"}], + "utter_goodbye": [{"text": "goodbye 😢"}, {"text": "bye bye 😢"}], + "utter_default": [{"text": "default message"}], + }, + } + input_after = copy.deepcopy(input_before) Domain.from_dict(input_after) + assert input_after == input_before