test_data_node.py 27 KB

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