_reload.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  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 functools
  12. from typing import Dict, Type
  13. import threading
  14. from ...common._check_dependencies import EnterpriseEditionUtils
  15. from .._manager._manager import _Manager
  16. from ..common._utils import _load_fct
  17. from ..notification import EventOperation, Notifier, _make_event
  18. class _Reloader:
  19. """The _Reloader singleton class"""
  20. _instance = None
  21. _lock = threading.RLock()
  22. def __new__(cls, *args, **kwargs):
  23. with cls._lock:
  24. if not isinstance(cls._instance, cls):
  25. cls._instance = super().__new__(cls *args, **kwargs)
  26. cls._instance._no_reload_context = False # Initialize once
  27. cls._instance._context_depth = 0 # Track nested `with` usage
  28. cls._managers = cls._build_managers()
  29. return cls._instance
  30. def _reload(self, manager: str, obj):
  31. if self._no_reload_context:
  32. return obj
  33. entity = self._get_manager(manager)._get(obj, obj)
  34. if obj._is_in_context and hasattr(entity, "_properties"):
  35. if obj._properties._pending_changes:
  36. entity._properties._pending_changes = obj._properties._pending_changes
  37. if obj._properties._pending_deletions:
  38. entity._properties._pending_deletions = obj._properties._pending_deletions
  39. entity._properties._entity_owner = obj
  40. return entity
  41. def __enter__(self):
  42. with self._lock:
  43. self._context_depth += 1
  44. self._no_reload_context = True
  45. return self
  46. def __exit__(self, exc_type, exc_value, exc_traceback):
  47. with self._lock:
  48. self._context_depth -= 1
  49. if self._context_depth == 0:
  50. self._no_reload_context = False
  51. @classmethod
  52. @functools.lru_cache
  53. def _build_managers(cls) -> Dict[str, Type[_Manager]]:
  54. from ..cycle._cycle_manager_factory import _CycleManagerFactory
  55. from ..data._data_manager_factory import _DataManagerFactory
  56. from ..job._job_manager_factory import _JobManagerFactory
  57. from ..scenario._scenario_manager_factory import _ScenarioManagerFactory
  58. from ..sequence._sequence_manager_factory import _SequenceManagerFactory
  59. from ..submission._submission_manager_factory import _SubmissionManagerFactory
  60. from ..task._task_manager_factory import _TaskManagerFactory
  61. managers: Dict[str, Type[_Manager]] = {
  62. "scenario": _ScenarioManagerFactory._build_manager(),
  63. "sequence": _SequenceManagerFactory._build_manager(),
  64. "data": _DataManagerFactory._build_manager(),
  65. "cycle": _CycleManagerFactory._build_manager(),
  66. "job": _JobManagerFactory._build_manager(),
  67. "task": _TaskManagerFactory._build_manager(),
  68. "submission": _SubmissionManagerFactory._build_manager(),
  69. }
  70. if EnterpriseEditionUtils._using_enterprise():
  71. _build_enterprise_managers = _load_fct(
  72. EnterpriseEditionUtils._TAIPY_ENTERPRISE_CORE_MODULE + "._entity.utils", "_build_enterprise_managers"
  73. )
  74. managers.update(_build_enterprise_managers())
  75. return managers
  76. @classmethod
  77. @functools.lru_cache
  78. def _get_manager(cls, manager: str) -> Type[_Manager]:
  79. return cls._managers[manager]
  80. def _self_reload(manager: str):
  81. def __reload(fct):
  82. @functools.wraps(fct)
  83. def _do_reload(self, *args, **kwargs):
  84. self = _Reloader()._reload(manager, self)
  85. return fct(self, *args, **kwargs)
  86. return _do_reload
  87. return __reload
  88. def _self_setter(manager):
  89. def __update_entity(fct):
  90. @functools.wraps(fct)
  91. def _do_update_entity(self, *args, **kwargs):
  92. fct(self, *args, **kwargs)
  93. value = args[0] if len(args) == 1 else args
  94. event = _make_event(
  95. self,
  96. EventOperation.UPDATE,
  97. attribute_name=fct.__name__,
  98. attribute_value=value,
  99. )
  100. if not self._is_in_context:
  101. entity = _Reloader()._reload(manager, self)
  102. fct(entity, *args, **kwargs)
  103. _Reloader._get_manager(manager)._update(entity)
  104. Notifier.publish(event)
  105. else:
  106. self._in_context_attributes_changed_collector.append(event)
  107. return _do_update_entity
  108. return __update_entity