From b00be70c25db0e5d8b7bb1c35d488302e04550f9 Mon Sep 17 00:00:00 2001 From: Thierry RAMORASOAVINA Date: Thu, 27 Aug 2026 16:04:38 +0200 Subject: [PATCH 1/2] Fix a RuntimeException error when automatically correcting deprecated data paths (pre v11) in task args --- CHANGELOG.md | 5 +++++ khiops/core/api.py | 11 ++++++++--- tests/test_core.py | 20 ++++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9c3f2ea..f15c72dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ - Example: 10.2.1.4 is the 5th version that supports khiops 10.2.1. - Internals: Changes in *Internals* sections are unlikely to be of interest for data scientists. +## Unreleased + +### Fixed +- (`core`) Fix a RuntimeException error when automatically correcting deprecated data paths in task args + ## 11.0.1.0 - 2026-07-02 ### Added diff --git a/khiops/core/api.py b/khiops/core/api.py index efdc2fc5..e5de8dea 100644 --- a/khiops/core/api.py +++ b/khiops/core/api.py @@ -175,7 +175,7 @@ def _preprocess_arguments(args): .. note:: This function *mutates* the input `args` dictionary. """ - # Execute the preprocess of common task arguments + # Execute the preprocessing of common task arguments task_is_called_with_domain = _preprocess_task_arguments(args) # Create a command line options object @@ -222,6 +222,9 @@ def _deprecate_legacy_data_path(data_path_task_arg_name, task_args): """Detect and replace legacy data path with the current syntax .. note:: The function mutates task_args. + .. note:: A similar logic is repeated in `DictionaryDomain` + but cannot be factored out in a simple way + because no `DictionaryDomain` object is built here """ if ( data_path_task_arg_name in task_args @@ -233,7 +236,9 @@ def _deprecate_legacy_data_path(data_path_task_arg_name, task_args): else: current_dictionary_name = task_args["train_dictionary_name"] - for kdic_path in task_args[data_path_task_arg_name].keys(): + # Iterate through the keys of a clone to avoid any error + # while modifying the dict in-place + for kdic_path in task_args[data_path_task_arg_name].copy().keys(): if isinstance(kdic_path, str): deprecated_data_path_separator = "`" data_path_separator = "/" @@ -255,7 +260,7 @@ def _deprecate_legacy_data_path(data_path_task_arg_name, task_args): source_dictionary_name = kdic_path_parts[0] if source_dictionary_name == current_dictionary_name: # Escape any "/" char in the path parts except for the - # current dictionary, which is is skipped from the new path + # current dictionary, which is skipped from the new path new_kdic_path_parts = [] for kdic_path_part in kdic_path_parts[1:]: new_kdic_path_parts.append( diff --git a/tests/test_core.py b/tests/test_core.py index 59e645aa..8efe4628 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -25,6 +25,7 @@ import khiops.core as kh import khiops.core.internals.filesystems as fs from khiops.core import KhiopsRuntimeError +from khiops.core.api import _deprecate_legacy_data_path from khiops.core.internals.io import KhiopsOutputWriter from khiops.core.internals.runner import KhiopsLocalRunner, KhiopsRunner from khiops.core.internals.scenario import ConfigurableKhiopsScenario @@ -2569,6 +2570,25 @@ def test_dictionary_get_dictionary_at_legacy_data_path_deprecation(self): domain.get_dictionary_at_data_path(data_path), ) + def test_deprecated_legacy_data_path_in_task_args(self): + """Tests the automatic correction of deprecated data paths in task args""" + data_path_task_arg_name = "additional_data_tables" + task_args = { + "dictionary_name": "SpliceJunction", + "additional_data_tables": {"SpliceJunction`DNA": "SpliceJunctionDNA.txt"}, + } + _deprecate_legacy_data_path(data_path_task_arg_name, task_args) + self.assertNotIn( + "SpliceJunction`DNA", + task_args["additional_data_tables"], + "Deprecated data path should have been removed", + ) + self.assertEqual( + {"DNA": "SpliceJunctionDNA.txt"}, + task_args["additional_data_tables"], + "Deprecated data path should have been corrected", + ) + class ScenarioWriterRunner(KhiopsRunner): """A khiops runner that only generates scenarios to a specific subdirectory""" From 778421e53be72878b825466b962689eb203e4a61 Mon Sep 17 00:00:00 2001 From: Popescu V <136721202+popescu-v@users.noreply.github.com> Date: Mon, 31 Aug 2026 11:53:33 +0200 Subject: [PATCH 2/2] Test that _deprecate_legacy_datapath raises expected warning --- tests/test_core.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 8efe4628..df36e49c 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -2577,7 +2577,21 @@ def test_deprecated_legacy_data_path_in_task_args(self): "dictionary_name": "SpliceJunction", "additional_data_tables": {"SpliceJunction`DNA": "SpliceJunctionDNA.txt"}, } - _deprecate_legacy_data_path(data_path_task_arg_name, task_args) + with warnings.catch_warnings(record=True) as warning_list: + _deprecate_legacy_data_path(data_path_task_arg_name, task_args) + self.assertTrue(len(warning_list) > 0) + deprecation_warning_found = False + for warning in warning_list: + warning_message = warning.message + if ( + issubclass(warning.category, UserWarning) + and len(warning_message.args) == 1 + and "'`'-based dictionary data path" in warning_message.args[0] + and "deprecated" in warning_message.args[0] + ): + deprecation_warning_found = True + break + self.assertTrue(deprecation_warning_found) self.assertNotIn( "SpliceJunction`DNA", task_args["additional_data_tables"],