test_task.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  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. from unittest import mock
  12. import pytest
  13. from taipy.config.common.scope import Scope
  14. from taipy.config.config import Config
  15. from taipy.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, PropertyKeyAlreadyExisted
  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._set(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.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_property_and_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.properties["key"] = "value"
  94. assert task.properties == {"key": "value"}
  95. assert task.key == "value"
  96. task.properties["new_key"] = "new_value"
  97. task.another_key = "another_value"
  98. assert task.key == "value"
  99. assert task.new_key == "new_value"
  100. assert task.another_key == "another_value"
  101. assert task.properties == {"key": "value", "new_key": "new_value"}
  102. with pytest.raises(AttributeKeyAlreadyExisted):
  103. task.bar = "KeyAlreadyUsed"
  104. with pytest.raises(PropertyKeyAlreadyExisted):
  105. task.properties["bar"] = "KeyAlreadyUsed"
  106. def test_can_not_change_task_output(output):
  107. task = Task("name_1", {}, print, output=output)
  108. with pytest.raises(AttributeError):
  109. task.output = {}
  110. assert list(task.output.values()) == output
  111. output.append(output[0])
  112. assert list(task.output.values()) != output
  113. def test_can_not_change_task_input(input):
  114. task = Task("name_1", {}, print, input=input)
  115. with pytest.raises(AttributeError):
  116. task.input = {}
  117. assert list(task.input.values()) == input
  118. input.append(input[0])
  119. assert list(task.input.values()) != input
  120. def test_can_not_change_task_config_output(output_config):
  121. task_config = Config.configure_task("name_1", print, [], output=output_config)
  122. assert task_config.output_configs == output_config
  123. with pytest.raises(AttributeError):
  124. task_config.output_configs = []
  125. output_config.append(output_config[0])
  126. assert task_config._output != output_config
  127. def test_can_not_update_task_output_values(output_config):
  128. data_node_cfg = Config.configure_data_node("data_node_cfg")
  129. task_config = Config.configure_task("name_1", print, [], output=output_config)
  130. task_config.output_configs.append(data_node_cfg)
  131. assert task_config.output_configs == output_config
  132. task_config.output_configs[0] = data_node_cfg
  133. assert task_config.output_configs[0] != data_node_cfg
  134. def test_can_not_update_task_input_values(input_config):
  135. data_node_config = DataNodeConfig("data_node")
  136. task_config = Config.configure_task("name_1", print, input=input_config, output=[])
  137. task_config.input_configs.append(data_node_config)
  138. assert task_config.input_configs == input_config
  139. task_config.input_configs[0] = data_node_config
  140. assert task_config.input_configs[0] != data_node_config
  141. def mock_func():
  142. pass
  143. def test_auto_set_and_reload(data_node):
  144. task_1 = Task(
  145. config_id="foo", properties={}, function=print, input=None, output=None, owner_id=None, skippable=False
  146. )
  147. _DataManager._set(data_node)
  148. _TaskManager._set(task_1)
  149. task_2 = _TaskManager._get(task_1)
  150. # auto set & reload on function attribute
  151. assert task_1.function == print
  152. assert task_2.function == print
  153. task_1.function = sum
  154. assert task_1.function == sum
  155. assert task_2.function == sum
  156. task_2.function = mock_func
  157. assert task_1.function == mock_func
  158. assert task_2.function == mock_func
  159. # auto set & reload on skippable attribute
  160. assert not task_1.skippable
  161. assert not task_2.skippable
  162. task_1.skippable = True
  163. assert task_1.skippable
  164. assert task_2.skippable
  165. task_2.skippable = False
  166. assert not task_1.skippable
  167. assert not task_2.skippable
  168. # auto set & reload on parent_ids attribute (set() object does not have auto set yet)
  169. assert task_1.parent_ids == set()
  170. assert task_2.parent_ids == set()
  171. task_1._parent_ids.update(["sc2"])
  172. _TaskManager._set(task_1)
  173. assert task_1.parent_ids == {"sc2"}
  174. assert task_2.parent_ids == {"sc2"}
  175. task_2._parent_ids.clear()
  176. task_2._parent_ids.update(["sc1"])
  177. _TaskManager._set(task_2)
  178. assert task_1.parent_ids == {"sc1"}
  179. assert task_2.parent_ids == {"sc1"}
  180. with task_1 as task:
  181. assert task.config_id == "foo"
  182. assert task.owner_id is None
  183. assert task.function == mock_func
  184. assert not task.skippable
  185. assert task._is_in_context
  186. task.function = print
  187. task.skippable = True
  188. assert task.config_id == "foo"
  189. assert task.owner_id is None
  190. assert task.function == mock_func
  191. assert not task.skippable
  192. assert task._is_in_context
  193. assert task_1.config_id == "foo"
  194. assert task_1.owner_id is None
  195. assert task_1.function == print
  196. assert task.skippable
  197. assert not task_1._is_in_context
  198. def test_auto_set_and_reload_properties():
  199. task_1 = Task(
  200. config_id="foo", properties={}, function=print, input=None, output=None, owner_id=None, skippable=False
  201. )
  202. _TaskManager._set(task_1)
  203. task_2 = _TaskManager._get(task_1)
  204. # auto set & reload on properties attribute
  205. assert task_1.properties == {}
  206. assert task_2.properties == {}
  207. task_1._properties["qux"] = 4
  208. assert task_1.properties["qux"] == 4
  209. assert task_2.properties["qux"] == 4
  210. assert task_1.properties == {"qux": 4}
  211. assert task_2.properties == {"qux": 4}
  212. task_2._properties["qux"] = 5
  213. assert task_1.properties["qux"] == 5
  214. assert task_2.properties["qux"] == 5
  215. task_1.properties["temp_key_1"] = "temp_value_1"
  216. task_1.properties["temp_key_2"] = "temp_value_2"
  217. assert task_1.properties == {
  218. "qux": 5,
  219. "temp_key_1": "temp_value_1",
  220. "temp_key_2": "temp_value_2",
  221. }
  222. assert task_2.properties == {
  223. "qux": 5,
  224. "temp_key_1": "temp_value_1",
  225. "temp_key_2": "temp_value_2",
  226. }
  227. task_1.properties.pop("temp_key_1")
  228. assert "temp_key_1" not in task_1.properties.keys()
  229. assert "temp_key_1" not in task_1.properties.keys()
  230. assert task_1.properties == {
  231. "qux": 5,
  232. "temp_key_2": "temp_value_2",
  233. }
  234. assert task_2.properties == {
  235. "qux": 5,
  236. "temp_key_2": "temp_value_2",
  237. }
  238. task_2.properties.pop("temp_key_2")
  239. assert task_1.properties == {"qux": 5}
  240. assert task_2.properties == {"qux": 5}
  241. assert "temp_key_2" not in task_1.properties.keys()
  242. assert "temp_key_2" not in task_2.properties.keys()
  243. task_1.properties["temp_key_3"] = 0
  244. assert task_1.properties == {"qux": 5, "temp_key_3": 0}
  245. assert task_2.properties == {"qux": 5, "temp_key_3": 0}
  246. task_1.properties.update({"temp_key_3": 1})
  247. assert task_1.properties == {"qux": 5, "temp_key_3": 1}
  248. assert task_2.properties == {"qux": 5, "temp_key_3": 1}
  249. task_1.properties.update({})
  250. assert task_1.properties == {"qux": 5, "temp_key_3": 1}
  251. assert task_2.properties == {"qux": 5, "temp_key_3": 1}
  252. task_1.properties["temp_key_4"] = 0
  253. task_1.properties["temp_key_5"] = 0
  254. with task_1 as task:
  255. assert task._is_in_context
  256. assert task.properties["qux"] == 5
  257. assert task.properties["temp_key_3"] == 1
  258. assert task.properties["temp_key_4"] == 0
  259. assert task.properties["temp_key_5"] == 0
  260. task.properties["qux"] = 9
  261. task.properties.pop("temp_key_3")
  262. task.properties.pop("temp_key_4")
  263. task.properties.update({"temp_key_4": 1})
  264. task.properties.update({"temp_key_5": 2})
  265. task.properties.pop("temp_key_5")
  266. task.properties.update({})
  267. assert task._is_in_context
  268. assert task.properties["qux"] == 5
  269. assert task.properties["temp_key_3"] == 1
  270. assert task.properties["temp_key_4"] == 0
  271. assert task.properties["temp_key_5"] == 0
  272. assert not task_1._is_in_context
  273. assert task_1.properties["qux"] == 9
  274. assert "temp_key_3" not in task_1.properties.keys()
  275. assert task_1.properties["temp_key_4"] == 1
  276. assert "temp_key_5" not in task_1.properties.keys()
  277. def test_get_parents(task):
  278. with mock.patch("taipy.core.get_parents") as mck:
  279. task.get_parents()
  280. mck.assert_called_once_with(task)
  281. def test_submit_task(task: Task):
  282. with mock.patch("taipy.core.task._task_manager._TaskManager._submit") as mock_submit:
  283. task.submit([], True)
  284. mock_submit.assert_called_once_with(task, [], True, False, None)