test_task.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. # Copyright 2021-2025 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. from unittest import mock
  12. import pytest
  13. from taipy import Scope
  14. from taipy.common.config import Config
  15. from taipy.common.config.exceptions.exceptions import InvalidConfigurationId
  16. from taipy.core.config.data_node_config import DataNodeConfig
  17. from taipy.core.data._data_manager import _DataManager
  18. from taipy.core.data.csv import CSVDataNode
  19. from taipy.core.data.data_node import DataNode
  20. from taipy.core.data.in_memory import InMemoryDataNode
  21. from taipy.core.exceptions import AttributeKeyAlreadyExisted
  22. from taipy.core.scenario._scenario_manager import _ScenarioManager
  23. from taipy.core.task._task_manager import _TaskManager
  24. from taipy.core.task._task_manager_factory import _TaskManagerFactory
  25. from taipy.core.task.task import Task
  26. @pytest.fixture
  27. def output():
  28. return [DataNode("name_1"), DataNode("name_2"), DataNode("name_3")]
  29. @pytest.fixture
  30. def output_config():
  31. return [DataNodeConfig("name_1"), DataNodeConfig("name_2"), DataNodeConfig("name_3")]
  32. @pytest.fixture
  33. def input():
  34. return [DataNode("input_name_1"), DataNode("input_name_2"), DataNode("input_name_3")]
  35. @pytest.fixture
  36. def input_config():
  37. return [DataNodeConfig("input_name_1"), DataNodeConfig("input_name_2"), DataNodeConfig("input_name_3")]
  38. def test_task_equals(task):
  39. task_manager = _TaskManagerFactory()._build_manager()
  40. task_id = task.id
  41. task_manager._repository._save(task)
  42. # To test if instance is same type
  43. dn = CSVDataNode("foo_bar", Scope.SCENARIO, task_id)
  44. task_2 = task_manager._get(task_id)
  45. assert task == task_2
  46. assert task != task_id
  47. assert task != dn
  48. def test_create_task():
  49. name = "name_1"
  50. task = Task(name, {}, print, [], [])
  51. assert f"TASK_{name}_" in task.id
  52. assert task.config_id == "name_1"
  53. with pytest.raises(InvalidConfigurationId):
  54. Task("foo bar", {}, print, [], [])
  55. path = "my/csv/path"
  56. foo_dn = CSVDataNode("foo", Scope.SCENARIO, properties={"path": path, "has_header": True})
  57. task = Task("name_1", {}, print, [foo_dn], [])
  58. assert task.config_id == "name_1"
  59. assert task.id is not None
  60. assert task.owner_id is None
  61. assert task.parent_ids == set()
  62. assert task.foo == foo_dn
  63. assert task.foo.path == path
  64. with pytest.raises(AttributeError):
  65. _ = task.bar
  66. task = Task("name_1", {}, print, [foo_dn], [], parent_ids={"parent_id"})
  67. assert task.parent_ids == {"parent_id"}
  68. path = "my/csv/path"
  69. abc_dn = InMemoryDataNode("name_1ea", Scope.SCENARIO, properties={"path": path})
  70. task = Task("name_1ea", {}, print, [abc_dn], [], owner_id="owner_id", parent_ids={"parent_id_1", "parent_id_2"})
  71. assert task.config_id == "name_1ea"
  72. assert task.id is not None
  73. assert task.owner_id == "owner_id"
  74. assert task.parent_ids == {"parent_id_1", "parent_id_2"}
  75. assert task.name_1ea == abc_dn
  76. assert task.name_1ea.properties["path"] == path
  77. with pytest.raises(AttributeError):
  78. _ = task.bar
  79. with mock.patch("taipy.core.get") as get_mck:
  80. class MockOwner:
  81. label = "owner_label"
  82. def get_label(self):
  83. return self.label
  84. get_mck.return_value = MockOwner()
  85. assert task.get_label() == "owner_label > " + task.config_id
  86. assert task.get_simple_label() == task.config_id
  87. def test_get_set_attribute():
  88. dn_cfg = Config.configure_data_node("bar")
  89. task_config = Config.configure_task("print", print, [dn_cfg], None)
  90. scenario_config = Config.configure_scenario("scenario", [task_config])
  91. scenario = _ScenarioManager._create(scenario_config)
  92. task = scenario.tasks["print"]
  93. task.key = "value"
  94. assert task.key == "value"
  95. with pytest.raises(AttributeKeyAlreadyExisted):
  96. task.bar = "KeyAlreadyUsed"
  97. def test_can_not_change_task_output(output):
  98. task = Task("name_1", {}, print, output=output)
  99. with pytest.raises(AttributeError):
  100. task.output = {}
  101. assert list(task.output.values()) == output
  102. output.append(output[0])
  103. assert list(task.output.values()) != output
  104. def test_can_not_change_task_input(input):
  105. task = Task("name_1", {}, print, input=input)
  106. with pytest.raises(AttributeError):
  107. task.input = {}
  108. assert list(task.input.values()) == input
  109. input.append(input[0])
  110. assert list(task.input.values()) != input
  111. def test_can_not_change_task_config_output(output_config):
  112. task_config = Config.configure_task("name_1", print, [], output=output_config)
  113. assert task_config.output_configs == output_config
  114. with pytest.raises(AttributeError):
  115. task_config.output_configs = []
  116. output_config.append(output_config[0])
  117. assert task_config._output != output_config
  118. def test_can_not_update_task_output_values(output_config):
  119. data_node_cfg = Config.configure_data_node("data_node_cfg")
  120. task_config = Config.configure_task("name_1", print, [], output=output_config)
  121. task_config.output_configs.append(data_node_cfg)
  122. assert task_config.output_configs == output_config
  123. task_config.output_configs[0] = data_node_cfg
  124. assert task_config.output_configs[0] != data_node_cfg
  125. def test_can_not_update_task_input_values(input_config):
  126. data_node_config = DataNodeConfig("data_node")
  127. task_config = Config.configure_task("name_1", print, input=input_config, output=[])
  128. task_config.input_configs.append(data_node_config)
  129. assert task_config.input_configs == input_config
  130. task_config.input_configs[0] = data_node_config
  131. assert task_config.input_configs[0] != data_node_config
  132. def mock_func():
  133. pass
  134. def test_auto_update_and_reload(data_node):
  135. task_1 = Task(
  136. config_id="foo", properties={}, function=print, input=None, output=None, owner_id=None, skippable=False
  137. )
  138. _DataManager._repository._save(data_node)
  139. _TaskManager._repository._save(task_1)
  140. task_2 = _TaskManager._get(task_1)
  141. # auto set & reload on function attribute
  142. assert task_1.function == print
  143. assert task_2.function == print
  144. task_1.function = sum
  145. assert task_1.function == sum
  146. assert task_2.function == sum
  147. task_2.function = mock_func
  148. assert task_1.function == mock_func
  149. assert task_2.function == mock_func
  150. # auto set & reload on skippable attribute
  151. assert not task_1.skippable
  152. assert not task_2.skippable
  153. task_1.skippable = True
  154. assert task_1.skippable
  155. assert task_2.skippable
  156. task_2.skippable = False
  157. assert not task_1.skippable
  158. assert not task_2.skippable
  159. # auto set & reload on parent_ids attribute (set() object does not have auto set yet)
  160. assert task_1.parent_ids == set()
  161. assert task_2.parent_ids == set()
  162. task_1._parent_ids.update(["sc2"])
  163. _TaskManager._update(task_1)
  164. assert task_1.parent_ids == {"sc2"}
  165. assert task_2.parent_ids == {"sc2"}
  166. task_2._parent_ids.clear()
  167. task_2._parent_ids.update(["sc1"])
  168. _TaskManager._update(task_2)
  169. assert task_1.parent_ids == {"sc1"}
  170. assert task_2.parent_ids == {"sc1"}
  171. with task_1 as task:
  172. assert task.config_id == "foo"
  173. assert task.owner_id is None
  174. assert task.function == mock_func
  175. assert not task.skippable
  176. assert task._is_in_context
  177. task.function = print
  178. task.skippable = True
  179. assert task.config_id == "foo"
  180. assert task.owner_id is None
  181. assert task.function == mock_func
  182. assert not task.skippable
  183. assert task._is_in_context
  184. assert task_1.config_id == "foo"
  185. assert task_1.owner_id is None
  186. assert task_1.function == print
  187. assert task.skippable
  188. assert not task_1._is_in_context
  189. def test_auto_update_and_reload_properties():
  190. task_1 = Task(
  191. config_id="foo", properties={}, function=print, input=None, output=None, owner_id=None, skippable=False
  192. )
  193. _TaskManager._repository._save(task_1)
  194. task_2 = _TaskManager._get(task_1)
  195. # auto set & reload on properties attribute
  196. assert task_1.properties == {}
  197. assert task_2.properties == {}
  198. task_1._properties["qux"] = 4
  199. assert task_1.properties["qux"] == 4
  200. assert task_2.properties["qux"] == 4
  201. assert task_1.properties == {"qux": 4}
  202. assert task_2.properties == {"qux": 4}
  203. task_2._properties["qux"] = 5
  204. assert task_1.properties["qux"] == 5
  205. assert task_2.properties["qux"] == 5
  206. task_1.properties["temp_key_1"] = "temp_value_1"
  207. task_1.properties["temp_key_2"] = "temp_value_2"
  208. assert task_1.properties == {
  209. "qux": 5,
  210. "temp_key_1": "temp_value_1",
  211. "temp_key_2": "temp_value_2",
  212. }
  213. assert task_2.properties == {
  214. "qux": 5,
  215. "temp_key_1": "temp_value_1",
  216. "temp_key_2": "temp_value_2",
  217. }
  218. task_1.properties.pop("temp_key_1")
  219. assert "temp_key_1" not in task_1.properties.keys()
  220. assert "temp_key_1" not in task_1.properties.keys()
  221. assert task_1.properties == {
  222. "qux": 5,
  223. "temp_key_2": "temp_value_2",
  224. }
  225. assert task_2.properties == {
  226. "qux": 5,
  227. "temp_key_2": "temp_value_2",
  228. }
  229. task_2.properties.pop("temp_key_2")
  230. assert task_1.properties == {"qux": 5}
  231. assert task_2.properties == {"qux": 5}
  232. assert "temp_key_2" not in task_1.properties.keys()
  233. assert "temp_key_2" not in task_2.properties.keys()
  234. task_1.properties["temp_key_3"] = 0
  235. assert task_1.properties == {"qux": 5, "temp_key_3": 0}
  236. assert task_2.properties == {"qux": 5, "temp_key_3": 0}
  237. task_1.properties.update({"temp_key_3": 1})
  238. assert task_1.properties == {"qux": 5, "temp_key_3": 1}
  239. assert task_2.properties == {"qux": 5, "temp_key_3": 1}
  240. task_1.properties.update({})
  241. assert task_1.properties == {"qux": 5, "temp_key_3": 1}
  242. assert task_2.properties == {"qux": 5, "temp_key_3": 1}
  243. task_1.properties["temp_key_4"] = 0
  244. task_1.properties["temp_key_5"] = 0
  245. with task_1 as task:
  246. assert task._is_in_context
  247. assert task.properties["qux"] == 5
  248. assert task.properties["temp_key_3"] == 1
  249. assert task.properties["temp_key_4"] == 0
  250. assert task.properties["temp_key_5"] == 0
  251. task.properties["qux"] = 9
  252. task.properties.pop("temp_key_3")
  253. task.properties.pop("temp_key_4")
  254. task.properties.update({"temp_key_4": 1})
  255. task.properties.update({"temp_key_5": 2})
  256. task.properties.pop("temp_key_5")
  257. task.properties.update({})
  258. assert task._is_in_context
  259. assert task.properties["qux"] == 5
  260. assert task.properties["temp_key_3"] == 1
  261. assert task.properties["temp_key_4"] == 0
  262. assert task.properties["temp_key_5"] == 0
  263. assert not task_1._is_in_context
  264. assert task_1.properties["qux"] == 9
  265. assert "temp_key_3" not in task_1.properties.keys()
  266. assert task_1.properties["temp_key_4"] == 1
  267. assert "temp_key_5" not in task_1.properties.keys()
  268. def test_get_parents(task):
  269. with mock.patch("taipy.core.get_parents") as mck:
  270. task.get_parents()
  271. mck.assert_called_once_with(task)
  272. def test_submit_task(task: Task):
  273. with mock.patch("taipy.core.task._task_manager._TaskManager._submit") as mock_submit:
  274. task.submit([], True)
  275. mock_submit.assert_called_once_with(task, [], True, False, None)