浏览代码

Merge pull request #1647 from Avaiga/feature/#1566-check-children-config-id-not-properties

Feature/#1566 - Check children config_id to make sure not overlapping the properties
Đỗ Trường Giang 9 月之前
父节点
当前提交
3a6090eaa5

+ 24 - 1
taipy/core/config/checkers/_scenario_config_checker.py

@@ -38,10 +38,33 @@ class _ScenarioConfigChecker(_ConfigChecker):
                 self._check_addition_data_node_configs(scenario_config_id, scenario_config)
                 self._check_additional_dns_not_overlapping_tasks_dns(scenario_config_id, scenario_config)
                 self._check_tasks_in_sequences_exist_in_scenario_tasks(scenario_config_id, scenario_config)
+                self._check_if_children_config_id_is_overlapping_with_properties(scenario_config_id, scenario_config)
                 self._check_comparators(scenario_config_id, scenario_config)
 
         return self._collector
 
+    def _check_if_children_config_id_is_overlapping_with_properties(
+        self, scenario_config_id: str, scenario_config: ScenarioConfig
+    ):
+        if scenario_config.tasks:
+            for task in scenario_config.tasks:
+                if isinstance(task, TaskConfig) and task.id in scenario_config.properties:
+                    self._error(
+                        TaskConfig._ID_KEY,
+                        task.id,
+                        f"The id of the TaskConfig `{task.id}` is overlapping with the "
+                        f"property `{task.id}` of ScenarioConfig `{scenario_config_id}`.",
+                    )
+        if scenario_config.data_nodes:
+            for data_node in scenario_config.data_nodes:
+                if isinstance(data_node, DataNodeConfig) and data_node.id in scenario_config.properties:
+                    self._error(
+                        DataNodeConfig._ID_KEY,
+                        data_node.id,
+                        f"The id of the DataNodeConfig `{data_node.id}` is overlapping with the "
+                        f"property `{data_node.id}` of ScenarioConfig `{scenario_config_id}`.",
+                    )
+
     def _check_task_configs(self, scenario_config_id: str, scenario_config: ScenarioConfig):
         self._check_children(
             ScenarioConfig,
@@ -78,7 +101,7 @@ class _ScenarioConfigChecker(_ConfigChecker):
                 f"{ScenarioConfig._COMPARATOR_KEY} field of ScenarioConfig"
                 f" `{scenario_config_id}` must be populated with a dictionary value.",
             )
-        else:
+        elif scenario_config.comparators is not None:
             for data_node_id, comparator in scenario_config.comparators.items():
                 if data_node_id not in Config.data_nodes:
                     self._error(

+ 11 - 0
taipy/core/config/checkers/_task_config_checker.py

@@ -40,8 +40,19 @@ class _TaskConfigChecker(_ConfigChecker):
                 self._check_existing_function(task_config_id, task_config)
                 self._check_inputs(task_config_id, task_config)
                 self._check_outputs(task_config_id, task_config)
+                self._check_if_children_config_id_is_overlapping_with_properties(task_config_id, task_config)
         return self._collector
 
+    def _check_if_children_config_id_is_overlapping_with_properties(self, task_config_id: str, task_config: TaskConfig):
+        for data_node in task_config.input_configs + task_config.output_configs:
+            if isinstance(data_node, DataNodeConfig) and data_node.id in task_config.properties:
+                self._error(
+                    DataNodeConfig._ID_KEY,
+                    data_node.id,
+                    f"The id of the DataNodeConfig `{data_node.id}` is overlapping with the "
+                    f"property `{data_node.id}` of TaskConfig `{task_config_id}`.",
+                )
+
     def _check_if_config_id_is_overlapping_with_scenario_attributes(
         self, task_config_id: str, task_config: TaskConfig, scenario_attributes: List[str]
     ):

+ 41 - 0
tests/core/config/checkers/test_scenario_config_checker.py

@@ -83,6 +83,47 @@ class TestScenarioConfigChecker:
         )
         assert expected_error_message in caplog.text
 
+    def test_check_if_children_id_is_used_in_properties(self, caplog):
+        config = Config._applied_config
+        Config._compile_configs()
+        input_dn_config = DataNodeConfig("input_dn")
+        output_dn_config = DataNodeConfig("output_dn")
+        test_dn_config = DataNodeConfig("test")
+        task_config = TaskConfig("bar", print, [input_dn_config], [output_dn_config])
+        test_task_config = TaskConfig("test", print, [test_dn_config], [output_dn_config])
+
+        config._sections[ScenarioConfig.name]["new"] = copy(config._sections[ScenarioConfig.name]["default"])
+        config._sections[ScenarioConfig.name]["new"]._properties["test"] = "test"
+        config._sections[ScenarioConfig.name]["new"]._tasks = [task_config]
+        Config._collector = IssueCollector()
+        Config.check()
+        assert len(Config._collector.errors) == 0
+
+        config._sections[ScenarioConfig.name]["new"]._tasks = [test_task_config]
+        with pytest.raises(SystemExit):
+            Config._collector = IssueCollector()
+            Config.check()
+        assert len(Config._collector.errors) == 2
+        assert (
+            "The id of the TaskConfig `test` is overlapping with the property `test` of ScenarioConfig `new`."
+            in caplog.text
+        )
+        assert (
+            "The id of the DataNodeConfig `test` is overlapping with the property `test` of ScenarioConfig `new`."
+            in caplog.text
+        )
+
+        config._sections[ScenarioConfig.name]["new"]._tasks = [task_config]
+        config._sections[ScenarioConfig.name]["new"]._additional_data_nodes = [test_dn_config]
+        with pytest.raises(SystemExit):
+            Config._collector = IssueCollector()
+            Config.check()
+        assert len(Config._collector.errors) == 1
+        assert (
+            "The id of the DataNodeConfig `test` is overlapping with the property `test` of ScenarioConfig `new`."
+            in caplog.text
+        )
+
     def test_check_task_configs(self, caplog):
         Config._collector = IssueCollector()
         config = Config._applied_config

+ 34 - 0
tests/core/config/checkers/test_task_config_checker.py

@@ -49,6 +49,40 @@ class TestTaskConfigChecker:
         assert len(Config._collector.errors) == 1
         assert len(Config._collector.warnings) == 2
 
+    def test_check_if_input_output_id_is_used_in_properties(self, caplog):
+        config = Config._applied_config
+        Config._compile_configs()
+        input_dn_config = DataNodeConfig("input_dn")
+        test_dn_config = DataNodeConfig("test")
+
+        config._sections[TaskConfig.name]["new"] = copy(config._sections[TaskConfig.name]["default"])
+        config._sections[TaskConfig.name]["new"].function = print
+        config._sections[TaskConfig.name]["new"]._properties["test"] = "test"
+        config._sections[TaskConfig.name]["new"]._inputs = [input_dn_config]
+        Config._collector = IssueCollector()
+        Config.check()
+        assert len(Config._collector.errors) == 0
+
+        config._sections[TaskConfig.name]["new"]._inputs = [test_dn_config]
+        with pytest.raises(SystemExit):
+            Config._collector = IssueCollector()
+            Config.check()
+        assert len(Config._collector.errors) == 1
+        assert (
+            "The id of the DataNodeConfig `test` is overlapping with the property `test` of TaskConfig `new`."
+            in caplog.text
+        )
+
+        config._sections[TaskConfig.name]["new"]._outputs = [test_dn_config]
+        with pytest.raises(SystemExit):
+            Config._collector = IssueCollector()
+            Config.check()
+        assert len(Config._collector.errors) == 2
+        assert (
+            "The id of the DataNodeConfig `test` is overlapping with the property `test` of TaskConfig `new`."
+            in caplog.text
+        )
+
     def test_check_config_id_is_different_from_all_task_properties(self, caplog):
         Config._collector = IssueCollector()
         config = Config._applied_config