From c2218c58ef14d18e12258785aaadef8602953a1c Mon Sep 17 00:00:00 2001 From: trgiangdo Date: Thu, 24 Oct 2024 00:20:56 +0700 Subject: [PATCH 1/5] refactor: make the EventEntityType, _UNSUBMITTABLE_ENTITY_TYPES expandable from elsewhere --- taipy/core/notification/__init__.py | 2 +- taipy/core/notification/_topic.py | 4 +-- taipy/core/notification/event.py | 52 ++++++++++++++++++----------- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/taipy/core/notification/__init__.py b/taipy/core/notification/__init__.py index c450a2f2e4..cead5789fb 100644 --- a/taipy/core/notification/__init__.py +++ b/taipy/core/notification/__init__.py @@ -26,6 +26,6 @@ from ._registration import _Registration from ._topic import _Topic from .core_event_consumer import CoreEventConsumerBase -from .event import _ENTITY_TO_EVENT_ENTITY_TYPE, Event, EventEntityType, EventOperation, _make_event +from .event import Event, EventEntityType, EventOperation, _make_event from .notifier import Notifier, _publish_event from .registration_id import RegistrationId diff --git a/taipy/core/notification/_topic.py b/taipy/core/notification/_topic.py index d5fd55ca65..be7901480b 100644 --- a/taipy/core/notification/_topic.py +++ b/taipy/core/notification/_topic.py @@ -12,7 +12,7 @@ from typing import Optional from ..exceptions.exceptions import InvalidEventOperation -from .event import _UNSUBMITTABLE_ENTITY_TYPES, EventEntityType, EventOperation +from .event import Event, EventEntityType, EventOperation class _Topic: @@ -35,7 +35,7 @@ def __preprocess_operation( if ( entity_type and operation - and entity_type in _UNSUBMITTABLE_ENTITY_TYPES + and entity_type in Event._UNSUBMITTABLE_ENTITY_TYPES and operation == EventOperation.SUBMISSION ): raise InvalidEventOperation diff --git a/taipy/core/notification/event.py b/taipy/core/notification/event.py index 845eb4c1b9..63eafca714 100644 --- a/taipy/core/notification/event.py +++ b/taipy/core/notification/event.py @@ -62,23 +62,20 @@ class EventEntityType(_ReprEnum): JOB = 6 SUBMISSION = 7 - -_NO_ATTRIBUTE_NAME_OPERATIONS = {EventOperation.CREATION, EventOperation.DELETION, EventOperation.SUBMISSION} -_UNSUBMITTABLE_ENTITY_TYPES = ( - EventEntityType.CYCLE, - EventEntityType.DATA_NODE, - EventEntityType.JOB, - EventEntityType.SUBMISSION, -) -_ENTITY_TO_EVENT_ENTITY_TYPE = { - "scenario": EventEntityType.SCENARIO, - "sequence": EventEntityType.SEQUENCE, - "task": EventEntityType.TASK, - "data": EventEntityType.DATA_NODE, - "job": EventEntityType.JOB, - "cycle": EventEntityType.CYCLE, - "submission": EventEntityType.SUBMISSION, -} + @classmethod + def add_member(cls, name, value): + # Check if the member already exists to prevent duplication + if name in cls._member_map_: + return + + # Create a new enum member + new_member = object.__new__(cls) + new_member._name_ = name + new_member._value_ = value + # Update the class dictionary and member maps + setattr(cls, name, new_member) + cls._member_map_[name] = new_member + cls._value2member_map_[value] = new_member @dataclass(frozen=True) @@ -88,6 +85,23 @@ class Event: An event holds the necessary attributes to identify the change. """ + _NO_ATTRIBUTE_NAME_OPERATIONS = {EventOperation.CREATION, EventOperation.DELETION, EventOperation.SUBMISSION} + _UNSUBMITTABLE_ENTITY_TYPES = { + EventEntityType.CYCLE, + EventEntityType.DATA_NODE, + EventEntityType.JOB, + EventEntityType.SUBMISSION, + } + _ENTITY_TO_EVENT_ENTITY_TYPE = { + "scenario": EventEntityType.SCENARIO, + "sequence": EventEntityType.SEQUENCE, + "task": EventEntityType.TASK, + "data": EventEntityType.DATA_NODE, + "job": EventEntityType.JOB, + "cycle": EventEntityType.CYCLE, + "submission": EventEntityType.SUBMISSION, + } + entity_type: EventEntityType """Type of the entity that was changed (`DataNode^`, `Scenario^`, `Cycle^`, etc. ).""" operation: EventOperation @@ -112,11 +126,11 @@ def __post_init__(self): super().__setattr__("creation_date", datetime.now()) # Check operation: - if self.entity_type in _UNSUBMITTABLE_ENTITY_TYPES and self.operation == EventOperation.SUBMISSION: + if self.entity_type in self._UNSUBMITTABLE_ENTITY_TYPES and self.operation == EventOperation.SUBMISSION: raise InvalidEventOperation # Check attribute name: - if self.operation in _NO_ATTRIBUTE_NAME_OPERATIONS and self.attribute_name is not None: + if self.operation in self._NO_ATTRIBUTE_NAME_OPERATIONS and self.attribute_name is not None: raise InvalidEventAttributeName From ef67923fd953a85bb4531626997158a2dc8fccfd Mon Sep 17 00:00:00 2001 From: trgiangdo Date: Thu, 24 Oct 2024 00:22:16 +0700 Subject: [PATCH 2/5] refactor: expand the _get_manager() with User entity from taipy.auth --- taipy/core/_entity/_reload.py | 14 ++++++++++++-- taipy/core/common/_check_dependencies.py | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/taipy/core/_entity/_reload.py b/taipy/core/_entity/_reload.py index ed32f3fc58..9af44857b0 100644 --- a/taipy/core/_entity/_reload.py +++ b/taipy/core/_entity/_reload.py @@ -12,6 +12,8 @@ import functools from .._manager._manager import _Manager +from ..common._check_dependencies import EnterpriseEditionUtils +from ..common._utils import _load_fct from ..notification import EventOperation, Notifier, _make_event @@ -96,7 +98,7 @@ def _get_manager(manager: str) -> _Manager: from ..submission._submission_manager_factory import _SubmissionManagerFactory from ..task._task_manager_factory import _TaskManagerFactory - return { + managers = { "scenario": _ScenarioManagerFactory._build_manager(), "sequence": _SequenceManagerFactory._build_manager(), "data": _DataManagerFactory._build_manager(), @@ -104,4 +106,12 @@ def _get_manager(manager: str) -> _Manager: "job": _JobManagerFactory._build_manager(), "task": _TaskManagerFactory._build_manager(), "submission": _SubmissionManagerFactory._build_manager(), - }[manager] # type: ignore + } + + if EnterpriseEditionUtils._using_enterprise(): + _build_user_manager = _load_fct( + EnterpriseEditionUtils._TAIPY_AUTH_MODULE + ".user._user_manager_factory", "_UserManagerFactory" + )._build_manager + managers["user"] = _build_user_manager() + + return managers[manager] diff --git a/taipy/core/common/_check_dependencies.py b/taipy/core/common/_check_dependencies.py index 333fb9cd1d..e0b296e848 100644 --- a/taipy/core/common/_check_dependencies.py +++ b/taipy/core/common/_check_dependencies.py @@ -32,6 +32,7 @@ def _check_dependency_is_installed(module_name: str, package_name: str) -> None: class EnterpriseEditionUtils: + _TAIPY_AUTH_MODULE = "taipy.auth" _TAIPY_ENTERPRISE_MODULE = "taipy.enterprise" _TAIPY_ENTERPRISE_CORE_MODULE = _TAIPY_ENTERPRISE_MODULE + ".core" From dd2059ec0191daeb07e6e8ae13f3b4ad177ccabe Mon Sep 17 00:00:00 2001 From: trgiangdo Date: Thu, 31 Oct 2024 17:26:58 +0700 Subject: [PATCH 3/5] refactor: build enterprise managers alongside community managers in the _Reloader class --- taipy/core/_entity/_entity.py | 6 +- taipy/core/_entity/_reload.py | 72 +++++++++++++----------- taipy/core/common/_check_dependencies.py | 1 - 3 files changed, 43 insertions(+), 36 deletions(-) diff --git a/taipy/core/_entity/_entity.py b/taipy/core/_entity/_entity.py index 7b5e3ed1be..f48d1a5a29 100644 --- a/taipy/core/_entity/_entity.py +++ b/taipy/core/_entity/_entity.py @@ -11,7 +11,7 @@ from typing import List -from .._entity._reload import _get_manager +from .._entity._reload import _Reloader from ..notification import Notifier @@ -33,8 +33,8 @@ def __exit__(self, exc_type, exc_value, exc_traceback): for to_delete_key in self._properties._pending_deletions: self._properties.data.pop(to_delete_key, None) self._properties.data.update(self._properties._pending_changes) - _get_manager(self._MANAGER_NAME)._set(self) + _Reloader._get_manager(self._MANAGER_NAME)._set(self) for event in self._in_context_attributes_changed_collector: Notifier.publish(event) - _get_manager(self._MANAGER_NAME)._set(self) + _Reloader._get_manager(self._MANAGER_NAME)._set(self) diff --git a/taipy/core/_entity/_reload.py b/taipy/core/_entity/_reload.py index 9af44857b0..da61be05af 100644 --- a/taipy/core/_entity/_reload.py +++ b/taipy/core/_entity/_reload.py @@ -10,6 +10,7 @@ # specific language governing permissions and limitations under the License. import functools +from typing import Dict from .._manager._manager import _Manager from ..common._check_dependencies import EnterpriseEditionUtils @@ -21,19 +22,21 @@ class _Reloader: """The _Reloader singleton class""" _instance = None - _no_reload_context = False + _managers: Dict[str, _Manager] = {} + def __new__(cls, *args, **kwargs): if not isinstance(cls._instance, cls): cls._instance = object.__new__(cls, *args, **kwargs) + cls._managers = cls._build_managers() return cls._instance def _reload(self, manager: str, obj): if self._no_reload_context: return obj - entity = _get_manager(manager)._get(obj, obj) + entity = self._get_manager(manager)._get(obj, obj) if obj._is_in_context and hasattr(entity, "_properties"): if obj._properties._pending_changes: entity._properties._pending_changes = obj._properties._pending_changes @@ -49,6 +52,40 @@ def __enter__(self): def __exit__(self, exc_type, exc_value, exc_traceback): self._no_reload_context = False + @classmethod + @functools.lru_cache + def _build_managers(cls) -> Dict[str, _Manager]: + from ..cycle._cycle_manager_factory import _CycleManagerFactory + from ..data._data_manager_factory import _DataManagerFactory + from ..job._job_manager_factory import _JobManagerFactory + from ..scenario._scenario_manager_factory import _ScenarioManagerFactory + from ..sequence._sequence_manager_factory import _SequenceManagerFactory + from ..submission._submission_manager_factory import _SubmissionManagerFactory + from ..task._task_manager_factory import _TaskManagerFactory + + managers = { + "scenario": _ScenarioManagerFactory._build_manager(), + "sequence": _SequenceManagerFactory._build_manager(), + "data": _DataManagerFactory._build_manager(), + "cycle": _CycleManagerFactory._build_manager(), + "job": _JobManagerFactory._build_manager(), + "task": _TaskManagerFactory._build_manager(), + "submission": _SubmissionManagerFactory._build_manager(), + } + + if EnterpriseEditionUtils._using_enterprise(): + _build_enterprise_managers = _load_fct( + EnterpriseEditionUtils._TAIPY_ENTERPRISE_CORE_MODULE + "._entity.utils", "_build_enterprise_managers" + ) + managers.update(_build_enterprise_managers()) + + return managers + + @classmethod + @functools.lru_cache + def _get_manager(cls, manager: str) -> _Manager: + return cls._managers[manager] + def _self_reload(manager: str): def __reload(fct): @@ -67,7 +104,7 @@ def __set_entity(fct): @functools.wraps(fct) def _do_set_entity(self, *args, **kwargs): fct(self, *args, **kwargs) - entity_manager = _get_manager(manager) + entity_manager = _Reloader._get_manager(manager) value = args[0] if len(args) == 1 else args event = _make_event( self, @@ -86,32 +123,3 @@ def _do_set_entity(self, *args, **kwargs): return _do_set_entity return __set_entity - - -@functools.lru_cache -def _get_manager(manager: str) -> _Manager: - from ..cycle._cycle_manager_factory import _CycleManagerFactory - from ..data._data_manager_factory import _DataManagerFactory - from ..job._job_manager_factory import _JobManagerFactory - from ..scenario._scenario_manager_factory import _ScenarioManagerFactory - from ..sequence._sequence_manager_factory import _SequenceManagerFactory - from ..submission._submission_manager_factory import _SubmissionManagerFactory - from ..task._task_manager_factory import _TaskManagerFactory - - managers = { - "scenario": _ScenarioManagerFactory._build_manager(), - "sequence": _SequenceManagerFactory._build_manager(), - "data": _DataManagerFactory._build_manager(), - "cycle": _CycleManagerFactory._build_manager(), - "job": _JobManagerFactory._build_manager(), - "task": _TaskManagerFactory._build_manager(), - "submission": _SubmissionManagerFactory._build_manager(), - } - - if EnterpriseEditionUtils._using_enterprise(): - _build_user_manager = _load_fct( - EnterpriseEditionUtils._TAIPY_AUTH_MODULE + ".user._user_manager_factory", "_UserManagerFactory" - )._build_manager - managers["user"] = _build_user_manager() - - return managers[manager] diff --git a/taipy/core/common/_check_dependencies.py b/taipy/core/common/_check_dependencies.py index e0b296e848..333fb9cd1d 100644 --- a/taipy/core/common/_check_dependencies.py +++ b/taipy/core/common/_check_dependencies.py @@ -32,7 +32,6 @@ def _check_dependency_is_installed(module_name: str, package_name: str) -> None: class EnterpriseEditionUtils: - _TAIPY_AUTH_MODULE = "taipy.auth" _TAIPY_ENTERPRISE_MODULE = "taipy.enterprise" _TAIPY_ENTERPRISE_CORE_MODULE = _TAIPY_ENTERPRISE_MODULE + ".core" From 0bf8cf10840ed17a61d8944e4688e9dddfb249cf Mon Sep 17 00:00:00 2001 From: trgiangdo Date: Wed, 6 Nov 2024 10:05:41 +0700 Subject: [PATCH 4/5] fix: _Reloader._managers should has values with type Type[_Manager], not _Manager --- taipy/core/_entity/_reload.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/taipy/core/_entity/_reload.py b/taipy/core/_entity/_reload.py index da61be05af..225ecfff02 100644 --- a/taipy/core/_entity/_reload.py +++ b/taipy/core/_entity/_reload.py @@ -10,7 +10,7 @@ # specific language governing permissions and limitations under the License. import functools -from typing import Dict +from typing import Dict, Type from .._manager._manager import _Manager from ..common._check_dependencies import EnterpriseEditionUtils @@ -24,7 +24,7 @@ class _Reloader: _instance = None _no_reload_context = False - _managers: Dict[str, _Manager] = {} + _managers: Dict[str, Type[_Manager]] = {} def __new__(cls, *args, **kwargs): if not isinstance(cls._instance, cls): @@ -54,7 +54,7 @@ def __exit__(self, exc_type, exc_value, exc_traceback): @classmethod @functools.lru_cache - def _build_managers(cls) -> Dict[str, _Manager]: + def _build_managers(cls) -> Dict[str, Type[_Manager]]: from ..cycle._cycle_manager_factory import _CycleManagerFactory from ..data._data_manager_factory import _DataManagerFactory from ..job._job_manager_factory import _JobManagerFactory @@ -63,7 +63,7 @@ def _build_managers(cls) -> Dict[str, _Manager]: from ..submission._submission_manager_factory import _SubmissionManagerFactory from ..task._task_manager_factory import _TaskManagerFactory - managers = { + managers: Dict[str, Type[_Manager]] = { "scenario": _ScenarioManagerFactory._build_manager(), "sequence": _SequenceManagerFactory._build_manager(), "data": _DataManagerFactory._build_manager(), @@ -83,7 +83,7 @@ def _build_managers(cls) -> Dict[str, _Manager]: @classmethod @functools.lru_cache - def _get_manager(cls, manager: str) -> _Manager: + def _get_manager(cls, manager: str) -> Type[_Manager]: return cls._managers[manager] From 74ae9618783a2f29dd5f09895c31d26dbfce92c3 Mon Sep 17 00:00:00 2001 From: trgiangdo Date: Wed, 6 Nov 2024 10:08:49 +0700 Subject: [PATCH 5/5] fix: clean up type: ignore in manager factories --- taipy/core/_manager/_manager_factory.py | 2 +- taipy/core/data/_data_manager_factory.py | 3 ++- taipy/core/job/_job_manager_factory.py | 3 ++- taipy/core/scenario/_scenario_manager_factory.py | 3 ++- taipy/core/sequence/_sequence_manager_factory.py | 5 +++-- taipy/core/submission/_submission_manager_factory.py | 3 ++- 6 files changed, 12 insertions(+), 7 deletions(-) diff --git a/taipy/core/_manager/_manager_factory.py b/taipy/core/_manager/_manager_factory.py index baa03827e4..ff42695d10 100644 --- a/taipy/core/_manager/_manager_factory.py +++ b/taipy/core/_manager/_manager_factory.py @@ -20,7 +20,7 @@ class _ManagerFactory: @classmethod @abstractmethod - def _build_manager(cls) -> Type[_Manager]: # type: ignore + def _build_manager(cls) -> Type[_Manager]: raise NotImplementedError @classmethod diff --git a/taipy/core/data/_data_manager_factory.py b/taipy/core/data/_data_manager_factory.py index df69842c4f..33a0dcd455 100644 --- a/taipy/core/data/_data_manager_factory.py +++ b/taipy/core/data/_data_manager_factory.py @@ -8,6 +8,7 @@ # 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 functools import lru_cache from typing import Type @@ -27,7 +28,7 @@ def _build_manager(cls) -> Type[_DataManager]: if EnterpriseEditionUtils._using_enterprise(): data_manager = _load_fct( EnterpriseEditionUtils._TAIPY_ENTERPRISE_CORE_MODULE + ".data._data_manager", "_DataManager" - ) # type: ignore + ) build_repository = _load_fct( EnterpriseEditionUtils._TAIPY_ENTERPRISE_CORE_MODULE + ".data._data_manager_factory", "_DataManagerFactory", diff --git a/taipy/core/job/_job_manager_factory.py b/taipy/core/job/_job_manager_factory.py index be2645bd7b..96f2407222 100644 --- a/taipy/core/job/_job_manager_factory.py +++ b/taipy/core/job/_job_manager_factory.py @@ -8,6 +8,7 @@ # 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 functools import lru_cache from typing import Type @@ -27,7 +28,7 @@ def _build_manager(cls) -> Type[_JobManager]: if EnterpriseEditionUtils._using_enterprise(): job_manager = _load_fct( EnterpriseEditionUtils._TAIPY_ENTERPRISE_CORE_MODULE + ".job._job_manager", "_JobManager" - ) # type: ignore + ) build_repository = _load_fct( EnterpriseEditionUtils._TAIPY_ENTERPRISE_CORE_MODULE + ".job._job_manager_factory", "_JobManagerFactory" )._build_repository # type: ignore diff --git a/taipy/core/scenario/_scenario_manager_factory.py b/taipy/core/scenario/_scenario_manager_factory.py index bb9630f907..6d0785221c 100644 --- a/taipy/core/scenario/_scenario_manager_factory.py +++ b/taipy/core/scenario/_scenario_manager_factory.py @@ -8,6 +8,7 @@ # 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 functools import lru_cache from typing import Type @@ -27,7 +28,7 @@ def _build_manager(cls) -> Type[_ScenarioManager]: if EnterpriseEditionUtils._using_enterprise(): scenario_manager = _load_fct( EnterpriseEditionUtils._TAIPY_ENTERPRISE_CORE_MODULE + ".scenario._scenario_manager", "_ScenarioManager" - ) # type: ignore + ) build_repository = _load_fct( EnterpriseEditionUtils._TAIPY_ENTERPRISE_CORE_MODULE + ".scenario._scenario_manager_factory", "_ScenarioManagerFactory", diff --git a/taipy/core/sequence/_sequence_manager_factory.py b/taipy/core/sequence/_sequence_manager_factory.py index d2490a35e7..5d11e6a46b 100644 --- a/taipy/core/sequence/_sequence_manager_factory.py +++ b/taipy/core/sequence/_sequence_manager_factory.py @@ -8,6 +8,7 @@ # 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 functools import lru_cache from typing import Type @@ -20,11 +21,11 @@ class _SequenceManagerFactory(_ManagerFactory): @classmethod @lru_cache - def _build_manager(cls) -> Type[_SequenceManager]: # type: ignore + def _build_manager(cls) -> Type[_SequenceManager]: if EnterpriseEditionUtils._using_enterprise(): sequence_manager = _load_fct( EnterpriseEditionUtils._TAIPY_ENTERPRISE_CORE_MODULE + ".sequence._sequence_manager", "_SequenceManager" - ) # type: ignore + ) else: sequence_manager = _SequenceManager return sequence_manager # type: ignore diff --git a/taipy/core/submission/_submission_manager_factory.py b/taipy/core/submission/_submission_manager_factory.py index 4d0f2f84ee..8758a34e08 100644 --- a/taipy/core/submission/_submission_manager_factory.py +++ b/taipy/core/submission/_submission_manager_factory.py @@ -8,6 +8,7 @@ # 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 functools import lru_cache from typing import Type @@ -28,7 +29,7 @@ def _build_manager(cls) -> Type[_SubmissionManager]: submission_manager = _load_fct( EnterpriseEditionUtils._TAIPY_ENTERPRISE_CORE_MODULE + ".submission._submission_manager", "_SubmissionManager", - ) # type: ignore + ) build_repository = _load_fct( EnterpriseEditionUtils._TAIPY_ENTERPRISE_CORE_MODULE + ".submission._submission_manager_factory", "_SubmissionManagerFactory",