Browse Source

Merge pull request #1633 from Avaiga/feature/enterprise#431-remove-production-version

Feature/enterprise#431 - Remove production version
Đỗ Trường Giang 9 months ago
parent
commit
421f854374
47 changed files with 218 additions and 1345 deletions
  1. 0 1
      .gitignore
  2. 7 5
      taipy/_entrypoint.py
  3. 2 2
      taipy/_run.py
  4. 4 25
      taipy/config/config.pyi
  5. 2 3
      taipy/config/stubs/generate_pyi.py
  6. 1 1
      taipy/config/stubs/pyi_header.py
  7. 0 0
      taipy/core/_cli/__init__.py
  8. 6 20
      taipy/core/_cli/_core_cli.py
  9. 33 0
      taipy/core/_cli/_core_cli_factory.py
  10. 4 3
      taipy/core/_core.py
  11. 8 38
      taipy/core/_version/_cli/_version_cli.py
  12. 33 0
      taipy/core/_version/_cli/_version_cli_factory.py
  13. 0 49
      taipy/core/_version/_utils.py
  14. 4 54
      taipy/core/_version/_version_fs_repository.py
  15. 50 90
      taipy/core/_version/_version_manager.py
  16. 0 46
      taipy/core/_version/_version_repository_interface.py
  17. 0 9
      taipy/core/config/__init__.py
  18. 0 66
      taipy/core/config/checkers/_migration_config_checker.py
  19. 6 7
      taipy/core/config/core_section.py
  20. 0 113
      taipy/core/config/migration_config.py
  21. 1 3
      taipy/core/data/_data_converter.py
  22. 6 2
      taipy/core/exceptions/exceptions.py
  23. 1 3
      taipy/core/scenario/_scenario_converter.py
  24. 7 24
      taipy/core/taipy.py
  25. 1 4
      taipy/core/task/_task_converter.py
  26. 1 8
      tests/conftest.py
  27. 10 0
      tests/core/_version/__init__.py
  28. 0 0
      tests/core/_version/dataset_2.0/cycles/CYCLE_Frequency.DAILY_2023-01-18T172525.892619_8956558e-d108-4ac4-8684-60f3e5ef9e8f.json
  29. 0 0
      tests/core/_version/dataset_2.0/data_nodes/DATANODE_d1_3d65c33b-b188-4402-8d2c-3b26d98b9a9e.json
  30. 0 0
      tests/core/_version/dataset_2.0/data_nodes/DATANODE_d2_bd8ee43e-6fa9-4832-b2e7-c0c9516b1e1c.json
  31. 0 0
      tests/core/_version/dataset_2.0/scenarios/SCENARIO_my_scenario_c4307ae8-d2ce-4802-8b16-8307baa7cff1.json
  32. 0 0
      tests/core/_version/dataset_2.0/sequences/SEQUENCE_my_sequence_1b97a539-ca51-4eb8-aa64-c232269618c6.json
  33. 0 0
      tests/core/_version/dataset_2.0/tasks/TASK_my_task_53cf9993-047f-4220-9c03-e28fa250f6b3.json
  34. 0 0
      tests/core/_version/test_version.py
  35. 23 99
      tests/core/_version/test_version_cli.py
  36. 0 0
      tests/core/_version/test_version_manager.py
  37. 0 0
      tests/core/_version/test_version_repositories.py
  38. 0 132
      tests/core/config/checkers/test_migration_config_checker.py
  39. 5 35
      tests/core/config/test_config_serialization.py
  40. 2 2
      tests/core/config/test_core_section.py
  41. 0 5
      tests/core/config/test_core_version.py
  42. 1 12
      tests/core/config/test_default_config.py
  43. 0 2
      tests/core/config/test_file_config.py
  44. 0 65
      tests/core/config/test_migration_config.py
  45. 0 1
      tests/core/conftest.py
  46. 0 136
      tests/core/test_core_cli.py
  47. 0 280
      tests/core/version/test_production_version_migration.py

+ 0 - 1
.gitignore

@@ -95,5 +95,4 @@ demo_*/
 *.dags
 data_sources
 pipelines
-tasks
 pickles

+ 7 - 5
taipy/_entrypoint.py

@@ -14,9 +14,9 @@ import sys
 from importlib.util import find_spec
 
 from taipy._cli._base_cli._taipy_parser import _TaipyParser
-from taipy.core._core_cli import _CoreCLI
+from taipy.core._cli._core_cli_factory import _CoreCLIFactory
 from taipy.core._entity._migrate_cli import _MigrateCLI
-from taipy.core._version._cli._version_cli import _VersionCLI
+from taipy.core._version._cli._version_cli_factory import _VersionCLIFactory
 from taipy.gui._gui_cli import _GuiCLI
 
 from ._cli._create_cli import _CreateCLI
@@ -41,11 +41,13 @@ def _entrypoint():
 
         _enterprise_entrypoint_initialize()
 
+    _core_cli = _CoreCLIFactory._build_cli()
+
     _RunCLI.create_parser()
     _GuiCLI.create_run_parser()
-    _CoreCLI.create_run_parser()
+    _core_cli.create_run_parser()
 
-    _VersionCLI.create_parser()
+    _VersionCLIFactory._build_cli().create_parser()
     _CreateCLI.generate_template_map()
     _CreateCLI.create_parser()
     _MigrateCLI.create_parser()
@@ -63,7 +65,7 @@ def _entrypoint():
 
     _RunCLI.handle_command()
     _HelpCLI.handle_command()
-    _VersionCLI.handle_command()
+    _VersionCLIFactory._build_cli().handle_command()
     _MigrateCLI.handle_command()
     _CreateCLI.handle_command()
 

+ 2 - 2
taipy/_run.py

@@ -42,10 +42,10 @@ def _run(*services: _AppType, **kwargs) -> t.Optional[Flask]:
     core = __get_app(services, Core)
 
     if gui and core:
-        from taipy.core._core_cli import _CoreCLI
+        from taipy.core._cli._core_cli_factory import _CoreCLIFactory
         from taipy.gui._gui_cli import _GuiCLI
 
-        _CoreCLI.create_parser()
+        _CoreCLIFactory._build_cli().create_parser()
         _GuiCLI.create_parser()
 
     if rest or core:

+ 4 - 25
taipy/config/config.pyi

@@ -14,7 +14,7 @@ from datetime import timedelta
 from typing import Any, Callable, Dict, List, Optional, Union
 
 from taipy.config._config import _Config
-from taipy.core.config import CoreSection, DataNodeConfig, JobConfig, MigrationConfig, ScenarioConfig, TaskConfig
+from taipy.core.config import CoreSection, DataNodeConfig, JobConfig, ScenarioConfig, TaskConfig
 
 from .checker.issue_collector import IssueCollector
 from .common._classproperty import _Classproperty
@@ -169,10 +169,6 @@ class Config:
     def scenarios(cls) -> Dict[str, ScenarioConfig]:
         """"""
 
-    @_Classproperty
-    def migration_functions(cls) -> Dict[str, MigrationConfig]:
-        """"""
-
     @_Classproperty
     def core(cls) -> Dict[str, CoreSection]:
         """"""
@@ -852,25 +848,6 @@ class Config:
             The new job execution configuration.
         """
 
-    @staticmethod
-    def add_migration_function(
-        target_version: str,
-        config: Union[Section, str],
-        migration_fct: Callable,
-        **properties,
-    ):
-        """Add a migration function for a Configuration to migrate entities to the target version.
-
-        Parameters:
-            target_version (str): The production version that entities are migrated to.
-            config (Union[Section, str]): The configuration or the `id` of the config that needs to migrate.
-            migration_fct (Callable): Migration function that takes an entity as input and returns a new entity
-                that is compatible with the target production version.
-            **properties (Dict[str, Any]): A keyworded variable length list of additional arguments.
-        Returns:
-            `MigrationConfig^`: The Migration configuration.
-        """
-
     @staticmethod
     def configure_core(
         root_folder: Optional[str] = None,
@@ -902,7 +879,9 @@ class Config:
             read_entity_retry (Optional[int]): Number of retries to read an entity from the repository
                 before return failure. The default value is 3.
             mode (Optional[str]): Indicates the mode of the version management system.
-                Possible values are *"development"*, *"experiment"*, or *"production"*.
+                Possible values are *"development"* or *"experiment"*. On Enterprise edition of Taipy,
+                *production* mode is also available. Please refer to the
+                [Versioning management](../../userman/versioning/index.md) documentation page for more details.
             version_number (Optional[str]): The string identifier of the version.
                  In development mode, the version number is ignored.
             force (Optional[bool]): If True, Taipy will override a version even if the configuration

+ 2 - 3
taipy/config/stubs/generate_pyi.py

@@ -14,7 +14,8 @@ import re
 from pathlib import Path
 from typing import List
 
-_end_doc = re.compile(r'\"\"\"\s*(#\s*noqa\s*:\s*E501)?\s*\n')
+_end_doc = re.compile(r"\"\"\"\s*(#\s*noqa\s*:\s*E501)?\s*\n")
+
 
 def _get_function_delimiters(initial_line, lines):
     begin = end = initial_line
@@ -148,7 +149,6 @@ if __name__ == "__main__":
     job_filename = "taipy/core/config/job_config.py"
     scenario_filename = "taipy/core/config/scenario_config.py"
     task_filename = "taipy/core/config/task_config.py"
-    migration_filename = "taipy/core/config/migration_config.py"
     core_filename = "taipy/core/config/core_section.py"
 
     entities_map, property_map = _generate_entity_and_property_maps(config_init)
@@ -159,7 +159,6 @@ if __name__ == "__main__":
     pyi = _build_entity_config_pyi(pyi, dn_filename, entities_map["DataNodeConfig"])
     pyi = _build_entity_config_pyi(pyi, task_filename, entities_map["TaskConfig"])
     pyi = _build_entity_config_pyi(pyi, job_filename, entities_map["JobConfig"])
-    pyi = _build_entity_config_pyi(pyi, migration_filename, entities_map["MigrationConfig"])
     pyi = _build_entity_config_pyi(pyi, core_filename, entities_map["CoreSection"])
 
     # Remove the final redundant \n

+ 1 - 1
taipy/config/stubs/pyi_header.py

@@ -14,7 +14,7 @@ from datetime import timedelta
 from typing import Any, Callable, Dict, List, Optional, Union
 
 from taipy.config._config import _Config
-from taipy.core.config import CoreSection, DataNodeConfig, JobConfig, MigrationConfig, ScenarioConfig, TaskConfig
+from taipy.core.config import CoreSection, DataNodeConfig, JobConfig, ScenarioConfig, TaskConfig
 
 from .checker.issue_collector import IssueCollector
 from .common._classproperty import _Classproperty

+ 0 - 0
tests/core/version/__init__.py → taipy/core/_cli/__init__.py


+ 6 - 20
taipy/core/_core_cli.py → taipy/core/_cli/_core_cli.py

@@ -14,13 +14,13 @@ from typing import Dict
 from taipy._cli._base_cli._abstract_cli import _AbstractCLI
 from taipy._cli._base_cli._taipy_parser import _TaipyParser
 
-from .config import CoreSection
+from ..config import CoreSection
 
 
 class _CoreCLI(_AbstractCLI):
     """Command-line interface for Taipy Core application."""
 
-    __MODE_ARGS: Dict[str, Dict] = {
+    _mode_args: Dict[str, Dict] = {
         "--development": {
             "action": "store_true",
             "dest": "taipy_development",
@@ -40,17 +40,6 @@ class _CoreCLI(_AbstractCLI):
                 application. Without being specified, the version number will be a random string.
             """,
         },
-        "--production": {
-            "dest": "taipy_production",
-            "nargs": "?",
-            "const": "",
-            "metavar": "VERSION",
-            "help": """
-                When execute in `production` mode, the current version is used in production. All production versions
-                should have the same configuration and share all entities. Without being specified, the latest version
-                is used.
-            """,
-        },
     }
 
     __FORCE_ARGS: Dict[str, Dict] = {
@@ -73,7 +62,7 @@ class _CoreCLI(_AbstractCLI):
         core_parser = _TaipyParser._add_groupparser("Taipy Core", "Optional arguments for Taipy Core service")
 
         mode_group = core_parser.add_mutually_exclusive_group()
-        for mode_arg, mode_arg_dict in cls.__MODE_ARGS.items():
+        for mode_arg, mode_arg_dict in cls._mode_args.items():
             mode_group.add_argument(mode_arg, cls.__add_taipy_prefix(mode_arg), **mode_arg_dict)
 
         force_group = core_parser.add_mutually_exclusive_group()
@@ -84,7 +73,7 @@ class _CoreCLI(_AbstractCLI):
     def create_run_parser(cls):
         run_parser = _TaipyParser._add_subparser("run", help="Run a Taipy application.")
         mode_group = run_parser.add_mutually_exclusive_group()
-        for mode_arg, mode_arg_dict in cls.__MODE_ARGS.items():
+        for mode_arg, mode_arg_dict in cls._mode_args.items():
             mode_group.add_argument(mode_arg, **mode_arg_dict)
 
         force_group = run_parser.add_mutually_exclusive_group()
@@ -97,13 +86,10 @@ class _CoreCLI(_AbstractCLI):
 
         as_dict = {}
         if args.taipy_development:
-            as_dict[CoreSection._MODE_KEY] = CoreSection._DEVELOPMENT_MODE
+            as_dict[CoreSection._MODE_KEY] = "development"
         elif args.taipy_experiment is not None:
-            as_dict[CoreSection._MODE_KEY] = CoreSection._EXPERIMENT_MODE
+            as_dict[CoreSection._MODE_KEY] = "experiment"
             as_dict[CoreSection._VERSION_NUMBER_KEY] = args.taipy_experiment
-        elif args.taipy_production is not None:
-            as_dict[CoreSection._MODE_KEY] = CoreSection._PRODUCTION_MODE
-            as_dict[CoreSection._VERSION_NUMBER_KEY] = args.taipy_production
 
         if args.taipy_force:
             as_dict[CoreSection._FORCE_KEY] = True

+ 33 - 0
taipy/core/_cli/_core_cli_factory.py

@@ -0,0 +1,33 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+
+from importlib import import_module, util
+from operator import attrgetter
+from typing import Type
+
+from taipy._cli._base_cli._abstract_cli import _AbstractCLI
+
+from ._core_cli import _CoreCLI
+
+
+class _CoreCLIFactory:
+    _TAIPY_ENTERPRISE_MODULE = "taipy.enterprise"
+    _TAIPY_ENTERPRISE_CORE_MODULE = _TAIPY_ENTERPRISE_MODULE + ".core"
+
+    @classmethod
+    def _build_cli(cls) -> Type[_AbstractCLI]:
+        if util.find_spec(cls._TAIPY_ENTERPRISE_MODULE) is not None:
+            module = import_module(cls._TAIPY_ENTERPRISE_CORE_MODULE + "._cli._core_cli")
+            core_cli = attrgetter("_CoreCLI")(module)
+        else:
+            core_cli = _CoreCLI
+
+        return core_cli

+ 4 - 3
taipy/core/_core.py

@@ -15,7 +15,7 @@ from typing import Optional
 from taipy.config import Config
 from taipy.logger._taipy_logger import _TaipyLogger
 
-from ._core_cli import _CoreCLI
+from ._cli._core_cli_factory import _CoreCLIFactory
 from ._orchestrator._dispatcher._job_dispatcher import _JobDispatcher
 from ._orchestrator._orchestrator import _Orchestrator
 from ._orchestrator._orchestrator_factory import _OrchestratorFactory
@@ -102,8 +102,9 @@ class Core:
     @classmethod
     def __update_core_section(cls):
         cls.__logger.info("Updating configuration with command-line arguments...")
-        _CoreCLI.create_parser()
-        Config._applied_config._unique_sections[CoreSection.name]._update(_CoreCLI.handle_command())
+        _core_cli = _CoreCLIFactory._build_cli()
+        _core_cli.create_parser()
+        Config._applied_config._unique_sections[CoreSection.name]._update(_core_cli.handle_command())
 
     @classmethod
     def __manage_version(cls):

+ 8 - 38
taipy/core/_version/_cli/_version_cli.py

@@ -10,6 +10,7 @@
 # specific language governing permissions and limitations under the License.
 
 import sys
+from importlib.util import find_spec
 
 from taipy._cli._base_cli._abstract_cli import _AbstractCLI
 from taipy._cli._base_cli._taipy_parser import _TaipyParser
@@ -17,7 +18,6 @@ from taipy.config import Config
 from taipy.config.exceptions.exceptions import InconsistentEnvVariableError
 
 from ...data._data_manager_factory import _DataManagerFactory
-from ...exceptions.exceptions import VersionIsNotProductionVersion
 from ...job._job_manager_factory import _JobManagerFactory
 from ...scenario._scenario_manager_factory import _ScenarioManagerFactory
 from ...sequence._sequence_manager_factory import _SequenceManagerFactory
@@ -31,7 +31,7 @@ class _VersionCLI(_AbstractCLI):
     """Command-line interface of the versioning system."""
 
     _COMMAND_NAME = "manage-versions"
-    _ARGUMENTS = ["-l", "--list", "--rename", "--compare-config", "-d", "--delete", "-dp", "--delete-production"]
+    _ARGUMENTS = ["-l", "--list", "--rename", "--compare-config", "-d", "--delete"]
 
     @classmethod
     def create_parser(cls):
@@ -56,14 +56,6 @@ class _VersionCLI(_AbstractCLI):
             "-d", "--delete", metavar="VERSION", help="Delete a Taipy version by version number."
         )
 
-        version_parser.add_argument(
-            "-dp",
-            "--delete-production",
-            metavar="VERSION",
-            help="Delete a Taipy version from production by version number. The version is still kept as an experiment "
-            "version.",
-        )
-
     @classmethod
     def handle_command(cls):
         args = cls._parse_arguments()
@@ -91,16 +83,6 @@ class _VersionCLI(_AbstractCLI):
             cls.__compare_version_config(args.compare_config[0], args.compare_config[1])
             sys.exit(0)
 
-        if args.delete_production:
-            try:
-                _VersionManagerFactory._build_manager()._delete_production_version(args.delete_production)
-                cls._logger.info(
-                    f"Successfully delete version {args.delete_production} from the production version list."
-                )
-                sys.exit(0)
-            except VersionIsNotProductionVersion as e:
-                raise SystemExit(e) from None
-
         if args.delete:
             if clean_all_entities(args.delete):
                 cls._logger.info(f"Successfully delete version {args.delete}.")
@@ -115,7 +97,10 @@ class _VersionCLI(_AbstractCLI):
 
         latest_version_number = _VersionManagerFactory._build_manager()._get_latest_version()
         development_version_number = _VersionManagerFactory._build_manager()._get_development_version()
-        production_version_numbers = _VersionManagerFactory._build_manager()._get_production_versions()
+        if find_spec("taipy.enterprise"):
+            production_version_numbers = _VersionManagerFactory._build_manager()._get_production_versions()
+        else:
+            production_version_numbers = []
 
         versions = _VersionManagerFactory._build_manager()._get_all()
         versions.sort(key=lambda x: x.creation_date, reverse=True)
@@ -186,23 +171,8 @@ class _VersionCLI(_AbstractCLI):
             datanode._version = new_version
             _DataManagerFactory._build_manager()._set(datanode)
 
-        # Update the version entity
-        if old_version in _version_manager._get_production_versions():
-            _version_manager._set_production_version(new_version)
-        if old_version == _version_manager._get_latest_version():
-            _version_manager._set_experiment_version(new_version)
-        if old_version == _version_manager._get_development_version():
-            _version_manager._set_development_version(new_version)
-        _version_manager._delete(old_version)
-
-        try:
-            _version_manager._delete_production_version(old_version)
-        except VersionIsNotProductionVersion:
-            pass
-
-        if not _version_manager._get(new_version):
-            version_entity.id = new_version
-            _version_manager._set(version_entity)
+        # Rename the _Version entity
+        _version_manager._rename_version(old_version, new_version)
 
     @classmethod
     def __compare_version_config(cls, version_1: str, version_2: str):

+ 33 - 0
taipy/core/_version/_cli/_version_cli_factory.py

@@ -0,0 +1,33 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+
+from importlib import import_module, util
+from operator import attrgetter
+from typing import Type
+
+from taipy._cli._base_cli._abstract_cli import _AbstractCLI
+
+from ._version_cli import _VersionCLI
+
+
+class _VersionCLIFactory:
+    _TAIPY_ENTERPRISE_MODULE = "taipy.enterprise"
+    _TAIPY_ENTERPRISE_CORE_MODULE = _TAIPY_ENTERPRISE_MODULE + ".core"
+
+    @classmethod
+    def _build_cli(cls) -> Type[_AbstractCLI]:
+        if util.find_spec(cls._TAIPY_ENTERPRISE_MODULE) is not None:
+            module = import_module(cls._TAIPY_ENTERPRISE_CORE_MODULE + "._version._cli._version_cli")
+            core_cli = attrgetter("_VersionCLI")(module)
+        else:
+            core_cli = _VersionCLI
+
+        return core_cli

+ 0 - 49
taipy/core/_version/_utils.py

@@ -1,49 +0,0 @@
-# Copyright 2021-2024 Avaiga Private Limited
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
-# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations under the License.
-
-from typing import Callable, List
-
-from taipy.config.config import Config
-
-from .._entity._reload import _Reloader
-from ..config import MigrationConfig
-from ._version_manager_factory import _VersionManagerFactory
-
-
-def _migrate_entity(entity):
-    if (
-        latest_version := _VersionManagerFactory._build_manager()._get_latest_version()
-    ) in _VersionManagerFactory._build_manager()._get_production_versions():
-        if migration_fcts := __get_migration_fcts_to_latest(entity._version, entity.config_id):
-            with _Reloader():
-                for fct in migration_fcts:
-                    entity = fct(entity)
-                entity._version = latest_version
-
-    return entity
-
-
-def __get_migration_fcts_to_latest(source_version: str, config_id: str) -> List[Callable]:
-    migration_fcts_to_latest: List[Callable] = []
-
-    production_versions = _VersionManagerFactory._build_manager()._get_production_versions()
-    try:
-        start_index = production_versions.index(source_version) + 1
-    except ValueError:
-        return migration_fcts_to_latest
-
-    versions_to_migrate = production_versions[start_index:]
-
-    for version in versions_to_migrate:
-        if migration_fct := Config.unique_sections[MigrationConfig.name].migration_fcts.get(version, {}).get(config_id):
-            migration_fcts_to_latest.append(migration_fct)
-
-    return migration_fcts_to_latest

+ 4 - 54
taipy/core/_version/_version_fs_repository.py

@@ -10,18 +10,16 @@
 # specific language governing permissions and limitations under the License.
 
 import json
-from typing import List
-
-from taipy.logger._taipy_logger import _TaipyLogger
 
 from .._repository._filesystem_repository import _FileSystemRepository
-from ..exceptions.exceptions import VersionIsNotProductionVersion
 from ._version_converter import _VersionConverter
 from ._version_model import _VersionModel
-from ._version_repository_interface import _VersionRepositoryInterface
 
 
-class _VersionFSRepository(_FileSystemRepository, _VersionRepositoryInterface):
+class _VersionFSRepository(_FileSystemRepository):
+    _LATEST_VERSION_KEY = "latest_version"
+    _DEVELOPMENT_VERSION_KEY = "development_version"
+
     def __init__(self) -> None:
         super().__init__(model_type=_VersionModel, converter=_VersionConverter, dir_name="version")
 
@@ -45,7 +43,6 @@ class _VersionFSRepository(_FileSystemRepository, _VersionRepositoryInterface):
             file_content = {
                 self._LATEST_VERSION_KEY: version_number,
                 self._DEVELOPMENT_VERSION_KEY: "",
-                self._PRODUCTION_VERSION_KEY: [],
             }
         self._version_file_path.write_text(
             json.dumps(
@@ -72,7 +69,6 @@ class _VersionFSRepository(_FileSystemRepository, _VersionRepositoryInterface):
             file_content = {
                 self._LATEST_VERSION_KEY: version_number,
                 self._DEVELOPMENT_VERSION_KEY: version_number,
-                self._PRODUCTION_VERSION_KEY: [],
             }
         self._version_file_path.write_text(
             json.dumps(
@@ -86,49 +82,3 @@ class _VersionFSRepository(_FileSystemRepository, _VersionRepositoryInterface):
         with open(self._version_file_path, "r") as f:
             file_content = json.load(f)
         return file_content[self._DEVELOPMENT_VERSION_KEY]
-
-    def _set_production_version(self, version_number):
-        if self._version_file_path.exists():
-            with open(self._version_file_path, "r") as f:
-                file_content = json.load(f)
-            file_content[self._LATEST_VERSION_KEY] = version_number
-            if version_number not in file_content[self._PRODUCTION_VERSION_KEY]:
-                file_content[self._PRODUCTION_VERSION_KEY].append(version_number)
-            else:
-                _TaipyLogger._get_logger().info(f"Version {version_number} is already a production version.")
-        else:
-            self.dir_path.mkdir(parents=True, exist_ok=True)
-            file_content = {
-                self._LATEST_VERSION_KEY: version_number,
-                self._DEVELOPMENT_VERSION_KEY: "",
-                self._PRODUCTION_VERSION_KEY: [version_number],
-            }
-        self._version_file_path.write_text(
-            json.dumps(
-                file_content,
-                ensure_ascii=False,
-                indent=0,
-            )
-        )
-
-    def _get_production_versions(self) -> List[str]:
-        with open(self._version_file_path, "r") as f:
-            file_content = json.load(f)
-        return file_content[self._PRODUCTION_VERSION_KEY]
-
-    def _delete_production_version(self, version_number):
-        try:
-            with open(self._version_file_path, "r") as f:
-                file_content = json.load(f)
-            if version_number not in file_content[self._PRODUCTION_VERSION_KEY]:
-                raise VersionIsNotProductionVersion(f"Version '{version_number}' is not a production version.")
-            file_content[self._PRODUCTION_VERSION_KEY].remove(version_number)
-            self._version_file_path.write_text(
-                json.dumps(
-                    file_content,
-                    ensure_ascii=False,
-                    indent=0,
-                )
-            )
-        except FileNotFoundError:
-            raise VersionIsNotProductionVersion(f"Version '{version_number}' is not a production version.") from None

+ 50 - 90
taipy/core/_version/_version_manager.py

@@ -14,13 +14,18 @@ from typing import List, Optional, Union
 
 from taipy.config import Config
 from taipy.config._config_comparator._comparator_result import _ComparatorResult
-from taipy.config.checker.issue_collector import IssueCollector
 from taipy.config.exceptions.exceptions import InconsistentEnvVariableError
-from taipy.core.exceptions.exceptions import ConfigCoreVersionMismatched
 from taipy.logger._taipy_logger import _TaipyLogger
 
 from .._manager._manager import _Manager
-from ..exceptions.exceptions import ConflictedConfigurationError, ModelNotFound, NonExistingVersion
+from ..exceptions.exceptions import (
+    ConfigCoreVersionMismatched,
+    ConflictedConfigurationError,
+    ModelNotFound,
+    NonExistingVersion,
+    VersionAlreadyExistsAsDevelopment,
+)
+from ..reason import ReasonCollection
 from ._version import _Version
 from ._version_fs_repository import _VersionFSRepository
 
@@ -28,14 +33,13 @@ from ._version_fs_repository import _VersionFSRepository
 class _VersionManager(_Manager[_Version]):
     _ENTITY_NAME = _Version.__name__
 
-    __logger = _TaipyLogger._get_logger()
+    _logger = _TaipyLogger._get_logger()
 
-    __DEVELOPMENT_VERSION = ["development", "dev"]
-    __LATEST_VERSION = "latest"
-    __PRODUCTION_VERSION = "production"
-    __ALL_VERSION = ["all", ""]
+    _DEVELOPMENT_VERSION = ["development", "dev"]
+    _LATEST_VERSION = "latest"
+    _ALL_VERSION = ["all", ""]
 
-    _DEFAULT_VERSION = __LATEST_VERSION
+    _DEFAULT_VERSION = _LATEST_VERSION
 
     _repository: _VersionFSRepository
 
@@ -58,7 +62,7 @@ class _VersionManager(_Manager[_Version]):
                 if not force:
                     raise ConflictedConfigurationError()
 
-                cls.__logger.warning(f"Option --force is detected, overriding the configuration of version {id} ...")
+                cls._logger.warning(f"Option --force is detected, overriding the configuration of version {id} ...")
                 version.config = Config._applied_config  # type: ignore[attr-defined]
 
         else:
@@ -78,6 +82,10 @@ class _VersionManager(_Manager[_Version]):
         filters = [{"version": version} for version in version_number]
         return cls._repository._load_all(filters)
 
+    @classmethod
+    def _is_deletable(cls, version_number: Optional[str]) -> ReasonCollection:
+        return ReasonCollection()
+
     @classmethod
     def _set_development_version(cls, version_number: str) -> str:
         cls._get_or_create(version_number, force=True)
@@ -94,16 +102,7 @@ class _VersionManager(_Manager[_Version]):
     @classmethod
     def _set_experiment_version(cls, version_number: str, force: bool = False) -> str:
         if version_number == cls._get_development_version():
-            raise SystemExit(
-                f"Version number {version_number} is the development version. Please choose a different name"
-                f" for this experiment."
-            )
-
-        if version_number in cls._get_production_versions():
-            raise SystemExit(
-                f"Version number {version_number} is already a production version. Please choose a different name"
-                f" for this experiment."
-            )
+            raise VersionAlreadyExistsAsDevelopment(version_number)
 
         try:
             cls._get_or_create(version_number, force)
@@ -124,51 +123,16 @@ class _VersionManager(_Manager[_Version]):
             # This set the default versioning behavior on Jupyter notebook to Development mode
             return cls._set_development_version(str(uuid.uuid4()))
 
-    @classmethod
-    def _set_production_version(cls, version_number: str, force: bool = False) -> str:
-        if version_number == cls._get_development_version():
-            cls._set_development_version(str(uuid.uuid4()))
-
-        try:
-            cls._get_or_create(version_number, force)
-        except ConflictedConfigurationError:
-            raise SystemExit(
-                f"Please add a new production version with migration functions.\n"
-                f"If old entities remain compatible with the new configuration, you can also run your application with"
-                f" --force option to override the production configuration of version {version_number}."
-            ) from None
-        cls._repository._set_production_version(version_number)
-        return version_number
-
-    @classmethod
-    def _get_production_versions(cls) -> List[str]:
-        try:
-            return cls._repository._get_production_versions()
-        except (FileNotFoundError, ModelNotFound):
-            return []
-
-    @classmethod
-    def _delete_production_version(cls, version_number) -> str:
-        return cls._repository._delete_production_version(version_number)
-
     @classmethod
     def _replace_version_number(cls, version_number: Optional[str] = None):
         if version_number is None:
-            version_number = cls._replace_version_number(cls._DEFAULT_VERSION)
+            return cls._replace_version_number(cls._DEFAULT_VERSION)
 
-            production_versions = cls._get_production_versions()
-            if version_number in production_versions:
-                return production_versions
-
-            return version_number
-
-        if version_number == cls.__LATEST_VERSION:
+        if version_number == cls._LATEST_VERSION:
             return cls._get_latest_version()
-        if version_number in cls.__DEVELOPMENT_VERSION:
+        if version_number in cls._DEVELOPMENT_VERSION:
             return cls._get_development_version()
-        if version_number == cls.__PRODUCTION_VERSION:
-            return cls._get_production_versions()
-        if version_number in cls.__ALL_VERSION:
+        if version_number in cls._ALL_VERSION:
             return ""
 
         try:
@@ -183,50 +147,46 @@ class _VersionManager(_Manager[_Version]):
         raise NonExistingVersion(version_number)
 
     @classmethod
-    def _manage_version(cls):
-        from ..taipy import clean_all_entities
+    def _rename_version(cls, old_version: str, new_version: str) -> None:
+        version_entity = cls._get(old_version)
+
+        if old_version == cls._get_latest_version():
+            try:
+                cls._set_experiment_version(new_version)
+            except VersionAlreadyExistsAsDevelopment as err:
+                raise SystemExit(err.message) from None
+        if old_version == cls._get_development_version():
+            cls._set_development_version(new_version)
+        cls._delete(old_version)
+
+        if not cls._get(new_version):
+            version_entity.id = new_version
+            cls._set(version_entity)
 
+    @classmethod
+    def _manage_version(cls) -> None:
         if Config.core.mode == "development":
+            from ..taipy import clean_all_entities
+
             current_version_number = cls._get_development_version()
-            cls.__logger.info(f"Development mode: Clean all entities of version {current_version_number}")
+            cls._logger.info(f"Development mode: Clean all entities of version {current_version_number}")
             clean_all_entities(current_version_number)
             cls._set_development_version(current_version_number)
 
-        elif Config.core.mode in ["experiment", "production"]:
-            default_version_number = {
-                "experiment": str(uuid.uuid4()),
-                "production": cls._get_latest_version(),
-            }
-            version_setter = {
-                "experiment": cls._set_experiment_version,
-                "production": cls._set_production_version,
-            }
-
+        elif Config.core.mode == "experiment":
             if Config.core.version_number:
                 current_version_number = Config.core.version_number
             else:
-                current_version_number = default_version_number[Config.core.mode]
+                current_version_number = str(uuid.uuid4())
+
+            try:
+                cls._set_experiment_version(current_version_number, Config.core.force)
+            except VersionAlreadyExistsAsDevelopment as err:
+                raise SystemExit(err.message) from None
 
-            version_setter[Config.core.mode](current_version_number, Config.core.force)
-            if Config.core.mode == "production":
-                cls.__check_production_migration_config()
         else:
             raise SystemExit(f"Undefined execution mode: {Config.core.mode}.")
 
-    @classmethod
-    def __check_production_migration_config(cls):
-        from ..config.checkers._migration_config_checker import _MigrationConfigChecker
-
-        collector = _MigrationConfigChecker(Config._applied_config, IssueCollector())._check()
-        for issue in collector._warnings:
-            cls.__logger.warning(str(issue))
-        for issue in collector._infos:
-            cls.__logger.info(str(issue))
-        for issue in collector._errors:
-            cls.__logger.error(str(issue))
-        if len(collector._errors) != 0:
-            raise SystemExit("Configuration errors found. Please check the error log for more information.")
-
     @classmethod
     def _delete_entities_of_multiple_types(cls, _entity_ids):
         raise NotImplementedError

+ 0 - 46
taipy/core/_version/_version_repository_interface.py

@@ -1,46 +0,0 @@
-# Copyright 2021-2024 Avaiga Private Limited
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
-# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations under the License.
-
-from abc import ABC, abstractmethod
-
-
-class _VersionRepositoryInterface(ABC):
-    _LATEST_VERSION_KEY = "latest_version"
-    _DEVELOPMENT_VERSION_KEY = "development_version"
-    _PRODUCTION_VERSION_KEY = "production_version"
-
-    @abstractmethod
-    def _set_latest_version(self, version_number):
-        raise NotImplementedError
-
-    @abstractmethod
-    def _get_latest_version(self):
-        raise NotImplementedError
-
-    @abstractmethod
-    def _set_development_version(self, version_number):
-        raise NotImplementedError
-
-    @abstractmethod
-    def _get_development_version(self):
-        raise NotImplementedError
-
-    @abstractmethod
-    def _set_production_version(self, version_number):
-        raise NotImplementedError
-
-    @abstractmethod
-    def _get_production_versions(self):
-        raise NotImplementedError
-
-    @abstractmethod
-    def _delete_production_version(self, version_number):
-        raise NotImplementedError

+ 0 - 9
taipy/core/config/__init__.py

@@ -25,7 +25,6 @@ from .checkers._task_config_checker import _TaskConfigChecker
 from .core_section import CoreSection
 from .data_node_config import DataNodeConfig
 from .job_config import JobConfig
-from .migration_config import MigrationConfig
 from .scenario_config import ScenarioConfig
 from .task_config import TaskConfig
 
@@ -75,13 +74,6 @@ _inject_section(
         ("set_default_scenario_configuration", ScenarioConfig._set_default_configuration),
     ],
 )
-_inject_section(
-    MigrationConfig,
-    "migration_functions",
-    MigrationConfig.default_config(),
-    [("add_migration_function", MigrationConfig._add_migration_function)],
-    add_to_unconflicted_sections=True,
-)
 _inject_section(
     CoreSection,
     "core",
@@ -94,6 +86,5 @@ _Checker.add_checker(_ConfigIdChecker)
 _Checker.add_checker(_CoreSectionChecker)
 _Checker.add_checker(_DataNodeConfigChecker)
 _Checker.add_checker(_JobConfigChecker)
-# We don't need to add _MigrationConfigChecker because it is run only when the Core service is run.
 _Checker.add_checker(_TaskConfigChecker)
 _Checker.add_checker(_ScenarioConfigChecker)

+ 0 - 66
taipy/core/config/checkers/_migration_config_checker.py

@@ -1,66 +0,0 @@
-# Copyright 2021-2024 Avaiga Private Limited
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
-# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations under the License.
-
-from taipy.config._config import _Config
-from taipy.config.checker._checkers._config_checker import _ConfigChecker
-from taipy.config.checker.issue_collector import IssueCollector
-
-from ..._version._version_manager_factory import _VersionManagerFactory
-from ..migration_config import MigrationConfig
-
-
-class _MigrationConfigChecker(_ConfigChecker):
-    def __init__(self, config: _Config, collector: IssueCollector):
-        super().__init__(config, collector)
-
-    def _check(self) -> IssueCollector:
-        if migration_config := self._config._unique_sections.get(MigrationConfig.name):
-            self._check_if_entity_property_key_used_is_predefined(migration_config)
-
-            migration_fcts = migration_config.migration_fcts
-
-            for target_version, migration_functions in migration_config.migration_fcts.items():  # type: ignore[union-attr]
-                for config_id, migration_function in migration_functions.items():
-                    self._check_callable(target_version, config_id, migration_function)
-
-            self._check_valid_production_version(migration_fcts)
-            self._check_migration_from_productions_to_productions_exist(migration_fcts)
-
-        return self._collector
-
-    def _check_callable(self, target_version, config_id, migration_function):
-        if not callable(migration_function):
-            self._error(
-                MigrationConfig._MIGRATION_FCTS_KEY,
-                migration_function,
-                f"The migration function of config `{config_id}` from version {target_version}"
-                f" must be populated with Callable value.",
-            )
-
-    def _check_valid_production_version(self, migration_fcts):
-        for target_version in migration_fcts.keys():
-            if target_version not in _VersionManagerFactory._build_manager()._get_production_versions():
-                self._error(
-                    MigrationConfig._MIGRATION_FCTS_KEY,
-                    target_version,
-                    "The target version for a migration function must be a production version.",
-                )
-
-    def _check_migration_from_productions_to_productions_exist(self, migration_fcts):
-        production_versions = _VersionManagerFactory._build_manager()._get_production_versions()
-        for source_version, target_version in zip(production_versions[:-1], production_versions[1:]):
-            if not migration_fcts.get(target_version):
-                self._info(
-                    "target_version",
-                    None,
-                    f'There is no migration function from production version "{source_version}"'
-                    f' to version "{target_version}".',
-                )

+ 6 - 7
taipy/core/config/core_section.py

@@ -39,8 +39,8 @@ class CoreSection(UniqueSection):
         read_entity_retry (int): Number of retries to read an entity from the repository before return failure.
             The default value is 3.
         mode (str): The Taipy operating mode. By default, the `Core^` service runs in "development" mode.
-            An "experiment" and a "production" mode are also available. Please refer to the
-            [Versioning management](../../userman/versioning/index.md) documentation page for more details.
+            Please refer to the [Versioning management](../../userman/versioning/index.md)
+            documentation page for more details.
         version_number (str)): The identifier of the user application version. Please refer to the
             [Versioning management](../../userman/versioning/index.md) documentation page for more details.
         force (bool): If True, force the application run even if there are some conflicts in the
@@ -69,10 +69,7 @@ class CoreSection(UniqueSection):
     _DEFAULT_READ_ENTITY_RETRY = 1
 
     _MODE_KEY = "mode"
-    _DEVELOPMENT_MODE = "development"
-    _EXPERIMENT_MODE = "experiment"
-    _PRODUCTION_MODE = "production"
-    _DEFAULT_MODE = _DEVELOPMENT_MODE
+    _DEFAULT_MODE = "development"
 
     _VERSION_NUMBER_KEY = "version_number"
     _DEFAULT_VERSION_NUMBER = ""
@@ -366,7 +363,9 @@ class CoreSection(UniqueSection):
             read_entity_retry (Optional[int]): Number of retries to read an entity from the repository
                 before return failure. The default value is 3.
             mode (Optional[str]): Indicates the mode of the version management system.
-                Possible values are *"development"*, *"experiment"*, or *"production"*.
+                Possible values are *"development"* or *"experiment"*. On Enterprise edition of Taipy,
+                *production* mode is also available. Please refer to the
+                [Versioning management](../../userman/versioning/index.md) documentation page for more details.
             version_number (Optional[str]): The string identifier of the version.
                  In development mode, the version number is ignored.
             force (Optional[bool]): If True, Taipy will override a version even if the configuration

+ 0 - 113
taipy/core/config/migration_config.py

@@ -1,113 +0,0 @@
-# Copyright 2021-2024 Avaiga Private Limited
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
-# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations under the License.
-
-import collections.abc
-from copy import deepcopy
-from typing import Any, Callable, Dict, Optional, Union
-
-from taipy.config._config import _Config
-from taipy.config.common._template_handler import _TemplateHandler as _tpl
-from taipy.config.config import Config
-from taipy.config.section import Section
-from taipy.config.unique_section import UniqueSection
-
-
-class MigrationConfig(UniqueSection):
-    """
-    Configuration fields needed to register migration functions from an old version to newer one.
-
-    Attributes:
-        migration_fcts (Dict[str, Dict[str, Callable]]): A dictionary that maps the version that entities are
-            migrated from to the migration functions.
-        **properties (dict[str, Any]): A dictionary of additional properties.
-    """
-
-    name = "VERSION_MIGRATION"
-
-    _MIGRATION_FCTS_KEY = "migration_fcts"
-
-    def __init__(
-        self,
-        migration_fcts: Dict[str, Dict[str, Callable]],
-        **properties,
-    ):
-        self.migration_fcts = migration_fcts
-
-        super().__init__(**properties)
-
-    def __copy__(self):
-        return MigrationConfig(
-            deepcopy(self.migration_fcts),
-            **deepcopy(self._properties),
-        )
-
-    def _clean(self):
-        self.migration_fcts.clear()
-        self._properties.clear()
-
-    def __getattr__(self, item: str) -> Optional[Any]:
-        return _tpl._replace_templates(self._properties.get(item))  # type: ignore
-
-    @classmethod
-    def default_config(cls):
-        return MigrationConfig({})
-
-    def _to_dict(self):
-        return {
-            self._MIGRATION_FCTS_KEY: self.migration_fcts,
-            **self._properties,
-        }
-
-    @classmethod
-    def _from_dict(cls, as_dict: Dict[str, Any], id: str, config: Optional[_Config]):
-        return MigrationConfig(**as_dict)
-
-    def _update(self, as_dict, default_section=None):
-        def deep_update(d, u):
-            for k, v in u.items():
-                if isinstance(v, collections.abc.Mapping):
-                    d[k] = deep_update(d.get(k, {}), v)
-                else:
-                    d[k] = v
-            return d
-
-        migration_fcts = as_dict.pop(self._MIGRATION_FCTS_KEY)
-        deep_update(self.migration_fcts, migration_fcts)
-
-        self._properties.update(as_dict)
-
-    @staticmethod
-    def _add_migration_function(
-        target_version: str,
-        config: Union[Section, str],
-        migration_fct: Callable,
-        **properties,
-    ):
-        """Add a migration function for a Configuration to migrate entities to the target version.
-
-        Parameters:
-            target_version (str): The production version that entities are migrated to.
-            config (Union[Section, str]): The configuration or the `id` of the config that needs to migrate.
-            migration_fct (Callable): Migration function that takes an entity as input and returns a new entity
-                that is compatible with the target production version.
-            **properties (Dict[str, Any]): A keyworded variable length list of additional arguments.
-        Returns:
-            `MigrationConfig^`: The Migration configuration.
-        """
-        config_id = config if isinstance(config, str) else config.id
-        migration_fcts = {target_version: {config_id: migration_fct}}
-
-        section = MigrationConfig(
-            migration_fcts,
-            **properties,
-        )
-        Config._register(section)
-        return Config.unique_sections[MigrationConfig.name]

+ 1 - 3
taipy/core/data/_data_converter.py

@@ -14,7 +14,6 @@ from datetime import datetime, timedelta
 from pydoc import locate
 
 from .._repository._abstract_converter import _AbstractConverter
-from .._version._utils import _migrate_entity
 from ..common._utils import _load_fct
 from ..data._data_model import _DataNodeModel
 from ..data.data_node import DataNode
@@ -299,7 +298,7 @@ class _DataNodeConverter(_AbstractConverter):
             validity_period = timedelta(days=model.validity_days, seconds=model.validity_seconds)
 
         exp_date = datetime.fromisoformat(model.editor_expiration_date) if model.editor_expiration_date else None
-        datanode = DataNode._class_map()[model.storage_type](
+        return DataNode._class_map()[model.storage_type](
             config_id=model.config_id,
             scope=model.scope,
             id=model.id,
@@ -314,4 +313,3 @@ class _DataNodeConverter(_AbstractConverter):
             editor_expiration_date=exp_date,
             properties=data_node_properties,
         )
-        return _migrate_entity(datanode)

+ 6 - 2
taipy/core/exceptions/exceptions.py

@@ -346,8 +346,12 @@ class NonExistingVersion(Exception):
         self.message = f"Version '{version_number}' does not exist."
 
 
-class VersionIsNotProductionVersion(Exception):
-    """Raised if the version is not a production version."""
+class VersionAlreadyExistsAsDevelopment(Exception):
+    """Raised if a version already exists as the development version."""
+
+    def __init__(self, version_number: str):
+        self.message = f"Version number {version_number} already exists as the development version."
+        self.message += " Please choose a different name for this experiment."
 
 
 class ConflictedConfigurationError(Exception):

+ 1 - 3
taipy/core/scenario/_scenario_converter.py

@@ -13,7 +13,6 @@ from datetime import datetime
 from typing import Dict, List, Optional, Set, Union
 
 from .._repository._abstract_converter import _AbstractConverter
-from .._version._utils import _migrate_entity
 from ..common import _utils
 from ..cycle._cycle_manager_factory import _CycleManagerFactory
 from ..cycle.cycle import Cycle, CycleId
@@ -67,7 +66,7 @@ class _ScenarioConverter(_AbstractConverter):
                         for it in subscribers
                     ]
 
-        scenario = Scenario(
+        return Scenario(
             scenario_id=model.id,
             config_id=model.config_id,
             tasks=tasks,
@@ -84,7 +83,6 @@ class _ScenarioConverter(_AbstractConverter):
             version=model.version,
             sequences=model.sequences,
         )
-        return _migrate_entity(scenario)
 
     @staticmethod
     def __to_cycle(cycle_id: Optional[CycleId] = None) -> Optional[Cycle]:

+ 7 - 24
taipy/core/taipy.py

@@ -36,12 +36,7 @@ from .cycle.cycle_id import CycleId
 from .data._data_manager_factory import _DataManagerFactory
 from .data.data_node import DataNode
 from .data.data_node_id import DataNodeId
-from .exceptions.exceptions import (
-    DataNodeConfigIsNotGlobal,
-    ModelNotFound,
-    NonExistingVersion,
-    VersionIsNotProductionVersion,
-)
+from .exceptions.exceptions import DataNodeConfigIsNotGlobal, ModelNotFound, NonExistingVersion
 from .job._job_manager_factory import _JobManagerFactory
 from .job.job import Job
 from .job.job_id import JobId
@@ -967,19 +962,14 @@ def clean_all_entities_by_version(version_number=None) -> bool:
 
 def clean_all_entities(version_number: str) -> bool:
     """Deletes all entities associated with the specified version.
+    This function cleans all entities, including jobs, submissions, scenarios, cycles, sequences, tasks, and data nodes.
 
     Parameters:
-        version_number (str): The version number of the entities to be deleted.
-            The version_number should not be a production version.
+        version_number (str): The version number of the entities to be deleted.<br/>
+            - If the specified version does not exist, the operation will be aborted, and False will be returned.
 
     Returns:
         True if the operation succeeded, False otherwise.
-
-    Notes:
-        - If the specified version does not exist, the operation will be aborted, and False will be returned.
-        - If the specified version is a production version, the operation will be aborted, and False will be returned.
-        - This function cleans all entities, including jobs, submissions, scenarios, cycles, sequences, tasks,
-            and data nodes.
     """
     version_manager = _VersionManagerFactory._build_manager()
     try:
@@ -988,10 +978,9 @@ def clean_all_entities(version_number: str) -> bool:
         __logger.warning(f"{e.message} Abort cleaning the entities of version '{version_number}'.")
         return False
 
-    if version_number in version_manager._get_production_versions():
-        __logger.warning(
-            f"Abort cleaning the entities of version '{version_number}'. A production version can not be deleted."
-        )
+    if not version_manager._is_deletable(version_number):
+        reason_collection = version_manager._is_deletable(version_number)
+        __logger.warning(f"Abort cleaning the entities of version '{version_number}'. {reason_collection.reasons}.")
         return False
 
     _JobManagerFactory._build_manager()._delete_by_version(version_number)
@@ -1000,14 +989,8 @@ def clean_all_entities(version_number: str) -> bool:
     _SequenceManagerFactory._build_manager()._delete_by_version(version_number)
     _TaskManagerFactory._build_manager()._delete_by_version(version_number)
     _DataManagerFactory._build_manager()._delete_by_version(version_number)
-
     version_manager._delete(version_number)
 
-    try:
-        version_manager._delete_production_version(version_number)
-    except VersionIsNotProductionVersion:
-        pass
-
     return True
 
 

+ 1 - 4
taipy/core/task/_task_converter.py

@@ -9,9 +9,7 @@
 # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 # specific language governing permissions and limitations under the License.
 
-
 from .._repository._abstract_converter import _AbstractConverter
-from .._version._utils import _migrate_entity
 from ..common._utils import _load_fct
 from ..data._data_manager_factory import _DataManagerFactory
 from ..exceptions import NonExistingDataNode
@@ -39,7 +37,7 @@ class _TaskConverter(_AbstractConverter):
 
     @classmethod
     def _model_to_entity(cls, model: _TaskModel) -> Task:
-        task = Task(
+        return Task(
             id=TaskId(model.id),
             owner_id=model.owner_id,
             parent_ids=set(model.parent_ids),
@@ -51,7 +49,6 @@ class _TaskConverter(_AbstractConverter):
             skippable=model.skippable,
             properties=model.properties,
         )
-        return _migrate_entity(task)
 
     @staticmethod
     def __to_ids(data_nodes):

+ 1 - 8
tests/conftest.py

@@ -21,7 +21,7 @@ from taipy.config._serializer._toml_serializer import _TomlSerializer
 from taipy.config.checker._checker import _Checker
 from taipy.config.checker.issue_collector import IssueCollector
 from taipy.config.config import Config
-from taipy.core.config import CoreSection, DataNodeConfig, JobConfig, MigrationConfig, ScenarioConfig, TaskConfig
+from taipy.core.config import CoreSection, DataNodeConfig, JobConfig, ScenarioConfig, TaskConfig
 
 
 def pytest_addoption(parser):
@@ -137,12 +137,5 @@ def inject_core_sections():
                 ("set_default_scenario_configuration", ScenarioConfig._set_default_configuration),
             ],
         )
-        _inject_section(
-            MigrationConfig,
-            "migration_functions",
-            MigrationConfig.default_config(),
-            [("add_migration_function", MigrationConfig._add_migration_function)],
-            True,
-        )
 
     return _inject_core_sections

+ 10 - 0
tests/core/_version/__init__.py

@@ -0,0 +1,10 @@
+# Copyright 2021-2024 Avaiga Private Limited
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.

+ 0 - 0
tests/core/version/dataset_2.0/cycles/CYCLE_Frequency.DAILY_2023-01-18T172525.892619_8956558e-d108-4ac4-8684-60f3e5ef9e8f.json → tests/core/_version/dataset_2.0/cycles/CYCLE_Frequency.DAILY_2023-01-18T172525.892619_8956558e-d108-4ac4-8684-60f3e5ef9e8f.json


+ 0 - 0
tests/core/version/dataset_2.0/data_nodes/DATANODE_d1_3d65c33b-b188-4402-8d2c-3b26d98b9a9e.json → tests/core/_version/dataset_2.0/data_nodes/DATANODE_d1_3d65c33b-b188-4402-8d2c-3b26d98b9a9e.json


+ 0 - 0
tests/core/version/dataset_2.0/data_nodes/DATANODE_d2_bd8ee43e-6fa9-4832-b2e7-c0c9516b1e1c.json → tests/core/_version/dataset_2.0/data_nodes/DATANODE_d2_bd8ee43e-6fa9-4832-b2e7-c0c9516b1e1c.json


+ 0 - 0
tests/core/version/dataset_2.0/scenarios/SCENARIO_my_scenario_c4307ae8-d2ce-4802-8b16-8307baa7cff1.json → tests/core/_version/dataset_2.0/scenarios/SCENARIO_my_scenario_c4307ae8-d2ce-4802-8b16-8307baa7cff1.json


+ 0 - 0
tests/core/version/dataset_2.0/sequences/SEQUENCE_my_sequence_1b97a539-ca51-4eb8-aa64-c232269618c6.json → tests/core/_version/dataset_2.0/sequences/SEQUENCE_my_sequence_1b97a539-ca51-4eb8-aa64-c232269618c6.json


+ 0 - 0
tests/core/version/dataset_2.0/tasks/TASK_my_task_53cf9993-047f-4220-9c03-e28fa250f6b3.json → tests/core/_version/dataset_2.0/tasks/TASK_my_task_53cf9993-047f-4220-9c03-e28fa250f6b3.json


+ 0 - 0
tests/core/version/test_version.py → tests/core/_version/test_version.py


+ 23 - 99
tests/core/version/test_version_cli.py → tests/core/_version/test_version_cli.py

@@ -19,7 +19,7 @@ from taipy.config.common.frequency import Frequency
 from taipy.config.common.scope import Scope
 from taipy.config.config import Config
 from taipy.core import Core
-from taipy.core._version._cli._version_cli import _VersionCLI
+from taipy.core._version._cli._version_cli_factory import _VersionCLIFactory
 from taipy.core._version._version_manager import _VersionManager
 from taipy.core.data._data_manager import _DataManager
 from taipy.core.job._job_manager import _JobManager
@@ -59,11 +59,6 @@ def test_delete_version(caplog):
         _ScenarioManager._submit(scenario)
         core.stop()
 
-    with patch("sys.argv", ["prog", "--production", "1.1"]):
-        core = Core()
-        core.run()
-        core.stop()
-
     with patch("sys.argv", ["prog", "--experiment", "2.0"]):
         core = Core()
         core.run()
@@ -71,61 +66,28 @@ def test_delete_version(caplog):
         _ScenarioManager._submit(scenario)
         core.stop()
 
-    with patch("sys.argv", ["prog", "--experiment", "2.1"]):
-        core = Core()
-        core.run()
-        scenario = _ScenarioManager._create(scenario_config)
-        _ScenarioManager._submit(scenario)
-        core.stop()
-
-    with patch("sys.argv", ["prog", "--production", "2.1"]):
-        core = Core()
-        core.run()
-        core.stop()
-
     all_versions = [version.id for version in _VersionManager._get_all()]
-    production_version = _VersionManager._get_production_versions()
-    assert len(all_versions) == 5
-    assert len(production_version) == 2
+    assert len(all_versions) == 4
     assert "1.0" in all_versions
-    assert "1.1" in all_versions and "1.1" in production_version
+    assert "1.1" in all_versions
     assert "2.0" in all_versions
-    assert "2.1" in all_versions and "2.1" in production_version
 
-    _VersionCLI.create_parser()
+    _VersionCLIFactory._build_cli().create_parser()
     with pytest.raises(SystemExit):
         with patch("sys.argv", ["prog", "manage-versions", "--delete", "1.0"]):
-            _VersionCLI.handle_command()
+            _VersionCLIFactory._build_cli().handle_command()
 
     assert "Successfully delete version 1.0." in caplog.text
     all_versions = [version.id for version in _VersionManager._get_all()]
-    assert len(all_versions) == 4
+    assert len(all_versions) == 3
     assert "1.0" not in all_versions
 
     # Test delete a non-existed version
     with pytest.raises(SystemExit):
         with patch("sys.argv", ["prog", "manage-versions", "--delete", "non_exist_version"]):
-            _VersionCLI.handle_command()
+            _VersionCLIFactory._build_cli().handle_command()
     assert "Version 'non_exist_version' does not exist." in caplog.text
 
-    # Test delete production version will change the version from production to experiment
-    with pytest.raises(SystemExit):
-        with patch("sys.argv", ["prog", "manage-versions", "--delete-production", "1.1"]):
-            _VersionCLI.handle_command()
-
-    assert "Successfully delete version 1.1 from the production version list." in caplog.text
-    all_versions = [version.id for version in _VersionManager._get_all()]
-    production_version = _VersionManager._get_production_versions()
-    assert len(all_versions) == 4
-    assert "1.1" in all_versions and "1.1" not in production_version
-
-    # Test delete a non-existed production version
-    with pytest.raises(SystemExit) as e:
-        with patch("sys.argv", ["prog", "manage-versions", "--delete-production", "non_exist_version"]):
-            _VersionCLI.handle_command()
-
-    assert str(e.value) == "Version 'non_exist_version' is not a production version."
-
 
 def test_list_versions(capsys):
     with patch("sys.argv", ["prog", "--development"]):
@@ -138,45 +100,23 @@ def test_list_versions(capsys):
         core.run()
         core.stop()
     sleep(0.05)
-    with patch("sys.argv", ["prog", "--experiment", "1.1"]):
-        core = Core()
-        core.run()
-        core.stop()
-    sleep(0.05)
-    with patch("sys.argv", ["prog", "--production", "1.1"]):
-        core = Core()
-        core.run()
-        core.stop()
-    sleep(0.05)
     with patch("sys.argv", ["prog", "--experiment", "2.0"]):
         core = Core()
         core.run()
         core.stop()
-    sleep(0.05)
-    with patch("sys.argv", ["prog", "--experiment", "2.1"]):
-        core = Core()
-        core.run()
-        core.stop()
-    sleep(0.05)
-    with patch("sys.argv", ["prog", "--production", "2.1"]):
-        core = Core()
-        core.run()
-        core.stop()
 
-    _VersionCLI.create_parser()
+    _VersionCLIFactory._build_cli().create_parser()
     with pytest.raises(SystemExit):
         with patch("sys.argv", ["prog", "manage-versions", "--list"]):
-            _VersionCLI.handle_command()
+            _VersionCLIFactory._build_cli().handle_command()
 
     out, _ = capsys.readouterr()
     version_list = str(out).strip().split("\n")
-    assert len(version_list) == 6  # 5 versions with the header
+    assert len(version_list) == 4  # 3 versions with the header
     assert all(column in version_list[0] for column in ["Version number", "Mode", "Creation date"])
-    assert all(column in version_list[1] for column in ["2.1", "Production", "latest"])
-    assert all(column in version_list[2] for column in ["2.0", "Experiment"]) and "latest" not in version_list[2]
-    assert all(column in version_list[3] for column in ["1.1", "Production"]) and "latest" not in version_list[3]
-    assert all(column in version_list[4] for column in ["1.0", "Experiment"]) and "latest" not in version_list[4]
-    assert "Development" in version_list[5] and "latest" not in version_list[5]
+    assert all(column in version_list[1] for column in ["2.0", "Experiment", "latest"])
+    assert all(column in version_list[2] for column in ["1.0", "Experiment"]) and "latest" not in version_list[2]
+    assert "Development" in version_list[3] and "latest" not in version_list[3]
 
 
 def test_rename_version(caplog):
@@ -189,7 +129,7 @@ def test_rename_version(caplog):
         _ScenarioManager._submit(scenario)
         core.stop()
 
-    with patch("sys.argv", ["prog", "--production", "2.0"]):
+    with patch("sys.argv", ["prog", "--experiment", "2.0"]):
         core = Core()
         core.run()
         scenario = _ScenarioManager._create(scenario_config)
@@ -198,24 +138,22 @@ def test_rename_version(caplog):
 
     dev_ver = _VersionManager._get_development_version()
 
-    _VersionCLI.create_parser()
+    _VersionCLIFactory._build_cli().create_parser()
     with pytest.raises(SystemExit):
         with patch("sys.argv", ["prog", "manage-versions", "--rename", "non_exist_version", "1.1"]):
             # This should raise an exception since version "non_exist_version" does not exist
-            _VersionCLI.handle_command()
+            _VersionCLIFactory._build_cli().handle_command()
     assert "Version 'non_exist_version' does not exist." in caplog.text
 
-    _VersionCLI.create_parser()
     with pytest.raises(SystemExit):
         with patch("sys.argv", ["prog", "manage-versions", "--rename", "1.0", "2.0"]):
             # This should raise an exception since 2.0 already exists
-            _VersionCLI.handle_command()
+            _VersionCLIFactory._build_cli().handle_command()
     assert "Version name '2.0' is already used." in caplog.text
 
-    _VersionCLI.create_parser()
     with pytest.raises(SystemExit):
         with patch("sys.argv", ["prog", "manage-versions", "--rename", "1.0", "1.1"]):
-            _VersionCLI.handle_command()
+            _VersionCLIFactory._build_cli().handle_command()
     assert _VersionManager._get("1.0") is None
     assert [version.id for version in _VersionManager._get_all()].sort() == [dev_ver, "1.1", "2.0"].sort()
     # All entities are assigned to the new version
@@ -225,20 +163,6 @@ def test_rename_version(caplog):
     assert len(_ScenarioManager._get_all("1.1")) == 1
     assert len(_JobManager._get_all("1.1")) == 1
 
-    _VersionCLI.create_parser()
-    with pytest.raises(SystemExit):
-        with patch("sys.argv", ["prog", "manage-versions", "--rename", "2.0", "2.1"]):
-            _VersionCLI.handle_command()
-    assert _VersionManager._get("2.0") is None
-    assert [version.id for version in _VersionManager._get_all()].sort() == [dev_ver, "1.1", "2.1"].sort()
-    assert _VersionManager._get_production_versions() == ["2.1"]
-    # All entities are assigned to the new version
-    assert len(_DataManager._get_all("2.1")) == 2
-    assert len(_TaskManager._get_all("2.1")) == 1
-    assert len(_SequenceManager._get_all("2.1")) == 1
-    assert len(_ScenarioManager._get_all("2.1")) == 1
-    assert len(_JobManager._get_all("2.1")) == 1
-
 
 def test_compare_version_config(caplog, init_config):
     scenario_config_1 = config_scenario()
@@ -262,27 +186,27 @@ def test_compare_version_config(caplog, init_config):
         _ScenarioManager._submit(scenario)
         core.stop()
 
-    _VersionCLI.create_parser()
+    _VersionCLIFactory._build_cli().create_parser()
     with pytest.raises(SystemExit):
         with patch("sys.argv", ["prog", "manage-versions", "--compare-config", "non_exist_version", "2.0"]):
             # This should raise an exception since version "non_exist_version" does not exist
-            _VersionCLI.handle_command()
+            _VersionCLIFactory._build_cli().handle_command()
     assert "Version 'non_exist_version' does not exist." in caplog.text
 
     with pytest.raises(SystemExit):
         with patch("sys.argv", ["prog", "manage-versions", "--compare-config", "1.0", "non_exist_version"]):
             # This should raise an exception since 2.0 already exists
-            _VersionCLI.handle_command()
+            _VersionCLIFactory._build_cli().handle_command()
     assert "Version 'non_exist_version' does not exist." in caplog.text
 
     with pytest.raises(SystemExit):
         with patch("sys.argv", ["prog", "manage-versions", "--compare-config", "1.0", "1.0"]):
-            _VersionCLI.handle_command()
+            _VersionCLIFactory._build_cli().handle_command()
     assert "There is no difference between version 1.0 Configuration and version 1.0 Configuration." in caplog.text
 
     with pytest.raises(SystemExit):
         with patch("sys.argv", ["prog", "manage-versions", "--compare-config", "1.0", "2.0"]):
-            _VersionCLI.handle_command()
+            _VersionCLIFactory._build_cli().handle_command()
     expected_message = """Differences between version 1.0 Configuration and version 2.0 Configuration:
 \tDATA_NODE "d2" has attribute "default_path" modified: foo.csv -> bar.csv"""
     assert expected_message in caplog.text

+ 0 - 0
tests/core/version/test_version_manager.py → tests/core/_version/test_version_manager.py


+ 0 - 0
tests/core/version/test_version_repositories.py → tests/core/_version/test_version_repositories.py


+ 0 - 132
tests/core/config/checkers/test_migration_config_checker.py

@@ -1,132 +0,0 @@
-# Copyright 2021-2024 Avaiga Private Limited
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
-# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations under the License.
-
-from unittest.mock import patch
-
-import pytest
-
-from taipy.config.config import Config
-from taipy.core import Core
-from taipy.core._version._version_manager import _VersionManager
-from taipy.core.config import MigrationConfig
-
-
-def mock_func():
-    pass
-
-
-def test_check_if_entity_property_key_used_is_predefined(caplog):
-    Config.unique_sections[MigrationConfig.name]._properties["_entity_owner"] = None
-    with patch("sys.argv", ["prog", "--production", "1.0"]):
-        with pytest.raises(SystemExit):
-            core = Core()
-            core.run()
-        core.stop()
-    assert (
-        "Properties of MigrationConfig `VERSION_MIGRATION` cannot have `_entity_owner` as its property." in caplog.text
-    )
-
-    caplog.clear()
-
-    Config.unique_sections[MigrationConfig.name]._properties["_entity_owner"] = "entity_owner"
-    with patch("sys.argv", ["prog", "--production", "1.0"]):
-        with pytest.raises(SystemExit):
-            core = Core()
-            core.run()
-        core.stop()
-    expected_error_message = (
-        "Properties of MigrationConfig `VERSION_MIGRATION` cannot have `_entity_owner` as its property."
-        ' Current value of property `_entity_owner` is "entity_owner".'
-    )
-    assert expected_error_message in caplog.text
-
-
-def test_check_valid_version(caplog):
-    data_nodes1 = Config.configure_data_node("data_nodes1", "pickle")
-
-    Config.add_migration_function("2.0", data_nodes1, mock_func)
-    with patch("sys.argv", ["prog", "--production", "1.0"]):
-        with pytest.raises(SystemExit):
-            core = Core()
-            core.run()
-        core.stop()
-    assert "The target version for a migration function must be a production version." in caplog.text
-
-    caplog.clear()
-    Config.unblock_update()
-
-    with patch("sys.argv", ["prog", "--production", "2.0"]):
-        # No SystemExit should be raised
-        core = Core()
-        core.run()
-    core.stop()
-
-
-def test_check_callable_function(caplog):
-    data_nodes1 = Config.configure_data_node("data_nodes1", "pickle")
-    Config.add_migration_function("1.0", data_nodes1, 1)
-    with patch("sys.argv", ["prog", "--production", "1.0"]):
-        with pytest.raises(SystemExit):
-            core = Core()
-            core.run()
-        core.stop()
-    expected_error_message = (
-        "The migration function of config `data_nodes1` from version 1.0 must be populated with"
-        " Callable value. Current value of property `migration_fcts` is 1."
-    )
-    assert expected_error_message in caplog.text
-
-    caplog.clear()
-    Config.unblock_update()
-
-    Config.add_migration_function("1.0", data_nodes1, "bar")
-    with patch("sys.argv", ["prog", "--production", "1.0"]):
-        with pytest.raises(SystemExit):
-            core = Core()
-            core.run()
-        core.stop()
-    expected_error_message = (
-        "The migration function of config `data_nodes1` from version 1.0 must be populated with"
-        ' Callable value. Current value of property `migration_fcts` is "bar".'
-    )
-    assert expected_error_message in caplog.text
-
-    caplog.clear()
-    Config.unblock_update()
-
-    Config.add_migration_function("1.0", data_nodes1, mock_func)
-    with patch("sys.argv", ["prog", "--production", "1.0"]):
-        core = Core()
-        core.run()
-        core.stop()
-
-
-def test_check_migration_from_productions_to_productions_exist(caplog):
-    _VersionManager._set_production_version("1.0", True)
-    _VersionManager._set_production_version("1.1", True)
-    _VersionManager._set_production_version("1.2", True)
-
-    with patch("sys.argv", ["prog", "--production", "1.0"]):
-        core = Core()
-        core.run()
-        core.stop()
-    assert 'There is no migration function from production version "1.0" to version "1.1".' in caplog.text
-    assert 'There is no migration function from production version "1.1" to version "1.2".' in caplog.text
-
-    caplog.clear()
-    Config.unblock_update()
-
-    Config.add_migration_function("1.2", "data_nodes1", mock_func)
-    with patch("sys.argv", ["prog", "--production", "1.0"]):
-        core = Core()
-        core.run()
-        core.stop()
-    assert 'There is no migration function from production version "1.0" to version "1.1".' in caplog.text

+ 5 - 35
tests/core/config/test_config_serialization.py

@@ -16,7 +16,7 @@ from taipy.config import Config
 from taipy.config._serializer._json_serializer import _JsonSerializer
 from taipy.config.common.frequency import Frequency
 from taipy.config.common.scope import Scope
-from taipy.core.config import CoreSection, DataNodeConfig, JobConfig, MigrationConfig, ScenarioConfig, TaskConfig
+from taipy.core.config import CoreSection, DataNodeConfig, JobConfig, ScenarioConfig, TaskConfig
 from tests.core.utils.named_temporary_file import NamedTemporaryFile
 
 
@@ -102,8 +102,6 @@ def config_test_scenario():
     )
     test_scenario_cfg.add_sequences({"sequence1": [test_task_cfg]})
 
-    Config.add_migration_function("1.0", test_csv_dn_cfg, migrate_csv_path)
-
     return test_scenario_cfg
 
 
@@ -172,9 +170,6 @@ tasks = [ "test_task:SECTION",]
 additional_data_nodes = [ "test_pickle_dn:SECTION",]
 frequency = "DAILY:FREQUENCY"
 
-[VERSION_MIGRATION.migration_fcts."1.0"]
-test_csv_dn = "tests.core.config.test_config_serialization.migrate_csv_path:function"
-
 [SCENARIO.default.comparators]
 
 [SCENARIO.default.sequences]
@@ -201,13 +196,11 @@ sequence1 = [ "test_task:SECTION",]
     assert actual_config_2 == expected_toml_config
 
     assert Config.unique_sections is not None
-    assert len(Config.unique_sections) == 3
+    assert len(Config.unique_sections) == 2
 
     assert Config.unique_sections[JobConfig.name].mode == "development"
     assert Config.unique_sections[JobConfig.name].max_nb_of_workers is None
 
-    assert Config.unique_sections[MigrationConfig.name].migration_fcts["1.0"] == {"test_csv_dn": migrate_csv_path}
-
     assert Config.sections is not None
     assert len(Config.sections) == 3
 
@@ -300,13 +293,6 @@ def test_read_write_json_configuration_file():
 """
         + """
 },
-"VERSION_MIGRATION": {
-"migration_fcts": {
-"1.0": {
-"test_csv_dn": "tests.core.config.test_config_serialization.migrate_csv_path:function"
-}
-}
-},
 "DATA_NODE": {
 "default": {
 "storage_type": "pickle",
@@ -402,13 +388,11 @@ def test_read_write_json_configuration_file():
     assert actual_config_2 == expected_json_config
 
     assert Config.unique_sections is not None
-    assert len(Config.unique_sections) == 3
+    assert len(Config.unique_sections) == 2
 
     assert Config.unique_sections[JobConfig.name].mode == "development"
     assert Config.unique_sections[JobConfig.name].max_nb_of_workers is None
 
-    assert Config.unique_sections[MigrationConfig.name].migration_fcts["1.0"] == {"test_csv_dn": migrate_csv_path}
-
     assert Config.sections is not None
     assert len(Config.sections) == 3
 
@@ -531,9 +515,6 @@ tasks = [ "test_task:SECTION",]
 sequences.test_sequence = [ "test_task:SECTION",]
 frequency = "DAILY:FREQUENCY"
 
-[VERSION_MIGRATION.migration_fcts."1.0"]
-test_csv_dn = "tests.core.config.test_config_serialization.migrate_csv_path:function"
-
 [SCENARIO.default.comparators]
 
 [SCENARIO.test_scenario.comparators]
@@ -548,7 +529,7 @@ test_json_dn = [ "tests.core.config.test_config_serialization.compare_function:f
     Config.restore(tf.filename)
 
     assert Config.unique_sections is not None
-    assert len(Config.unique_sections) == 3
+    assert len(Config.unique_sections) == 2
 
     assert Config.unique_sections[CoreSection.name].root_folder == "./taipy/"
     assert Config.unique_sections[CoreSection.name].storage_folder == ".data/"
@@ -561,8 +542,6 @@ test_json_dn = [ "tests.core.config.test_config_serialization.compare_function:f
     assert Config.unique_sections[JobConfig.name].mode == "development"
     assert Config.unique_sections[JobConfig.name].max_nb_of_workers == 1
 
-    assert Config.unique_sections[MigrationConfig.name].migration_fcts["1.0"] == {"test_csv_dn": migrate_csv_path}
-
     assert Config.sections is not None
     assert len(Config.sections) == 3
 
@@ -643,13 +622,6 @@ def test_read_write_json_configuration_file_migrate_sequence_in_scenario():
 "version_number": "",
 "force": "False:bool"
 },
-"VERSION_MIGRATION": {
-"migration_fcts": {
-"1.0": {
-"test_csv_dn": "tests.core.config.test_config_serialization.migrate_csv_path:function"
-}
-}
-},
 "DATA_NODE": {
 "default": {
 "storage_type": "pickle",
@@ -724,7 +696,7 @@ def test_read_write_json_configuration_file_migrate_sequence_in_scenario():
     Config.restore(tf.filename)
 
     assert Config.unique_sections is not None
-    assert len(Config.unique_sections) == 3
+    assert len(Config.unique_sections) == 2
 
     assert Config.unique_sections[CoreSection.name].root_folder == "./taipy/"
     assert Config.unique_sections[CoreSection.name].storage_folder == ".data/"
@@ -737,8 +709,6 @@ def test_read_write_json_configuration_file_migrate_sequence_in_scenario():
     assert Config.unique_sections[JobConfig.name].mode == "development"
     assert Config.unique_sections[JobConfig.name].max_nb_of_workers == 1
 
-    assert Config.unique_sections[MigrationConfig.name].migration_fcts["1.0"] == {"test_csv_dn": migrate_csv_path}
-
     assert Config.sections is not None
     assert len(Config.sections) == 3
 

+ 2 - 2
tests/core/config/test_core_section.py

@@ -45,7 +45,7 @@ def test_core_section():
 [TAIPY]
 
 [CORE]
-mode = "production"
+mode = "experiment"
 version_number = "test_num_2"
 force = "true:bool"
         """
@@ -54,7 +54,7 @@ force = "true:bool"
     with patch("sys.argv", ["prog"]):
         core = Core()
         core.run()
-    assert Config.core.mode == "production"
+    assert Config.core.mode == "experiment"
     assert Config.core.version_number == "test_num_2"
     assert Config.core.force
     core.stop()

+ 0 - 5
tests/core/config/test_core_version.py

@@ -81,8 +81,6 @@ class TestCoreVersionInCoreSectionConfig:
             version_number = ""
             force = "False:bool"
             core_version = "{core_version}"
-
-            [VERSION_MIGRATION.migration_fcts]
             """
         )
         if is_compatible:
@@ -111,8 +109,6 @@ class TestCoreVersionInCoreSectionConfig:
             version_number = ""
             force = "False:bool"
             core_version = "{core_version}"
-
-            [VERSION_MIGRATION.migration_fcts]
             """
         )
         if is_compatible:
@@ -137,7 +133,6 @@ class TestCoreVersionInCoreSectionConfig:
             mode = "development"
             version_number = ""
             force = "False:bool"
-            [VERSION_MIGRATION.migration_fcts]
             """
         )
         Config.load(file_config.filename)

+ 1 - 12
tests/core/config/test_default_config.py

@@ -15,7 +15,6 @@ from taipy.config.global_app.global_app_config import GlobalAppConfig
 from taipy.core.config import CoreSection
 from taipy.core.config.data_node_config import DataNodeConfig
 from taipy.core.config.job_config import JobConfig
-from taipy.core.config.migration_config import MigrationConfig
 from taipy.core.config.scenario_config import ScenarioConfig
 from taipy.core.config.task_config import TaskConfig
 
@@ -70,12 +69,6 @@ def _test_default_scenario_config(scenario_config: ScenarioConfig):
     assert len(scenario_config.properties) == 0  # type: ignore
 
 
-def _test_default_version_migration_config(version_migration_config: MigrationConfig):
-    assert version_migration_config is not None
-    assert version_migration_config.migration_fcts == {}
-    assert len(version_migration_config.properties) == 0  # type: ignore
-
-
 def _test_default_global_app_config(global_config: GlobalAppConfig):
     assert global_config is not None
     assert not global_config.notification
@@ -90,17 +83,13 @@ def test_default_configuration():
     _test_default_global_app_config(GlobalAppConfig().default_config())
 
     assert default_config._unique_sections is not None
-    assert len(default_config._unique_sections) == 3
+    assert len(default_config._unique_sections) == 2
     assert len(default_config._sections) == 3
 
     _test_default_job_config(default_config._unique_sections[JobConfig.name])
     _test_default_job_config(Config.job_config)
     _test_default_job_config(JobConfig().default_config())
 
-    _test_default_version_migration_config(default_config._unique_sections[MigrationConfig.name])
-    _test_default_version_migration_config(Config.migration_functions)
-    _test_default_version_migration_config(MigrationConfig.default_config())
-
     _test_default_core_section(default_config._unique_sections[CoreSection.name])
     _test_default_core_section(Config.core)
     _test_default_core_section(CoreSection().default_config())

+ 0 - 2
tests/core/config/test_file_config.py

@@ -40,8 +40,6 @@ version_number = ""
 force = "False:bool"
 core_version = "{CoreSection._CURRENT_CORE_VERSION}"
 
-[VERSION_MIGRATION.migration_fcts]
-
 [DATA_NODE.default]
 storage_type = "in_memory"
 scope = "SCENARIO:SCOPE"

+ 0 - 65
tests/core/config/test_migration_config.py

@@ -1,65 +0,0 @@
-# Copyright 2021-2024 Avaiga Private Limited
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
-# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations under the License.
-
-from taipy.config.config import Config
-
-
-def migrate_pickle_path(dn):
-    dn.path = "s1.pkl"
-
-
-def migrate_skippable(task):
-    task.skippable = True
-
-
-def test_migration_config():
-    assert Config.migration_functions.migration_fcts == {}
-
-    data_nodes1 = Config.configure_data_node("data_nodes1", "pickle")
-
-    migration_cfg = Config.add_migration_function(
-        target_version="1.0",
-        config=data_nodes1,
-        migration_fct=migrate_pickle_path,
-    )
-
-    assert migration_cfg.migration_fcts == {"1.0": {"data_nodes1": migrate_pickle_path}}
-    assert migration_cfg.properties == {}
-
-    data_nodes2 = Config.configure_data_node("data_nodes2", "pickle")
-
-    migration_cfg = Config.add_migration_function(
-        target_version="1.0",
-        config=data_nodes2,
-        migration_fct=migrate_pickle_path,
-    )
-    assert migration_cfg.migration_fcts == {
-        "1.0": {"data_nodes1": migrate_pickle_path, "data_nodes2": migrate_pickle_path}
-    }
-
-
-def test_clean_config():
-    assert Config.migration_functions.migration_fcts == {}
-
-    data_nodes1 = Config.configure_data_node("data_nodes1", "pickle")
-    migration_cfg = Config.add_migration_function(
-        target_version="1.0",
-        config=data_nodes1,
-        migration_fct=migrate_pickle_path,
-    )
-
-    assert migration_cfg.migration_fcts == {"1.0": {"data_nodes1": migrate_pickle_path}}
-    assert migration_cfg.properties == {}
-
-    migration_cfg._clean()
-
-    assert migration_cfg.migration_fcts == {}
-    assert migration_cfg._properties == {}

+ 0 - 1
tests/core/conftest.py

@@ -350,7 +350,6 @@ def init_config(reset_configuration_singleton, inject_core_sections):
         _Checker.add_checker(_CoreSectionChecker)
         _Checker.add_checker(_DataNodeConfigChecker)
         _Checker.add_checker(_JobConfigChecker)
-        # We don't need to add _MigrationConfigChecker because it is run only when the Core service is run.
         _Checker.add_checker(_TaskConfigChecker)
         _Checker.add_checker(_ScenarioConfigChecker)
 

+ 0 - 136
tests/core/test_core_cli.py

@@ -88,16 +88,6 @@ def test_core_cli_experiment_mode_with_force_version(init_config):
         core.stop()
 
 
-def test_core_cli_production_mode():
-    with patch("sys.argv", ["prog", "--production"]):
-        core = Core()
-        core.run()
-        assert Config.core.mode == "production"
-        assert Config.core.version_number == _VersionManagerFactory._build_manager()._get_latest_version()
-        assert not Config.core.force
-        core.stop()
-
-
 def test_dev_mode_clean_all_entities_of_the_latest_version():
     scenario_config = config_scenario()
 
@@ -268,28 +258,6 @@ def test_version_number_when_switching_mode():
         assert len(_VersionManager._get_all()) == 4
         core.stop()
 
-    # When run with production mode, the latest version is used as production
-    with patch("sys.argv", ["prog", "--production"]):
-        core = Core()
-        core.run()
-        ver_6 = _VersionManager._get_latest_version()
-        production_versions = _VersionManager._get_production_versions()
-        assert ver_6 == ver_5
-        assert production_versions == [ver_6]
-        assert len(_VersionManager._get_all()) == 4
-        core.stop()
-
-    # When run with production mode, the "2.1" version is used as production
-    with patch("sys.argv", ["prog", "--production", "2.1"]):
-        core = Core()
-        core.run()
-        ver_7 = _VersionManager._get_latest_version()
-        production_versions = _VersionManager._get_production_versions()
-        assert ver_7 == "2.1"
-        assert production_versions == [ver_6, ver_7]
-        assert len(_VersionManager._get_all()) == 4
-        core.stop()
-
     # Run with dev mode, the version number is the same as the first dev version to override it
     with patch("sys.argv", ["prog", "--development"]):
         core = Core()
@@ -300,56 +268,6 @@ def test_version_number_when_switching_mode():
         core.stop()
 
 
-def test_production_mode_load_all_entities_from_previous_production_version():
-    scenario_config = config_scenario()
-
-    with patch("sys.argv", ["prog", "--development"]):
-        core = Core()
-        core.run()
-        scenario = _ScenarioManager._create(scenario_config)
-        taipy.submit(scenario)
-        core.stop()
-
-    with patch("sys.argv", ["prog", "--production", "1.0"]):
-        core = Core()
-        core.run()
-        production_ver_1 = _VersionManager._get_latest_version()
-        assert _VersionManager._get_production_versions() == [production_ver_1]
-        # When run production mode on a new app, a dev version is created alongside
-        assert _VersionManager._get_development_version() not in _VersionManager._get_production_versions()
-        assert len(_VersionManager._get_all()) == 2
-
-        scenario = _ScenarioManager._create(scenario_config)
-        taipy.submit(scenario)
-
-        assert len(_DataManager._get_all()) == 2
-        assert len(_TaskManager._get_all()) == 1
-        assert len(_SequenceManager._get_all()) == 1
-        assert len(_ScenarioManager._get_all()) == 1
-        assert len(_CycleManager._get_all()) == 1
-        assert len(_JobManager._get_all()) == 1
-        core.stop()
-
-    with patch("sys.argv", ["prog", "--production", "2.0"]):
-        core = Core()
-        core.run()
-        production_ver_2 = _VersionManager._get_latest_version()
-        assert _VersionManager._get_production_versions() == [production_ver_1, production_ver_2]
-        assert len(_VersionManager._get_all()) == 3
-
-        # All entities from previous production version should be saved
-        scenario = _ScenarioManager._create(scenario_config)
-        taipy.submit(scenario)
-
-        assert len(_DataManager._get_all()) == 4
-        assert len(_TaskManager._get_all()) == 2
-        assert len(_SequenceManager._get_all()) == 2
-        assert len(_ScenarioManager._get_all()) == 2
-        assert len(_CycleManager._get_all()) == 1
-        assert len(_JobManager._get_all()) == 2
-        core.stop()
-
-
 def test_force_override_experiment_version():
     scenario_config = config_scenario()
 
@@ -402,60 +320,6 @@ def test_force_override_experiment_version():
         core.stop()
 
 
-def test_force_override_production_version():
-    scenario_config = config_scenario()
-
-    with patch("sys.argv", ["prog", "--production", "1.0"]):
-        core = Core()
-        core.run()
-        ver_1 = _VersionManager._get_latest_version()
-        production_versions = _VersionManager._get_production_versions()
-        assert ver_1 == "1.0"
-        assert production_versions == ["1.0"]
-        # When create new production version, a development version entity is also created as a placeholder
-        assert len(_VersionManager._get_all()) == 2  # 2 version include 1 production 1 development
-
-        scenario = _ScenarioManager._create(scenario_config)
-        taipy.submit(scenario)
-
-        assert len(_DataManager._get_all()) == 2
-        assert len(_TaskManager._get_all()) == 1
-        assert len(_SequenceManager._get_all()) == 1
-        assert len(_ScenarioManager._get_all()) == 1
-        assert len(_CycleManager._get_all()) == 1
-        assert len(_JobManager._get_all()) == 1
-        core.stop()
-
-    Config.configure_global_app(foo="bar")
-
-    # Without --taipy-force parameter, a SystemExit will be raised
-    with pytest.raises(SystemExit):
-        with patch("sys.argv", ["prog", "--production", "1.0"]):
-            core = Core()
-            core.run()
-    core.stop()
-
-    # With --taipy-force parameter
-    with patch("sys.argv", ["prog", "--production", "1.0", "--taipy-force"]):
-        core = Core()
-        core.run()
-        ver_2 = _VersionManager._get_latest_version()
-        assert ver_2 == "1.0"
-        assert len(_VersionManager._get_all()) == 2  # 2 version include 1 production 1 development
-
-        # All entities from previous submit should be saved
-        scenario = _ScenarioManager._create(scenario_config)
-        taipy.submit(scenario)
-
-        assert len(_DataManager._get_all()) == 4
-        assert len(_TaskManager._get_all()) == 2
-        assert len(_SequenceManager._get_all()) == 2
-        assert len(_ScenarioManager._get_all()) == 2
-        assert len(_CycleManager._get_all()) == 1
-        assert len(_JobManager._get_all()) == 2
-        core.stop()
-
-
 def test_modified_job_configuration_dont_block_application_run(caplog, init_config):
     _ = config_scenario()
 

+ 0 - 280
tests/core/version/test_production_version_migration.py

@@ -1,280 +0,0 @@
-# Copyright 2021-2024 Avaiga Private Limited
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
-# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations under the License.
-
-from unittest.mock import patch
-
-import pytest
-
-from taipy.config.config import Config
-from taipy.core import Core, taipy
-from taipy.core.data._data_manager import _DataManager
-from taipy.core.scenario._scenario_manager import _ScenarioManager
-from tests.core.utils import assert_true_after_time
-
-
-def twice(a):
-    return a * 2
-
-
-def triple(a):
-    return a * 3
-
-
-def migrate_pickle_path(dn):
-    dn.path = "bar.pkl"
-    return dn
-
-
-def migrate_skippable_task(task):
-    task.skippable = True
-    return task
-
-
-def migrate_foo_scenario(scenario):
-    scenario.properties["foo"] = "bar"
-    return scenario
-
-
-def test_migrate_datanode(init_config):
-    scenario_v1 = submit_v1()
-
-    init_config()
-    Config.add_migration_function("2.0", "d1", migrate_pickle_path)
-
-    submit_v2()
-    v1 = taipy.get(scenario_v1.id)
-    assert v1.d1.version == "2.0"
-    assert v1.d1.path == "bar.pkl"
-
-
-@pytest.mark.standalone
-def test_migrate_datanode_in_standalone_mode(init_config):
-    scenario_v1 = submit_v1()
-
-    init_config()
-    Config.configure_job_executions(mode="standalone", max_nb_of_workers=2)
-    Config.add_migration_function("2.0", "d1", migrate_pickle_path)
-
-    scenario_cfg_v2 = config_scenario_v2()
-    with patch("sys.argv", ["prog", "--production", "2.0"]):
-        core = Core()
-        core.run()
-        scenario_v2 = _ScenarioManager._create(scenario_cfg_v2)
-        jobs = _ScenarioManager._submit(scenario_v2).jobs
-        v1 = taipy.get(scenario_v1.id)
-        assert v1.d1.version == "2.0"
-        assert v1.d1.path == "bar.pkl"
-        assert_true_after_time(jobs[0].is_completed)
-        core.stop()
-
-
-def test_migrate_task(init_config):
-    scenario_v1 = submit_v1()
-
-    init_config()
-    Config.add_migration_function("2.0", "my_task", migrate_skippable_task)
-
-    submit_v2()
-    v1 = taipy.get(scenario_v1.id)
-    assert v1.my_task.version == "2.0"
-    assert v1.my_task.skippable is True
-
-
-@pytest.mark.standalone
-def test_migrate_task_in_standalone_mode(init_config):
-    scenario_v1 = submit_v1()
-
-    init_config()
-    Config.configure_job_executions(mode="standalone", max_nb_of_workers=2)
-    Config.add_migration_function("2.0", "my_task", migrate_skippable_task)
-
-    scenario_cfg_v2 = config_scenario_v2()
-    with patch("sys.argv", ["prog", "--production", "2.0"]):
-        core = Core()
-        core.run()
-        scenario_v2 = _ScenarioManager._create(scenario_cfg_v2)
-        jobs = _ScenarioManager._submit(scenario_v2).jobs
-        v1 = taipy.get(scenario_v1.id)
-        assert v1.my_task.version == "2.0"
-        assert v1.my_task.skippable is True
-        assert_true_after_time(jobs[0].is_completed)
-        core.stop()
-
-
-def test_migrate_scenario(init_config):
-    scenario_v1 = submit_v1()
-
-    init_config()
-    Config.add_migration_function("2.0", "my_scenario", migrate_foo_scenario)
-
-    submit_v2()
-    v1 = taipy.get(scenario_v1.id)
-    assert v1.version == "2.0"
-    assert v1.properties["foo"] == "bar"
-
-
-@pytest.mark.standalone
-def test_migrate_scenario_in_standalone_mode(init_config):
-    scenario_v1 = submit_v1()
-
-    init_config()
-    Config.configure_job_executions(mode="standalone", max_nb_of_workers=2)
-    Config.add_migration_function("2.0", "my_scenario", migrate_foo_scenario)
-
-    scenario_cfg_v2 = config_scenario_v2()
-    with patch("sys.argv", ["prog", "--production", "2.0"]):
-        core = Core()
-        core.run()
-        scenario_v2 = _ScenarioManager._create(scenario_cfg_v2)
-        jobs = _ScenarioManager._submit(scenario_v2).jobs
-        v1 = taipy.get(scenario_v1.id)
-        assert v1.version == "2.0"
-        assert v1.properties["foo"] == "bar"
-        assert_true_after_time(jobs[0].is_completed)
-        core.stop()
-
-
-def test_migrate_all_entities(init_config):
-    scenario_v1 = submit_v1()
-
-    init_config()
-    Config.add_migration_function("2.0", "d1", migrate_pickle_path)
-    Config.add_migration_function("2.0", "my_task", migrate_skippable_task)
-    Config.add_migration_function("2.0", "my_scenario", migrate_foo_scenario)
-
-    submit_v2()
-    v1 = taipy.get(scenario_v1.id)
-
-    assert v1.d1.version == "2.0"
-    assert v1.my_task.version == "2.0"
-
-    assert v1.d1.path == "bar.pkl"
-    assert v1.my_task.skippable is True
-    assert v1.properties["foo"] == "bar"
-
-
-@pytest.mark.standalone
-def test_migrate_all_entities_in_standalone_mode(init_config):
-    scenario_v1 = submit_v1()
-
-    init_config()
-    Config.configure_job_executions(mode="standalone", max_nb_of_workers=2)
-    Config.add_migration_function("2.0", "my_scenario", migrate_foo_scenario)
-
-    scenario_cfg_v2 = config_scenario_v2()
-    with patch("sys.argv", ["prog", "--production", "2.0"]):
-        core = Core()
-        core.run()
-        scenario_v2 = _ScenarioManager._create(scenario_cfg_v2)
-        jobs = _ScenarioManager._submit(scenario_v2).jobs
-        v1 = taipy.get(scenario_v1.id)
-        assert v1.version == "2.0"
-        assert v1.properties["foo"] == "bar"
-        assert_true_after_time(jobs[0].is_completed)
-        core.stop()
-
-
-def test_migrate_compatible_version(init_config):
-    scenario_cfg = config_scenario_v1()
-    # Production 1.0
-    with patch("sys.argv", ["prog", "--production", "1.0"]):
-        core = Core()
-        core.run()
-
-        scenario_v1 = _ScenarioManager._create(scenario_cfg)
-        _ScenarioManager._submit(scenario_v1)
-
-        assert scenario_v1.d2.read() == 2
-        assert len(_DataManager._get_all(version_number="all")) == 2
-        core.stop()
-
-    init_config()
-    scenario_cfg = config_scenario_v1()
-
-    # Production 2.0 is a compatible version
-    with patch("sys.argv", ["prog", "--production", "2.0"]):
-        core = Core()
-        core.run()
-
-        scenario_v2 = _ScenarioManager._create(scenario_cfg)
-        _ScenarioManager._submit(scenario_v2)
-
-        assert scenario_v2.d2.read() == 2
-        assert len(_DataManager._get_all(version_number="all")) == 4
-        core.stop()
-
-    init_config()
-
-    # Production 2.1
-    Config.add_migration_function(
-        target_version="2.1",
-        config="d1",
-        migration_fct=migrate_pickle_path,
-    )
-    scenario_cfg_v2_1 = config_scenario_v2()
-
-    with patch("sys.argv", ["prog", "--production", "2.1"]):
-        core = Core()
-        core.run()
-        scenario_v2_1 = _ScenarioManager._create(scenario_cfg_v2_1)
-        _ScenarioManager._submit(scenario_v2_1)
-        core.stop()
-
-    assert scenario_v2_1.d2.read() == 6
-    assert len(_DataManager._get_all(version_number="all")) == 6
-
-    v1 = taipy.get(scenario_v1.id)
-    assert v1.d1.version == "2.1"
-    assert v1.d1.path == "bar.pkl"
-
-    v2 = taipy.get(scenario_v2.id)
-    assert v2.d1.version == "2.1"
-    assert v2.d1.path == "bar.pkl"
-
-
-def submit_v1():
-    scenario_cfg_v1 = config_scenario_v1()
-    with patch("sys.argv", ["prog", "--production", "1.0"]):
-        core = Core()
-        core.run()
-        scenario_v1 = _ScenarioManager._create(scenario_cfg_v1)
-        _ScenarioManager._submit(scenario_v1)
-        core.stop()
-    return scenario_v1
-
-
-def submit_v2():
-    scenario_cfg_v2 = config_scenario_v2()
-    with patch("sys.argv", ["prog", "--production", "2.0"]):
-        core = Core()
-        core.run()
-        scenario_v2 = _ScenarioManager._create(scenario_cfg_v2)
-        _ScenarioManager._submit(scenario_v2)
-        core.stop()
-    return scenario_v2
-
-
-def config_scenario_v1():
-    dn1 = Config.configure_pickle_data_node(id="d1", default_data=1)
-    dn2 = Config.configure_pickle_data_node(id="d2")
-    task_cfg = Config.configure_task("my_task", twice, dn1, dn2)
-    scenario_cfg = Config.configure_scenario("my_scenario", [task_cfg])
-    scenario_cfg.add_sequences({"my_sequence": [task_cfg]})
-    return scenario_cfg
-
-
-def config_scenario_v2():
-    dn1 = Config.configure_pickle_data_node(id="d1", default_data=2)
-    dn2 = Config.configure_pickle_data_node(id="d2")
-    task_cfg = Config.configure_task("my_task", triple, dn1, dn2)
-    scenario_cfg = Config.configure_scenario("my_scenario", [task_cfg])
-    scenario_cfg.add_sequences({"my_scenario": [task_cfg]})
-    return scenario_cfg