pickle.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. # Copyright 2021-2024 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 os
  12. import pickle
  13. from datetime import datetime, timedelta
  14. from typing import Any, List, Optional, Set
  15. from taipy.config.common.scope import Scope
  16. from .._backup._backup import _replace_in_backup_file
  17. from .._entity._reload import _self_reload
  18. from .._version._version_manager_factory import _VersionManagerFactory
  19. from ._abstract_file import _AbstractFileDataNode
  20. from .data_node import DataNode
  21. from .data_node_id import DataNodeId, Edit
  22. class PickleDataNode(DataNode, _AbstractFileDataNode):
  23. """Data Node stored as a pickle file.
  24. Attributes:
  25. config_id (str): Identifier of the data node configuration. It must be a valid Python
  26. identifier.
  27. scope (Scope^): The scope of this data node.
  28. id (str): The unique identifier of this data node.
  29. owner_id (str): The identifier of the owner (sequence_id, scenario_id, cycle_id) or
  30. `None`.
  31. parent_ids (Optional[Set[str]]): The identifiers of the parent tasks or `None`.
  32. last_edit_date (datetime): The date and time of the last modification.
  33. edits (List[Edit^]): The ordered list of edits for that job.
  34. version (str): The string indicates the application version of the data node to instantiate. If not provided,
  35. the current version is used.
  36. validity_period (Optional[timedelta]): The duration implemented as a timedelta since the last edit date for
  37. which the data node can be considered up-to-date. Once the validity period has passed, the data node is
  38. considered stale and relevant tasks will run even if they are skippable (see the
  39. [Task management page](../core/entities/task-mgt.md) for more details).
  40. If _validity_period_ is set to `None`, the data node is always up-to-date.
  41. edit_in_progress (bool): True if a task computing the data node has been submitted
  42. and not completed yet. False otherwise.
  43. editor_id (Optional[str]): The identifier of the user who is currently editing the data node.
  44. editor_expiration_date (Optional[datetime]): The expiration date of the editor lock.
  45. properties (dict[str, Any]): A dictionary of additional properties.
  46. When creating a pickle data node, if the _properties_ dictionary contains a
  47. _"default_data"_ entry, the data node is automatically written with the corresponding
  48. _"default_data"_ value.
  49. If the _properties_ dictionary contains a _"default_path"_ or _"path"_ entry, the data will be stored
  50. using the corresponding value as the name of the pickle file.
  51. """
  52. __STORAGE_TYPE = "pickle"
  53. __PATH_KEY = "path"
  54. __DEFAULT_PATH_KEY = "default_path"
  55. __DEFAULT_DATA_KEY = "default_data"
  56. __IS_GENERATED_KEY = "is_generated"
  57. _REQUIRED_PROPERTIES: List[str] = []
  58. def __init__(
  59. self,
  60. config_id: str,
  61. scope: Scope,
  62. id: Optional[DataNodeId] = None,
  63. owner_id: Optional[str] = None,
  64. parent_ids: Optional[Set[str]] = None,
  65. last_edit_date: Optional[datetime] = None,
  66. edits: Optional[List[Edit]] = None,
  67. version: str = None,
  68. validity_period: Optional[timedelta] = None,
  69. edit_in_progress: bool = False,
  70. editor_id: Optional[str] = None,
  71. editor_expiration_date: Optional[datetime] = None,
  72. properties=None,
  73. ):
  74. if properties is None:
  75. properties = {}
  76. default_value = properties.pop(self.__DEFAULT_DATA_KEY, None)
  77. self._path = properties.get(self.__PATH_KEY, properties.get(self.__DEFAULT_PATH_KEY))
  78. if self._path is not None:
  79. properties[self.__PATH_KEY] = self._path
  80. self._is_generated = properties.get(self.__IS_GENERATED_KEY, self._path is None)
  81. properties[self.__IS_GENERATED_KEY] = self._is_generated
  82. super().__init__(
  83. config_id,
  84. scope,
  85. id,
  86. owner_id,
  87. parent_ids,
  88. last_edit_date,
  89. edits,
  90. version or _VersionManagerFactory._build_manager()._get_latest_version(),
  91. validity_period,
  92. edit_in_progress,
  93. editor_id,
  94. editor_expiration_date,
  95. **properties,
  96. )
  97. if self._path is None:
  98. self._path = self._build_path(self.storage_type())
  99. if not self._last_edit_date and os.path.exists(self._path):
  100. self._last_edit_date = datetime.now()
  101. if default_value is not None and not os.path.exists(self._path):
  102. self._write(default_value)
  103. self._last_edit_date = datetime.now()
  104. self._edits.append(
  105. Edit(
  106. {
  107. "timestamp": self._last_edit_date,
  108. "writer_identifier": "TAIPY",
  109. "comments": "Default data written.",
  110. }
  111. )
  112. )
  113. self._TAIPY_PROPERTIES.update(
  114. {
  115. self.__PATH_KEY,
  116. self.__DEFAULT_PATH_KEY,
  117. self.__DEFAULT_DATA_KEY,
  118. self.__IS_GENERATED_KEY,
  119. }
  120. )
  121. @classmethod
  122. def storage_type(cls) -> str:
  123. return cls.__STORAGE_TYPE
  124. @property # type: ignore
  125. @_self_reload(DataNode._MANAGER_NAME)
  126. def path(self) -> Any:
  127. return self._path
  128. @path.setter
  129. def path(self, value):
  130. tmp_old_path = self._path
  131. self._path = value
  132. self.properties[self.__PATH_KEY] = value
  133. self.properties[self.__IS_GENERATED_KEY] = False
  134. _replace_in_backup_file(old_file_path=tmp_old_path, new_file_path=self._path)
  135. @property # type: ignore
  136. @_self_reload(DataNode._MANAGER_NAME)
  137. def is_generated(self) -> bool:
  138. return self._is_generated
  139. def _read(self):
  140. with open(self._path, "rb") as pf:
  141. return pickle.load(pf)
  142. def _write(self, data):
  143. with open(self._path, "wb") as pf:
  144. pickle.dump(data, pf)