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. 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: diff --git a/tests/core/test_domain.py b/tests/core/test_domain.py index b8351d978072..4953d7282374 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 @@ -680,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) @@ -802,3 +801,29 @@ 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(): + 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