test_data_node.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778
  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. from datetime import datetime, timedelta
  13. from time import sleep
  14. from unittest import mock
  15. import pytest
  16. import taipy.core as tp
  17. from taipy.config import Config
  18. from taipy.config.common.scope import Scope
  19. from taipy.config.exceptions.exceptions import InvalidConfigurationId
  20. from taipy.core.data._data_manager import _DataManager
  21. from taipy.core.data._data_manager_factory import _DataManagerFactory
  22. from taipy.core.data.data_node import DataNode
  23. from taipy.core.data.data_node_id import DataNodeId
  24. from taipy.core.data.in_memory import InMemoryDataNode
  25. from taipy.core.exceptions.exceptions import AttributeKeyAlreadyExisted, DataNodeIsBeingEdited, NoData
  26. from taipy.core.job.job_id import JobId
  27. from taipy.core.task.task import Task
  28. from .utils import FakeDataNode
  29. def funct_a_b(input: str):
  30. print("task_a_b") # noqa: T201
  31. return "B"
  32. def funct_b_c(input: str):
  33. print("task_b_c") # noqa: T201
  34. return "C"
  35. def funct_b_d(input: str):
  36. print("task_b_d") # noqa: T201
  37. return "D"
  38. class TestDataNode:
  39. def test_dn_equals(self, data_node):
  40. data_manager = _DataManagerFactory()._build_manager()
  41. dn_id = data_node.id
  42. data_manager._set(data_node)
  43. # # To test if instance is same type
  44. task = Task("task", {}, print, [], [], dn_id)
  45. dn_2 = data_manager._get(dn_id)
  46. assert data_node == dn_2
  47. assert data_node != dn_id
  48. assert data_node != task
  49. def test_create_with_default_values(self):
  50. dn = DataNode("foo_bar")
  51. assert dn.config_id == "foo_bar"
  52. assert dn.scope == Scope.SCENARIO
  53. assert dn.id is not None
  54. assert dn.name is None
  55. assert dn.owner_id is None
  56. assert dn.parent_ids == set()
  57. assert dn.last_edit_date is None
  58. assert dn.job_ids == []
  59. assert not dn.is_ready_for_reading
  60. assert len(dn.properties) == 0
  61. def test_is_up_to_date_when_not_written(self):
  62. dn_confg_1 = Config.configure_in_memory_data_node("dn_1", default_data="a")
  63. dn_confg_2 = Config.configure_in_memory_data_node("dn_2")
  64. task_config_1 = Config.configure_task("t1", funct_a_b, [dn_confg_1], [dn_confg_2])
  65. scenario_config = Config.configure_scenario("sc", [task_config_1])
  66. scenario = tp.create_scenario(scenario_config)
  67. assert scenario.dn_1.is_up_to_date is True
  68. assert scenario.dn_2.is_up_to_date is False
  69. tp.submit(scenario)
  70. assert scenario.dn_1.is_up_to_date is True
  71. assert scenario.dn_2.is_up_to_date is True
  72. def test_create(self):
  73. a_date = datetime.now()
  74. dn = DataNode(
  75. "foo_bar",
  76. Scope.SCENARIO,
  77. DataNodeId("an_id"),
  78. "a_scenario_id",
  79. {"a_parent_id"},
  80. a_date,
  81. [{"job_id": "a_job_id"}],
  82. edit_in_progress=False,
  83. prop="erty",
  84. name="a name",
  85. )
  86. assert dn.config_id == "foo_bar"
  87. assert dn.scope == Scope.SCENARIO
  88. assert dn.id == "an_id"
  89. assert dn.name == "a name"
  90. assert dn.owner_id == "a_scenario_id"
  91. assert dn.parent_ids == {"a_parent_id"}
  92. assert dn.last_edit_date == a_date
  93. assert dn.job_ids == ["a_job_id"]
  94. assert dn.is_ready_for_reading
  95. assert len(dn.properties) == 2
  96. assert dn.properties == {"prop": "erty", "name": "a name"}
  97. with pytest.raises(InvalidConfigurationId):
  98. DataNode("foo bar")
  99. def test_get_set_property_and_attribute(self):
  100. dn_cfg = Config.configure_data_node("bar", key="value")
  101. dn = _DataManager._create_and_set(dn_cfg, "", "")
  102. assert "key" in dn.properties.keys()
  103. assert dn.key == "value"
  104. dn.properties["new_key"] = "new_value"
  105. dn.another_key = "another_value"
  106. assert dn.key == "value"
  107. assert dn.new_key == "new_value"
  108. assert dn.another_key == "another_value"
  109. assert dn.properties["new_key"] == "new_value"
  110. with pytest.raises(AttributeKeyAlreadyExisted):
  111. dn.key = "KeyAlreadyUsed"
  112. def test_read_write(self):
  113. dn = FakeDataNode("foo_bar")
  114. with pytest.raises(NoData):
  115. assert dn.read() is None
  116. dn.read_or_raise()
  117. assert dn.write_has_been_called == 0
  118. assert dn.read_has_been_called == 0
  119. assert not dn.is_ready_for_reading
  120. assert dn.last_edit_date is None
  121. assert dn.job_ids == []
  122. assert dn.edits == []
  123. dn.write("Any data")
  124. assert dn.write_has_been_called == 1
  125. assert dn.read_has_been_called == 0
  126. assert dn.last_edit_date is not None
  127. first_edition = dn.last_edit_date
  128. assert dn.is_ready_for_reading
  129. assert dn.job_ids == []
  130. assert len(dn.edits) == 1
  131. assert dn.get_last_edit()["timestamp"] == dn.last_edit_date
  132. sleep(0.1)
  133. dn.write("Any other data", job_id := JobId("a_job_id"))
  134. assert dn.write_has_been_called == 2
  135. assert dn.read_has_been_called == 0
  136. second_edition = dn.last_edit_date
  137. assert first_edition < second_edition
  138. assert dn.is_ready_for_reading
  139. assert dn.job_ids == [job_id]
  140. assert len(dn.edits) == 2
  141. assert dn.get_last_edit()["timestamp"] == dn.last_edit_date
  142. dn.read()
  143. assert dn.write_has_been_called == 2
  144. assert dn.read_has_been_called == 1
  145. second_edition = dn.last_edit_date
  146. assert first_edition < second_edition
  147. assert dn.is_ready_for_reading
  148. assert dn.job_ids == [job_id]
  149. def test_lock_initialization(self):
  150. dn = InMemoryDataNode("dn", Scope.SCENARIO)
  151. assert not dn.edit_in_progress
  152. assert dn._editor_id is None
  153. assert dn._editor_expiration_date is None
  154. def test_locked_dn_unlockable_only_by_same_editor(self):
  155. dn = InMemoryDataNode("dn", Scope.SCENARIO)
  156. dn.lock_edit("user_1")
  157. assert dn.edit_in_progress
  158. assert dn._editor_id == "user_1"
  159. assert dn._editor_expiration_date is not None
  160. with pytest.raises(DataNodeIsBeingEdited):
  161. dn.lock_edit("user_2")
  162. with pytest.raises(DataNodeIsBeingEdited):
  163. dn.unlock_edit("user_2")
  164. dn.unlock_edit("user_1")
  165. assert not dn.edit_in_progress
  166. assert dn._editor_id is None
  167. assert dn._editor_expiration_date is None
  168. def test_none_editor_can_lock_a_locked_dn(self):
  169. dn = InMemoryDataNode("dn", Scope.SCENARIO)
  170. dn.lock_edit("user")
  171. assert dn.edit_in_progress
  172. assert dn._editor_id == "user"
  173. assert dn._editor_expiration_date is not None
  174. dn.lock_edit()
  175. assert dn.edit_in_progress
  176. assert dn._editor_id is None
  177. assert dn._editor_expiration_date is None
  178. def test_none_editor_can_unlock_a_locked_dn(self):
  179. dn = InMemoryDataNode("dn", Scope.SCENARIO)
  180. dn.lock_edit("user")
  181. assert dn.edit_in_progress
  182. assert dn._editor_id == "user"
  183. assert dn._editor_expiration_date is not None
  184. dn.unlock_edit()
  185. assert not dn.edit_in_progress
  186. assert dn._editor_id is None
  187. assert dn._editor_expiration_date is None
  188. dn.lock_edit()
  189. assert dn.edit_in_progress
  190. assert dn._editor_id is None
  191. assert dn._editor_expiration_date is None
  192. dn.unlock_edit()
  193. assert not dn.edit_in_progress
  194. assert dn._editor_id is None
  195. assert dn._editor_expiration_date is None
  196. def test_ready_for_reading(self):
  197. dn = InMemoryDataNode("foo_bar", Scope.CYCLE)
  198. assert dn.last_edit_date is None
  199. assert not dn.is_ready_for_reading
  200. assert dn.job_ids == []
  201. dn.lock_edit()
  202. assert dn.last_edit_date is None
  203. assert not dn.is_ready_for_reading
  204. assert dn.job_ids == []
  205. dn.unlock_edit()
  206. assert dn.last_edit_date is None
  207. assert not dn.is_ready_for_reading
  208. assert dn.job_ids == []
  209. dn.lock_edit()
  210. assert dn.last_edit_date is None
  211. assert not dn.is_ready_for_reading
  212. assert dn.job_ids == []
  213. dn.write("toto", job_id := JobId("a_job_id"))
  214. assert dn.last_edit_date is not None
  215. assert dn.is_ready_for_reading
  216. assert dn.job_ids == [job_id]
  217. def test_is_valid_no_validity_period(self):
  218. # Test Never been written
  219. dn = InMemoryDataNode("foo", Scope.SCENARIO, DataNodeId("id"), "name", "owner_id")
  220. assert not dn.is_valid
  221. # test has been written
  222. dn.write("My data")
  223. assert dn.is_valid
  224. def test_is_valid_with_30_min_validity_period(self):
  225. # Test Never been written
  226. dn = InMemoryDataNode(
  227. "foo", Scope.SCENARIO, DataNodeId("id"), "name", "owner_id", validity_period=timedelta(minutes=30)
  228. )
  229. assert dn.is_valid is False
  230. # Has been written less than 30 minutes ago
  231. dn.write("My data")
  232. assert dn.is_valid is True
  233. # Has been written more than 30 minutes ago
  234. dn.last_edit_date = datetime.now() + timedelta(days=-1)
  235. assert dn.is_valid is False
  236. def test_is_valid_with_5_days_validity_period(self):
  237. # Test Never been written
  238. dn = InMemoryDataNode("foo", Scope.SCENARIO, validity_period=timedelta(days=5))
  239. assert dn.is_valid is False
  240. # Has been written less than 30 minutes ago
  241. dn.write("My data")
  242. assert dn.is_valid is True
  243. # Has been written more than 30 minutes ago
  244. dn._last_edit_date = datetime.now() - timedelta(days=6)
  245. _DataManager()._set(dn)
  246. assert dn.is_valid is False
  247. def test_is_up_to_date(self, current_datetime):
  248. dn_confg_1 = Config.configure_in_memory_data_node("dn_1")
  249. dn_confg_2 = Config.configure_in_memory_data_node("dn_2")
  250. dn_confg_3 = Config.configure_in_memory_data_node("dn_3", scope=Scope.GLOBAL)
  251. task_config_1 = Config.configure_task("t1", print, [dn_confg_1], [dn_confg_2])
  252. task_config_2 = Config.configure_task("t2", print, [dn_confg_2], [dn_confg_3])
  253. scenario_config = Config.configure_scenario("sc", [task_config_1, task_config_2])
  254. scenario_1 = tp.create_scenario(scenario_config)
  255. assert len(_DataManager._get_all()) == 3
  256. dn_1_1 = scenario_1.data_nodes["dn_1"]
  257. dn_2_1 = scenario_1.data_nodes["dn_2"]
  258. dn_3_1 = scenario_1.data_nodes["dn_3"]
  259. assert dn_1_1.last_edit_date is None
  260. assert dn_2_1.last_edit_date is None
  261. assert dn_3_1.last_edit_date is None
  262. dn_1_1.last_edit_date = current_datetime + timedelta(1)
  263. dn_2_1.last_edit_date = current_datetime + timedelta(2)
  264. dn_3_1.last_edit_date = current_datetime + timedelta(3)
  265. assert dn_1_1.is_up_to_date
  266. assert dn_2_1.is_up_to_date
  267. assert dn_3_1.is_up_to_date
  268. dn_2_1.last_edit_date = current_datetime + timedelta(4)
  269. assert dn_1_1.is_up_to_date
  270. assert dn_2_1.is_up_to_date
  271. assert not dn_3_1.is_up_to_date
  272. dn_1_1.last_edit_date = current_datetime + timedelta(5)
  273. assert dn_1_1.is_up_to_date
  274. assert not dn_2_1.is_up_to_date
  275. assert not dn_3_1.is_up_to_date
  276. dn_1_1.last_edit_date = current_datetime + timedelta(1)
  277. dn_2_1.last_edit_date = current_datetime + timedelta(2)
  278. dn_3_1.last_edit_date = current_datetime + timedelta(3)
  279. def test_is_up_to_date_across_scenarios(self, current_datetime):
  280. dn_confg_1 = Config.configure_in_memory_data_node("dn_1", scope=Scope.SCENARIO)
  281. dn_confg_2 = Config.configure_in_memory_data_node("dn_2", scope=Scope.SCENARIO)
  282. dn_confg_3 = Config.configure_in_memory_data_node("dn_3", scope=Scope.GLOBAL)
  283. task_config_1 = Config.configure_task("t1", print, [dn_confg_1], [dn_confg_2])
  284. task_config_2 = Config.configure_task("t2", print, [dn_confg_2], [dn_confg_3])
  285. scenario_config = Config.configure_scenario("sc", [task_config_1, task_config_2])
  286. scenario_1 = tp.create_scenario(scenario_config)
  287. scenario_2 = tp.create_scenario(scenario_config)
  288. assert len(_DataManager._get_all()) == 5
  289. dn_1_1 = scenario_1.data_nodes["dn_1"]
  290. dn_2_1 = scenario_1.data_nodes["dn_2"]
  291. dn_1_2 = scenario_2.data_nodes["dn_1"]
  292. dn_2_2 = scenario_2.data_nodes["dn_2"]
  293. dn_3 = scenario_1.data_nodes["dn_3"]
  294. assert dn_3 == scenario_2.data_nodes["dn_3"]
  295. assert dn_1_1.last_edit_date is None
  296. assert dn_2_1.last_edit_date is None
  297. assert dn_1_2.last_edit_date is None
  298. assert dn_2_2.last_edit_date is None
  299. assert dn_3.last_edit_date is None
  300. dn_1_1.last_edit_date = current_datetime + timedelta(1)
  301. dn_2_1.last_edit_date = current_datetime + timedelta(2)
  302. dn_1_2.last_edit_date = current_datetime + timedelta(3)
  303. dn_2_2.last_edit_date = current_datetime + timedelta(4)
  304. dn_3.last_edit_date = current_datetime + timedelta(5)
  305. assert dn_1_1.is_up_to_date
  306. assert dn_2_1.is_up_to_date
  307. assert dn_1_2.is_up_to_date
  308. assert dn_2_2.is_up_to_date
  309. assert dn_3.is_up_to_date
  310. dn_2_1.last_edit_date = current_datetime + timedelta(6)
  311. assert dn_1_1.is_up_to_date
  312. assert dn_2_1.is_up_to_date
  313. assert dn_1_2.is_up_to_date
  314. assert dn_2_2.is_up_to_date
  315. assert not dn_3.is_up_to_date
  316. dn_2_1.last_edit_date = current_datetime + timedelta(2)
  317. dn_2_2.last_edit_date = current_datetime + timedelta(6)
  318. assert dn_1_1.is_up_to_date
  319. assert dn_2_1.is_up_to_date
  320. assert dn_1_2.is_up_to_date
  321. assert dn_2_2.is_up_to_date
  322. assert not dn_3.is_up_to_date
  323. dn_2_2.last_edit_date = current_datetime + timedelta(4)
  324. dn_1_1.last_edit_date = current_datetime + timedelta(6)
  325. assert dn_1_1.is_up_to_date
  326. assert not dn_2_1.is_up_to_date
  327. assert dn_1_2.is_up_to_date
  328. assert dn_2_2.is_up_to_date
  329. assert not dn_3.is_up_to_date
  330. dn_1_2.last_edit_date = current_datetime + timedelta(6)
  331. assert dn_1_1.is_up_to_date
  332. assert not dn_2_1.is_up_to_date
  333. assert dn_1_2.is_up_to_date
  334. assert not dn_2_2.is_up_to_date
  335. assert not dn_3.is_up_to_date
  336. def test_do_not_recompute_data_node_valid_but_continue_sequence_execution(self):
  337. a = Config.configure_data_node("A", "pickle", default_data="A")
  338. b = Config.configure_data_node("B", "pickle")
  339. c = Config.configure_data_node("C", "pickle")
  340. d = Config.configure_data_node("D", "pickle")
  341. task_a_b = Config.configure_task("task_a_b", funct_a_b, input=a, output=b, skippable=True)
  342. task_b_c = Config.configure_task("task_b_c", funct_b_c, input=b, output=c)
  343. task_b_d = Config.configure_task("task_b_d", funct_b_d, input=b, output=d)
  344. scenario_cfg = Config.configure_scenario("scenario", [task_a_b, task_b_c, task_b_d])
  345. scenario = tp.create_scenario(scenario_cfg)
  346. scenario.submit()
  347. assert scenario.A.read() == "A"
  348. assert scenario.B.read() == "B"
  349. assert scenario.C.read() == "C"
  350. assert scenario.D.read() == "D"
  351. scenario.submit()
  352. assert len(tp.get_jobs()) == 6
  353. jobs_and_status = [(job.task.config_id, job.status) for job in tp.get_jobs()]
  354. assert ("task_a_b", tp.Status.COMPLETED) in jobs_and_status
  355. assert ("task_a_b", tp.Status.SKIPPED) in jobs_and_status
  356. assert ("task_b_c", tp.Status.COMPLETED) in jobs_and_status
  357. assert ("task_b_d", tp.Status.COMPLETED) in jobs_and_status
  358. def test_data_node_update_after_writing(self):
  359. dn = FakeDataNode("foo")
  360. _DataManager._set(dn)
  361. assert not _DataManager._get(dn.id).is_ready_for_reading
  362. dn.write("Any data")
  363. assert dn.is_ready_for_reading
  364. assert _DataManager._get(dn.id).is_ready_for_reading
  365. def test_expiration_date_raise_if_never_write(self):
  366. dn = FakeDataNode("foo")
  367. with pytest.raises(NoData):
  368. _ = dn.expiration_date
  369. def test_validity_null_if_never_write(self):
  370. dn = FakeDataNode("foo")
  371. assert dn.validity_period is None
  372. def test_auto_set_and_reload(self, current_datetime):
  373. dn_1 = InMemoryDataNode(
  374. "foo",
  375. scope=Scope.GLOBAL,
  376. id=DataNodeId("an_id"),
  377. owner_id=None,
  378. parent_ids=None,
  379. last_edit_date=current_datetime,
  380. edits=[{"job_id": "a_job_id"}],
  381. edit_in_progress=False,
  382. validity_period=None,
  383. properties={
  384. "name": "foo",
  385. },
  386. )
  387. dm = _DataManager()
  388. dm._set(dn_1)
  389. dn_2 = dm._get(dn_1)
  390. # auto set & reload on scope attribute
  391. assert dn_1.scope == Scope.GLOBAL
  392. assert dn_2.scope == Scope.GLOBAL
  393. dn_1.scope = Scope.CYCLE
  394. assert dn_1.scope == Scope.CYCLE
  395. assert dn_2.scope == Scope.CYCLE
  396. dn_2.scope = Scope.SCENARIO
  397. assert dn_1.scope == Scope.SCENARIO
  398. assert dn_2.scope == Scope.SCENARIO
  399. new_datetime = current_datetime + timedelta(1)
  400. new_datetime_1 = current_datetime + timedelta(3)
  401. # auto set & reload on last_edit_date attribute
  402. assert dn_1.last_edit_date == current_datetime
  403. assert dn_2.last_edit_date == current_datetime
  404. dn_1.last_edit_date = new_datetime_1
  405. assert dn_1.last_edit_date == new_datetime_1
  406. assert dn_2.last_edit_date == new_datetime_1
  407. dn_2.last_edit_date = new_datetime
  408. assert dn_1.last_edit_date == new_datetime
  409. assert dn_2.last_edit_date == new_datetime
  410. # auto set & reload on name attribute
  411. assert dn_1.name == "foo"
  412. assert dn_2.name == "foo"
  413. dn_1.name = "fed"
  414. assert dn_1.name == "fed"
  415. assert dn_2.name == "fed"
  416. dn_2.name = "def"
  417. assert dn_1.name == "def"
  418. assert dn_2.name == "def"
  419. # auto set & reload on parent_ids attribute (set() object does not have auto set yet)
  420. assert dn_1.parent_ids == set()
  421. assert dn_2.parent_ids == set()
  422. dn_1._parent_ids.update(["sc2"])
  423. _DataManager._set(dn_1)
  424. assert dn_1.parent_ids == {"sc2"}
  425. assert dn_2.parent_ids == {"sc2"}
  426. dn_2._parent_ids.clear()
  427. dn_2._parent_ids.update(["sc1"])
  428. _DataManager._set(dn_2)
  429. assert dn_1.parent_ids == {"sc1"}
  430. assert dn_2.parent_ids == {"sc1"}
  431. dn_2._parent_ids.clear()
  432. _DataManager._set(dn_2)
  433. # auto set & reload on edit_in_progress attribute
  434. assert not dn_2.edit_in_progress
  435. assert not dn_1.edit_in_progress
  436. dn_1.edit_in_progress = True
  437. assert dn_1.edit_in_progress
  438. assert dn_2.edit_in_progress
  439. dn_2.unlock_edit()
  440. assert not dn_1.edit_in_progress
  441. assert not dn_2.edit_in_progress
  442. dn_1.lock_edit()
  443. assert dn_1.edit_in_progress
  444. assert dn_2.edit_in_progress
  445. # auto set & reload on validity_period attribute
  446. time_period_1 = timedelta(1)
  447. time_period_2 = timedelta(5)
  448. assert dn_1.validity_period is None
  449. assert dn_2.validity_period is None
  450. dn_1.validity_period = time_period_1
  451. assert dn_1.validity_period == time_period_1
  452. assert dn_2.validity_period == time_period_1
  453. dn_2.validity_period = time_period_2
  454. assert dn_1.validity_period == time_period_2
  455. assert dn_2.validity_period == time_period_2
  456. dn_1.last_edit_date = new_datetime
  457. assert len(dn_1.job_ids) == 1
  458. assert len(dn_2.job_ids) == 1
  459. with dn_1 as dn:
  460. assert dn.config_id == "foo"
  461. assert dn.owner_id is None
  462. assert dn.scope == Scope.SCENARIO
  463. assert dn.last_edit_date == new_datetime
  464. assert dn.name == "def"
  465. assert dn.edit_in_progress
  466. assert dn.validity_period == time_period_2
  467. assert len(dn.job_ids) == 1
  468. assert dn._is_in_context
  469. new_datetime_2 = new_datetime + timedelta(5)
  470. dn.scope = Scope.CYCLE
  471. dn.last_edit_date = new_datetime_2
  472. dn.name = "abc"
  473. dn.edit_in_progress = False
  474. dn.validity_period = None
  475. assert dn.config_id == "foo"
  476. assert dn.owner_id is None
  477. assert dn.scope == Scope.SCENARIO
  478. assert dn.last_edit_date == new_datetime
  479. assert dn.name == "def"
  480. assert dn.edit_in_progress
  481. assert dn.validity_period == time_period_2
  482. assert len(dn.job_ids) == 1
  483. assert dn_1.config_id == "foo"
  484. assert dn_1.owner_id is None
  485. assert dn_1.scope == Scope.CYCLE
  486. assert dn_1.last_edit_date == new_datetime_2
  487. assert dn_1.name == "abc"
  488. assert not dn_1.edit_in_progress
  489. assert dn_1.validity_period is None
  490. assert not dn_1._is_in_context
  491. assert len(dn_1.job_ids) == 1
  492. def test_auto_set_and_reload_properties(self):
  493. dn_1 = InMemoryDataNode("foo", scope=Scope.GLOBAL, properties={"name": "def"})
  494. dm = _DataManager()
  495. dm._set(dn_1)
  496. dn_2 = dm._get(dn_1)
  497. # auto set & reload on properties attribute
  498. assert dn_1.properties == {"name": "def"}
  499. assert dn_2.properties == {"name": "def"}
  500. dn_1._properties["qux"] = 4
  501. assert dn_1.properties["qux"] == 4
  502. assert dn_2.properties["qux"] == 4
  503. assert dn_1.properties == {"qux": 4, "name": "def"}
  504. assert dn_2.properties == {"qux": 4, "name": "def"}
  505. dn_2._properties["qux"] = 5
  506. assert dn_1.properties["qux"] == 5
  507. assert dn_2.properties["qux"] == 5
  508. dn_1.properties["temp_key_1"] = "temp_value_1"
  509. dn_1.properties["temp_key_2"] = "temp_value_2"
  510. assert dn_1.properties == {
  511. "name": "def",
  512. "qux": 5,
  513. "temp_key_1": "temp_value_1",
  514. "temp_key_2": "temp_value_2",
  515. }
  516. assert dn_2.properties == {"name": "def", "qux": 5, "temp_key_1": "temp_value_1", "temp_key_2": "temp_value_2"}
  517. dn_1.properties.pop("temp_key_1")
  518. assert "temp_key_1" not in dn_1.properties.keys()
  519. assert "temp_key_1" not in dn_1.properties.keys()
  520. assert dn_1.properties == {"name": "def", "qux": 5, "temp_key_2": "temp_value_2"}
  521. assert dn_2.properties == {"name": "def", "qux": 5, "temp_key_2": "temp_value_2"}
  522. dn_2.properties.pop("temp_key_2")
  523. assert dn_1.properties == {"qux": 5, "name": "def"}
  524. assert dn_2.properties == {"qux": 5, "name": "def"}
  525. assert "temp_key_2" not in dn_1.properties.keys()
  526. assert "temp_key_2" not in dn_2.properties.keys()
  527. dn_1.properties["temp_key_3"] = 0
  528. assert dn_1.properties == {"qux": 5, "temp_key_3": 0, "name": "def"}
  529. assert dn_2.properties == {"qux": 5, "temp_key_3": 0, "name": "def"}
  530. dn_1.properties.update({"temp_key_3": 1})
  531. assert dn_1.properties == {"qux": 5, "temp_key_3": 1, "name": "def"}
  532. assert dn_2.properties == {"qux": 5, "temp_key_3": 1, "name": "def"}
  533. dn_1.properties.update({})
  534. assert dn_1.properties == {"qux": 5, "temp_key_3": 1, "name": "def"}
  535. assert dn_2.properties == {"qux": 5, "temp_key_3": 1, "name": "def"}
  536. dn_1.properties["temp_key_4"] = 0
  537. dn_1.properties["temp_key_5"] = 0
  538. with dn_1 as dn:
  539. assert dn._is_in_context
  540. assert dn.properties["qux"] == 5
  541. assert dn.properties["temp_key_3"] == 1
  542. assert dn.properties["temp_key_4"] == 0
  543. assert dn.properties["temp_key_5"] == 0
  544. dn.properties["qux"] = 9
  545. dn.properties.pop("temp_key_3")
  546. dn.properties.pop("temp_key_4")
  547. dn.properties.update({"temp_key_4": 1})
  548. dn.properties.update({"temp_key_5": 2})
  549. dn.properties.pop("temp_key_5")
  550. dn.properties.update({})
  551. assert dn.properties["qux"] == 5
  552. assert dn.properties["temp_key_3"] == 1
  553. assert dn.properties["temp_key_4"] == 0
  554. assert dn.properties["temp_key_5"] == 0
  555. assert not dn_1._is_in_context
  556. assert dn_1.properties["qux"] == 9
  557. assert "temp_key_3" not in dn_1.properties.keys()
  558. assert dn_1.properties["temp_key_4"] == 1
  559. assert "temp_key_5" not in dn_1.properties.keys()
  560. def test_get_parents(self, data_node):
  561. with mock.patch("taipy.core.get_parents") as mck:
  562. data_node.get_parents()
  563. mck.assert_called_once_with(data_node)
  564. def test_cacheable_deprecated_false(self):
  565. dn = FakeDataNode("foo")
  566. with pytest.warns(DeprecationWarning):
  567. _ = dn.cacheable
  568. assert dn.cacheable is False
  569. def test_cacheable_deprecated_true(self):
  570. dn = FakeDataNode("foo", properties={"cacheable": True})
  571. with pytest.warns(DeprecationWarning):
  572. _ = dn.cacheable
  573. assert dn.cacheable is True
  574. def test_data_node_with_env_variable_value_not_stored(self):
  575. dn_config = Config.configure_data_node("A", prop="ENV[FOO]")
  576. with mock.patch.dict(os.environ, {"FOO": "bar"}):
  577. dn = _DataManager._bulk_get_or_create([dn_config])[dn_config]
  578. assert dn._properties.data["prop"] == "ENV[FOO]"
  579. assert dn.properties["prop"] == "bar"
  580. assert dn.prop == "bar"
  581. def test_path_populated_with_config_default_path(self):
  582. dn_config = Config.configure_data_node("data_node", "pickle", default_path="foo.p")
  583. assert dn_config.default_path == "foo.p"
  584. data_node = _DataManager._bulk_get_or_create([dn_config])[dn_config]
  585. assert data_node.path == "foo.p"
  586. data_node.path = "baz.p"
  587. assert data_node.path == "baz.p"
  588. def test_track_edit(self):
  589. dn_config = Config.configure_data_node("A")
  590. data_node = _DataManager._bulk_get_or_create([dn_config])[dn_config]
  591. data_node.write(data="1", job_id="job_1")
  592. data_node.write(data="2", job_id="job_1")
  593. data_node.write(data="3", job_id="job_1")
  594. assert len(data_node.edits) == 3
  595. assert len(data_node.job_ids) == 3
  596. assert data_node.edits[-1] == data_node.get_last_edit()
  597. assert data_node.last_edit_date == data_node.get_last_edit().get("timestamp")
  598. date = datetime(2050, 1, 1, 12, 12)
  599. data_node.write(data="4", timestamp=date, message="This is a comment on this edit", env="staging")
  600. assert len(data_node.edits) == 4
  601. assert len(data_node.job_ids) == 3
  602. assert data_node.edits[-1] == data_node.get_last_edit()
  603. last_edit = data_node.get_last_edit()
  604. assert last_edit["message"] == "This is a comment on this edit"
  605. assert last_edit["env"] == "staging"
  606. assert last_edit["timestamp"] == date
  607. def test_label(self):
  608. a_date = datetime.now()
  609. dn = DataNode(
  610. "foo_bar",
  611. Scope.SCENARIO,
  612. DataNodeId("an_id"),
  613. "a_scenario_id",
  614. {"a_parent_id"},
  615. a_date,
  616. [{"job_id": "a_job_id"}],
  617. edit_in_progress=False,
  618. prop="erty",
  619. name="a name",
  620. )
  621. with mock.patch("taipy.core.get") as get_mck:
  622. class MockOwner:
  623. label = "owner_label"
  624. def get_label(self):
  625. return self.label
  626. get_mck.return_value = MockOwner()
  627. assert dn.get_label() == "owner_label > " + dn.name
  628. assert dn.get_simple_label() == dn.name
  629. def test_explicit_label(self):
  630. a_date = datetime.now()
  631. dn = DataNode(
  632. "foo_bar",
  633. Scope.SCENARIO,
  634. DataNodeId("an_id"),
  635. "a_scenario_id",
  636. {"a_parent_id"},
  637. a_date,
  638. [{"job_id": "a_job_id"}],
  639. edit_in_progress=False,
  640. label="a label",
  641. name="a name",
  642. )
  643. assert dn.get_label() == "a label"
  644. assert dn.get_simple_label() == "a label"
  645. def test_change_data_node_name(self):
  646. cgf = Config.configure_data_node("foo", scope=Scope.GLOBAL)
  647. dn = tp.create_global_data_node(cgf)
  648. dn.name = "bar"
  649. assert dn.name == "bar"
  650. # This new syntax will be the only one allowed: https://github.com/Avaiga/taipy-core/issues/806
  651. dn.properties["name"] = "baz"
  652. assert dn.name == "baz"