test_data_node.py 27 KB

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