_version_manager.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. # Copyright 2021-2025 Avaiga Private Limited
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  4. # the License. You may obtain a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  9. # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  10. # specific language governing permissions and limitations under the License.
  11. import uuid
  12. from typing import List, Optional, Union
  13. from taipy.common.config import Config
  14. from taipy.common.config._config_comparator._comparator_result import _ComparatorResult
  15. from taipy.common.config.exceptions.exceptions import InconsistentEnvVariableError
  16. from taipy.common.logger._taipy_logger import _TaipyLogger
  17. from .._manager._manager import _Manager
  18. from ..exceptions.exceptions import (
  19. ConfigCoreVersionMismatched,
  20. ConflictedConfigurationError,
  21. ModelNotFound,
  22. NonExistingVersion,
  23. VersionAlreadyExistsAsDevelopment,
  24. )
  25. from ..reason import ReasonCollection
  26. from ._version import _Version
  27. from ._version_fs_repository import _VersionFSRepository
  28. class _VersionManager(_Manager[_Version]):
  29. _ENTITY_NAME = _Version.__name__
  30. _logger = _TaipyLogger._get_logger()
  31. _DEVELOPMENT_VERSION = ["development", "dev"]
  32. _LATEST_VERSION = "latest"
  33. _ALL_VERSION = ["all", ""]
  34. _DEFAULT_VERSION = _LATEST_VERSION
  35. _repository: _VersionFSRepository
  36. @classmethod
  37. def _get(cls, entity: Union[str, _Version], default=None) -> _Version:
  38. """
  39. Returns the version entity by id or reference.
  40. """
  41. entity_id = entity if isinstance(entity, str) else entity.id
  42. try:
  43. return cls._repository._load(entity_id)
  44. except ModelNotFound:
  45. return default
  46. @classmethod
  47. def _get_or_create(cls, id: str, force: bool) -> _Version:
  48. if version := cls._get(id):
  49. comparator_result = Config._comparator._find_conflict_config(version.config, Config._applied_config, id) # type: ignore[attr-defined]
  50. if comparator_result.get(_ComparatorResult.CONFLICTED_SECTION_KEY):
  51. if not force:
  52. raise ConflictedConfigurationError()
  53. cls._logger.warning(f"Option --force is detected, overriding the configuration of version {id} ...")
  54. version.config = Config._applied_config # type: ignore[attr-defined]
  55. else:
  56. version = _Version(id=id, config=Config._applied_config) # type: ignore[attr-defined]
  57. cls._set(version)
  58. return version
  59. @classmethod
  60. def _get_all(cls, version_number: Optional[Union[str, List]] = "all") -> List[_Version]:
  61. """
  62. Returns all entities.
  63. """
  64. version_number = cls._replace_version_number(version_number) # type: ignore
  65. if not isinstance(version_number, List):
  66. version_number = [version_number] if version_number else []
  67. filters = [{"version": version} for version in version_number]
  68. return cls._repository._load_all(filters)
  69. @classmethod
  70. def _is_deletable(cls, version_number: Optional[str]) -> ReasonCollection:
  71. return ReasonCollection()
  72. @classmethod
  73. def _set_development_version(cls, version_number: str) -> str:
  74. cls._get_or_create(version_number, force=True)
  75. cls._repository._set_development_version(version_number)
  76. return version_number
  77. @classmethod
  78. def _get_development_version(cls) -> str:
  79. try:
  80. return cls._repository._get_development_version()
  81. except (FileNotFoundError, ModelNotFound):
  82. return cls._set_development_version(str(uuid.uuid4()))
  83. @classmethod
  84. def _set_experiment_version(cls, version_number: str, force: bool = False) -> str:
  85. if version_number == cls._get_development_version():
  86. raise VersionAlreadyExistsAsDevelopment(version_number)
  87. try:
  88. cls._get_or_create(version_number, force)
  89. except ConflictedConfigurationError:
  90. raise SystemExit(
  91. f"Please add a new experiment version or run your application with --force option to"
  92. f" override the Config of experiment {version_number}."
  93. ) from None
  94. cls._repository._set_latest_version(version_number)
  95. return version_number
  96. @classmethod
  97. def _get_latest_version(cls) -> str:
  98. try:
  99. return cls._repository._get_latest_version()
  100. except (FileNotFoundError, ModelNotFound):
  101. # If there is no version in the system yet, create a new version as development version
  102. # This set the default versioning behavior on Jupyter notebook to Development mode
  103. return cls._set_development_version(str(uuid.uuid4()))
  104. @classmethod
  105. def _replace_version_number(cls, version_number: Optional[str] = None):
  106. if version_number is None:
  107. return cls._replace_version_number(cls._DEFAULT_VERSION)
  108. if version_number == cls._LATEST_VERSION:
  109. return cls._get_latest_version()
  110. if version_number in cls._DEVELOPMENT_VERSION:
  111. return cls._get_development_version()
  112. if version_number in cls._ALL_VERSION:
  113. return ""
  114. try:
  115. if version_number == cls._get_development_version():
  116. if cls._exists(version_number):
  117. return version_number
  118. else:
  119. if cls._get(version_number):
  120. return version_number
  121. except InconsistentEnvVariableError: # The version exist but the Config is alternated
  122. return version_number
  123. except ConfigCoreVersionMismatched as e:
  124. cls._logger.error(e.message)
  125. raise SystemExit() from e
  126. raise NonExistingVersion(version_number)
  127. @classmethod
  128. def _rename_version(cls, old_version: str, new_version: str) -> None:
  129. version_entity = cls._get(old_version)
  130. if old_version == cls._get_latest_version():
  131. try:
  132. cls._set_experiment_version(new_version)
  133. except VersionAlreadyExistsAsDevelopment as err:
  134. raise SystemExit(err.message) from None
  135. if old_version == cls._get_development_version():
  136. cls._set_development_version(new_version)
  137. cls._delete(old_version)
  138. if not cls._get(new_version):
  139. version_entity.id = new_version
  140. cls._set(version_entity)
  141. @classmethod
  142. def _manage_version(cls) -> None:
  143. if Config.core.mode == "development":
  144. from ..taipy import clean_all_entities
  145. current_version_number = cls._get_development_version()
  146. cls._logger.info(f"Development mode: Clean all entities of version {current_version_number}")
  147. clean_all_entities(current_version_number)
  148. cls._set_development_version(current_version_number)
  149. elif Config.core.mode == "experiment":
  150. if Config.core.version_number:
  151. current_version_number = Config.core.version_number
  152. else:
  153. current_version_number = str(uuid.uuid4())
  154. try:
  155. cls._set_experiment_version(current_version_number, Config.core.force)
  156. except VersionAlreadyExistsAsDevelopment as err:
  157. raise SystemExit(err.message) from None
  158. except ConfigCoreVersionMismatched as e:
  159. cls._logger.error(e.message)
  160. raise SystemExit() from e
  161. else:
  162. raise SystemExit(f"Undefined execution mode: {Config.core.mode}.")
  163. @classmethod
  164. def _delete_entities_of_multiple_types(cls, _entity_ids):
  165. raise NotImplementedError