浏览代码

added more reason classes and fixed failed tests and import issues

Toan Quach 10 月之前
父节点
当前提交
c7df32acc2

+ 1 - 1
taipy/core/_entity/_ready_to_run_property.py

@@ -12,7 +12,7 @@
 from typing import TYPE_CHECKING, Dict, Set, Union
 
 from ..notification import EventOperation, Notifier, _make_event
-from ..reason.reason import Reasons
+from ..reason import Reasons
 
 if TYPE_CHECKING:
     from ..data.data_node import DataNode, DataNodeId

+ 2 - 4
taipy/core/_entity/submittable.py

@@ -19,9 +19,7 @@ from ..common._listattributes import _ListAttributes
 from ..common._utils import _Subscriber
 from ..data.data_node import DataNode
 from ..job.job import Job
-from ..reason import DataNodeEditInProgress, Reasons
-from ..reason.reason import _build_data_node_is_not_written
-from ..reason.reasons import Reasons
+from ..reason import DataNodeEditInProgress, DataNodeIsNotWritten, Reasons
 from ..submission.submission import Submission
 from ..task.task import Task
 from ._dag import _DAG
@@ -97,7 +95,7 @@ class Submittable:
             if node._edit_in_progress:
                 reason._add_reason(node.id, DataNodeEditInProgress(node.id))
             if not node._last_edit_date:
-                reason._add_reason(node.id, _build_data_node_is_not_written(node.id))
+                reason._add_reason(node.id, DataNodeIsNotWritten(node.id))
 
         return reason
 

+ 3 - 4
taipy/core/data/_data_manager.py

@@ -22,8 +22,7 @@ from ..config.data_node_config import DataNodeConfig
 from ..cycle.cycle_id import CycleId
 from ..exceptions.exceptions import InvalidDataNodeType
 from ..notification import Event, EventEntityType, EventOperation, Notifier, _make_event
-from ..reason._reason_factory import _build_not_global_scope_reason, _build_wrong_config_type_reason
-from ..reason.reason import Reasons
+from ..reason import NotGlobalScope, Reasons, WrongConfigType
 from ..scenario.scenario_id import ScenarioId
 from ..sequence.sequence_id import SequenceId
 from ._data_fs_repository import _DataFSRepository
@@ -75,9 +74,9 @@ class _DataManager(_Manager[DataNode], _VersionMixin):
 
         if config is not None:
             if not isinstance(config, DataNodeConfig):
-                reason._add_reason(config_id, _build_wrong_config_type_reason(config_id, "DataNodeConfig"))
+                reason._add_reason(config_id, WrongConfigType(config_id, "DataNodeConfig"))
             elif config.scope is not Scope.GLOBAL:
-                reason._add_reason(config_id, _build_not_global_scope_reason(config_id))
+                reason._add_reason(config_id, NotGlobalScope(config_id))
 
         return reason
 

+ 39 - 21
taipy/core/reason/reason.py

@@ -11,30 +11,37 @@
 
 from typing import Optional
 
-from ..data.data_node import DataNodeId
-
 
 class Reason:
     """
     TODO - NOT DOCUMENTED
     """
+
     def __init__(self, reason: str):
-        self.reason = reason
+        self._reason = reason
+
+    @property
+    def reason(self):
+        return self._reason
 
     def __str__(self):
-        return self.reason
+        return self._reason
 
     def __repr__(self):
-        return self.reason
+        return self._reason
+
+    def __hash__(self) -> int:
+        return hash(self._reason)
 
 
 class _DataNodeReasonMixin:
-    def __init__(self, datanode_id: DataNodeId):
+    def __init__(self, datanode_id: str):
         self.datanode_id = datanode_id
 
     @property
     def datanode(self):
         from ..data._data_manager_factory import _DataManagerFactory
+
         return _DataManagerFactory._build_manager()._get(self.datanode_id)
 
 
@@ -42,7 +49,8 @@ class DataNodeEditInProgress(Reason, _DataNodeReasonMixin):
     """
     TODO - NOT DOCUMENTED
     """
-    def __init__(self, datanode_id: DataNodeId):
+
+    def __init__(self, datanode_id: str):
         Reason.__init__(self, f"DataNode {datanode_id} is being edited")
         _DataNodeReasonMixin.__init__(self, datanode_id)
 
@@ -51,29 +59,39 @@ class DataNodeIsNotWritten(Reason, _DataNodeReasonMixin):
     """
     TODO - NOT DOCUMENTED
     """
-    def __init__(self, datanode_id: DataNodeId):
+
+    def __init__(self, datanode_id: str):
         Reason.__init__(self, f"DataNode {datanode_id} is not written")
         _DataNodeReasonMixin.__init__(self, datanode_id)
 
 
-def _build_data_node_is_being_edited_reason(dn_id: DataNodeId) -> str:
-    return f"DataNode {dn_id} is not written"
-
+class EntityIsNotSubmittableEntity(Reason):
+    """
+    TODO - NOT DOCUMENTED
+    """
 
-def _build_data_node_is_not_written(dn_id: DataNodeId) -> str:
-    return f"DataNode {dn_id} is not written"
+    def __init__(self, entity_id: str):
+        Reason.__init__(self, f"Entity {entity_id} is not a submittable entity")
 
 
-def _build_not_submittable_entity_reason(entity_id: str) -> str:
-    return f"Entity {entity_id} is not a submittable entity"
+class WrongConfigType(Reason):
+    """
+    TODO - NOT DOCUMENTED
+    """
 
+    def __init__(self, config_id: str, config_type: Optional[str]):
+        if config_type:
+            reason = f'Object "{config_id}" must be a valid {config_type}'
+        else:
+            reason = f'Object "{config_id}" is not a valid config to be created'
 
-def _build_wrong_config_type_reason(config_id: str, config_type: Optional[str]) -> str:
-    if config_type:
-        return f'Object "{config_id}" must be a valid {config_type}'
+        Reason.__init__(self, reason)
 
-    return f'Object "{config_id}" is not a valid config to be created'
 
+class NotGlobalScope(Reason):
+    """
+    TODO - NOT DOCUMENTED
+    """
 
-def _build_not_global_scope_reason(config_id: str) -> str:
-    return f'Data node config "{config_id}" does not have GLOBAL scope'
+    def __init__(self, config_id: str):
+        Reason.__init__(self, f'Data node config "{config_id}" does not have GLOBAL scope')

+ 5 - 3
taipy/core/reason/reasons.py

@@ -10,6 +10,7 @@
 # specific language governing permissions and limitations under the License.
 
 from typing import Dict, Set
+
 from .reason import Reason
 
 
@@ -19,17 +20,18 @@ class Reasons:
 
     TODO - Add more details about the class.
     """
+
     def __init__(self, entity_id: str) -> None:
         self.entity_id: str = entity_id
         self._reasons: Dict[str, Set[Reason]] = {}
 
-    def _add_reason(self, entity_id: str, reason: str) -> "Reasons":
+    def _add_reason(self, entity_id: str, reason: Reason) -> "Reasons":
         if entity_id not in self._reasons:
             self._reasons[entity_id] = set()
         self._reasons[entity_id].add(reason)
         return self
 
-    def _remove_reason(self, entity_id: str, reason: str) -> "Reasons":
+    def _remove_reason(self, entity_id: str, reason: Reason) -> "Reasons":
         if entity_id in self._reasons and reason in self._reasons[entity_id]:
             self._reasons[entity_id].remove(reason)
             if len(self._reasons[entity_id]) == 0:
@@ -44,4 +46,4 @@ class Reasons:
 
     @property
     def reasons(self) -> str:
-        return "; ".join("; ".join(reason) for reason in self._reasons.values()) + "." if self._reasons else ""
+        return "; ".join("; ".join(str(reason)) for reason in self._reasons.values()) + "." if self._reasons else ""

+ 3 - 4
taipy/core/scenario/_scenario_manager.py

@@ -39,8 +39,7 @@ from ..exceptions.exceptions import (
 from ..job._job_manager_factory import _JobManagerFactory
 from ..job.job import Job
 from ..notification import EventEntityType, EventOperation, Notifier, _make_event
-from ..reason._reason_factory import _build_not_submittable_entity_reason, _build_wrong_config_type_reason
-from ..reason.reason import Reasons
+from ..reason import EntityIsNotSubmittableEntity, Reasons, WrongConfigType
 from ..submission._submission_manager_factory import _SubmissionManagerFactory
 from ..submission.submission import Submission
 from ..task._task_manager_factory import _TaskManagerFactory
@@ -114,7 +113,7 @@ class _ScenarioManager(_Manager[Scenario], _VersionMixin):
 
         if config is not None:
             if not isinstance(config, ScenarioConfig):
-                reason._add_reason(config_id, _build_wrong_config_type_reason(config_id, "ScenarioConfig"))
+                reason._add_reason(config_id, WrongConfigType(config_id, "ScenarioConfig"))
 
         return reason
 
@@ -209,7 +208,7 @@ class _ScenarioManager(_Manager[Scenario], _VersionMixin):
         if not isinstance(scenario, Scenario):
             scenario = str(scenario)
             reason = Reasons((scenario))
-            reason._add_reason(scenario, _build_not_submittable_entity_reason(scenario))
+            reason._add_reason(scenario, EntityIsNotSubmittableEntity(scenario))
             return reason
 
         return scenario.is_ready_to_run()

+ 2 - 3
taipy/core/sequence/_sequence_manager.py

@@ -29,8 +29,7 @@ from ..job._job_manager_factory import _JobManagerFactory
 from ..job.job import Job
 from ..notification import Event, EventEntityType, EventOperation, Notifier
 from ..notification.event import _make_event
-from ..reason._reason_factory import _build_not_submittable_entity_reason
-from ..reason.reason import Reasons
+from ..reason import EntityIsNotSubmittableEntity, Reasons
 from ..scenario._scenario_manager_factory import _ScenarioManagerFactory
 from ..scenario.scenario import Scenario
 from ..scenario.scenario_id import ScenarioId
@@ -349,7 +348,7 @@ class _SequenceManager(_Manager[Sequence], _VersionMixin):
         if not isinstance(sequence, Sequence):
             sequence = str(sequence)
             reason = Reasons(sequence)
-            reason._add_reason(sequence, _build_not_submittable_entity_reason(sequence))
+            reason._add_reason(sequence, EntityIsNotSubmittableEntity(sequence))
             return reason
 
         return sequence.is_ready_to_run()

+ 2 - 3
taipy/core/taipy.py

@@ -45,8 +45,7 @@ from .exceptions.exceptions import (
 from .job._job_manager_factory import _JobManagerFactory
 from .job.job import Job
 from .job.job_id import JobId
-from .reason._reason_factory import _build_not_submittable_entity_reason
-from .reason.reason import Reasons
+from .reason import EntityIsNotSubmittableEntity, Reasons
 from .scenario._scenario_manager_factory import _ScenarioManagerFactory
 from .scenario.scenario import Scenario
 from .scenario.scenario_id import ScenarioId
@@ -105,7 +104,7 @@ def is_submittable(entity: Union[Scenario, ScenarioId, Sequence, SequenceId, Tas
         return _TaskManagerFactory._build_manager()._is_submittable(entity)
     if isinstance(entity, str) and entity.startswith(Task._ID_PREFIX):
         return _TaskManagerFactory._build_manager()._is_submittable(TaskId(entity))
-    return Reasons(str(entity))._add_reason(str(entity), _build_not_submittable_entity_reason(str(entity)))
+    return Reasons(str(entity))._add_reason(str(entity), EntityIsNotSubmittableEntity(str(entity)))
 
 
 def is_editable(

+ 7 - 8
taipy/core/task/_task_manager.py

@@ -26,8 +26,7 @@ from ..cycle.cycle_id import CycleId
 from ..data._data_manager_factory import _DataManagerFactory
 from ..exceptions.exceptions import NonExistingTask
 from ..notification import EventEntityType, EventOperation, Notifier, _make_event
-from ..reason import Reasons, DataNodeEditInProgress, DataNodeIsNotWritten, _build_not_submittable_entity_reason
-from ..reason.reason import Reasons
+from ..reason import DataNodeEditInProgress, DataNodeIsNotWritten, EntityIsNotSubmittableEntity, Reasons
 from ..scenario.scenario_id import ScenarioId
 from ..sequence.sequence_id import SequenceId
 from ..submission.submission import Submission
@@ -170,19 +169,19 @@ class _TaskManager(_Manager[Task], _VersionMixin):
             task = cls._get(task)
         if not isinstance(task, Task):
             task = str(task)
-            reason = Reasons(task)
-            reason._add_reason(task, _build_not_submittable_entity_reason(task))
+            reasons = Reasons(task)
+            reasons._add_reason(task, EntityIsNotSubmittableEntity(task))
         else:
-            reason = Reasons(task.id)
+            reasons = Reasons(task.id)
             data_manager = _DataManagerFactory._build_manager()
             for node in task.input.values():
                 node = data_manager._get(node)
                 if node._edit_in_progress:
-                    reason._add_reason(node.id, _build_data_node_is_being_edited_reason(node.id))
+                    reasons._add_reason(node.id, DataNodeEditInProgress(node.id))
                 if not node._last_edit_date:
-                    reason._add_reason(node.id, _build_data_node_is_not_written(node.id))
+                    reasons._add_reason(node.id, DataNodeIsNotWritten(node.id))
 
-        return reason
+        return reasons
 
     @classmethod
     def _submit(

+ 1 - 1
tests/core/_entity/test_ready_to_run_property.py

@@ -14,7 +14,7 @@ from taipy import ScenarioId, SequenceId, TaskId
 from taipy.config.common.frequency import Frequency
 from taipy.config.config import Config
 from taipy.core._entity._ready_to_run_property import _ReadyToRunProperty
-from taipy.core.reason.reason import Reasons
+from taipy.core.reason import Reasons
 from taipy.core.scenario._scenario_manager_factory import _ScenarioManagerFactory
 from taipy.core.sequence._sequence_manager_factory import _SequenceManagerFactory
 from taipy.core.task._task_manager_factory import _TaskManagerFactory

+ 1 - 1
tests/core/common/test_reason.py

@@ -9,7 +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 taipy.core.reason.reason import Reasons
+from taipy.core.reason import Reasons
 
 
 def test_create_reason():

+ 5 - 2
tests/core/data/test_data_manager.py

@@ -24,6 +24,7 @@ from taipy.core.data.data_node_id import DataNodeId
 from taipy.core.data.in_memory import InMemoryDataNode
 from taipy.core.data.pickle import PickleDataNode
 from taipy.core.exceptions.exceptions import InvalidDataNodeType, ModelNotFound
+from taipy.core.reason import NotGlobalScope, WrongConfigType
 from tests.core.utils.named_temporary_file import NamedTemporaryFile
 
 
@@ -61,11 +62,13 @@ class TestDataManager:
 
         reasons = _DataManager._can_create(dn_config)
         assert bool(reasons) is False
-        assert reasons._reasons == {dn_config.id: {'Data node config "dn" does not have GLOBAL scope'}}
+        assert isinstance(list(reasons._reasons[dn_config.id])[0], NotGlobalScope)
+        assert list(reasons._reasons[dn_config.id])[0].reason == 'Data node config "dn" does not have GLOBAL scope'
 
         reasons = _DataManager._can_create(1)
         assert bool(reasons) is False
-        assert reasons._reasons == {"1": {'Object "1" must be a valid DataNodeConfig'}}
+        assert isinstance(list(reasons._reasons["1"])[0], WrongConfigType)
+        assert list(reasons._reasons["1"])[0].reason == 'Object "1" must be a valid DataNodeConfig'
 
     def test_create_data_node_with_name_provided(self):
         dn_config = Config.configure_data_node(id="dn", foo="bar", name="acb")

+ 5 - 2
tests/core/scenario/test_scenario_manager.py

@@ -39,6 +39,7 @@ from taipy.core.exceptions.exceptions import (
     UnauthorizedTagError,
 )
 from taipy.core.job._job_manager import _JobManager
+from taipy.core.reason import WrongConfigType
 from taipy.core.scenario._scenario_manager import _ScenarioManager
 from taipy.core.scenario._scenario_manager_factory import _ScenarioManagerFactory
 from taipy.core.scenario.scenario import Scenario
@@ -381,13 +382,15 @@ def test_can_create():
 
     reasons = _ScenarioManager._can_create(task_config)
     assert bool(reasons) is False
-    assert reasons._reasons == {task_config.id: {'Object "task" must be a valid ScenarioConfig'}}
+    assert isinstance(list(reasons._reasons[task_config.id])[0], WrongConfigType)
+    assert list(reasons._reasons[task_config.id])[0].reason == 'Object "task" must be a valid ScenarioConfig'
     with pytest.raises(AttributeError):
         _ScenarioManager._create(task_config)
 
     reasons = _ScenarioManager._can_create(1)
     assert bool(reasons) is False
-    assert reasons._reasons == {"1": {'Object "1" must be a valid ScenarioConfig'}}
+    assert isinstance(list(reasons._reasons["1"])[0], WrongConfigType)
+    assert list(reasons._reasons["1"])[0].reason == 'Object "1" must be a valid ScenarioConfig'
     with pytest.raises(AttributeError):
         _ScenarioManager._create(1)