diff --git a/taipy/core/config/checkers/_scenario_config_checker.py b/taipy/core/config/checkers/_scenario_config_checker.py index 1ddb4582a1..1fc4c43e63 100644 --- a/taipy/core/config/checkers/_scenario_config_checker.py +++ b/taipy/core/config/checkers/_scenario_config_checker.py @@ -38,10 +38,33 @@ def _check(self) -> IssueCollector: 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 @@ def _check_comparators(self, scenario_config_id: str, scenario_config: ScenarioC 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( diff --git a/taipy/core/config/checkers/_task_config_checker.py b/taipy/core/config/checkers/_task_config_checker.py index 2ca1d654c5..911f8a7a38 100644 --- a/taipy/core/config/checkers/_task_config_checker.py +++ b/taipy/core/config/checkers/_task_config_checker.py @@ -40,8 +40,19 @@ def _check(self) -> IssueCollector: 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] ): diff --git a/tests/core/config/checkers/test_scenario_config_checker.py b/tests/core/config/checkers/test_scenario_config_checker.py index 547fa372d4..466b34f304 100644 --- a/tests/core/config/checkers/test_scenario_config_checker.py +++ b/tests/core/config/checkers/test_scenario_config_checker.py @@ -83,6 +83,47 @@ def test_check_if_entity_property_key_used_is_predefined(self, caplog): ) 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 diff --git a/tests/core/config/checkers/test_task_config_checker.py b/tests/core/config/checkers/test_task_config_checker.py index e9c48dc90b..045d5772ca 100644 --- a/tests/core/config/checkers/test_task_config_checker.py +++ b/tests/core/config/checkers/test_task_config_checker.py @@ -49,6 +49,40 @@ def test_check_config_id(self, caplog): 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