test_core_cli.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. # Copyright 2023 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.mock import patch
  12. import pytest
  13. from taipy.config.common.frequency import Frequency
  14. from taipy.config.common.scope import Scope
  15. from taipy.config.config import Config
  16. from taipy.core import Core
  17. from taipy.core._version._version_manager import _VersionManager
  18. from taipy.core._version._version_manager_factory import _VersionManagerFactory
  19. from taipy.core.common._utils import _load_fct
  20. from taipy.core.cycle._cycle_manager import _CycleManager
  21. from taipy.core.data._data_manager import _DataManager
  22. from taipy.core.exceptions.exceptions import NonExistingVersion
  23. from taipy.core.job._job_manager import _JobManager
  24. from taipy.core.scenario._scenario_manager import _ScenarioManager
  25. from taipy.core.sequence._sequence_manager import _SequenceManager
  26. from taipy.core.task._task_manager import _TaskManager
  27. from tests.core.utils import assert_true_after_time
  28. def test_core_cli_no_arguments():
  29. with patch("sys.argv", ["prog"]):
  30. core = Core()
  31. core.run()
  32. assert Config.core.mode == "development"
  33. assert Config.core.version_number == _VersionManagerFactory._build_manager()._get_development_version()
  34. assert not Config.core.force
  35. core.stop()
  36. def test_core_cli_development_mode():
  37. with patch("sys.argv", ["prog", "--development"]):
  38. core = Core()
  39. core.run()
  40. assert Config.core.mode == "development"
  41. assert Config.core.version_number == _VersionManagerFactory._build_manager()._get_development_version()
  42. core.stop()
  43. def test_core_cli_dev_mode():
  44. with patch("sys.argv", ["prog", "-dev"]):
  45. core = Core()
  46. core.run()
  47. assert Config.core.mode == "development"
  48. assert Config.core.version_number == _VersionManagerFactory._build_manager()._get_development_version()
  49. core.stop()
  50. def test_core_cli_experiment_mode():
  51. with patch("sys.argv", ["prog", "--experiment"]):
  52. core = Core()
  53. core.run()
  54. assert Config.core.mode == "experiment"
  55. assert Config.core.version_number == _VersionManagerFactory._build_manager()._get_latest_version()
  56. assert not Config.core.force
  57. core.stop()
  58. def test_core_cli_experiment_mode_with_version():
  59. with patch("sys.argv", ["prog", "--experiment", "2.1"]):
  60. core = Core()
  61. core.run()
  62. assert Config.core.mode == "experiment"
  63. assert Config.core.version_number == "2.1"
  64. assert not Config.core.force
  65. core.stop()
  66. def test_core_cli_experiment_mode_with_force_version(init_config):
  67. with patch("sys.argv", ["prog", "--experiment", "2.1", "--taipy-force"]):
  68. init_config()
  69. core = Core()
  70. core.run()
  71. assert Config.core.mode == "experiment"
  72. assert Config.core.version_number == "2.1"
  73. assert Config.core.force
  74. core.stop()
  75. def test_core_cli_production_mode():
  76. with patch("sys.argv", ["prog", "--production"]):
  77. core = Core()
  78. core.run()
  79. assert Config.core.mode == "production"
  80. assert Config.core.version_number == _VersionManagerFactory._build_manager()._get_latest_version()
  81. assert not Config.core.force
  82. core.stop()
  83. def test_dev_mode_clean_all_entities_of_the_latest_version():
  84. scenario_config = config_scenario()
  85. # Create a scenario in development mode
  86. with patch("sys.argv", ["prog"]):
  87. core = Core()
  88. core.run()
  89. scenario = _ScenarioManager._create(scenario_config)
  90. _ScenarioManager._submit(scenario)
  91. core.stop()
  92. # Initial assertion
  93. assert len(_DataManager._get_all(version_number="all")) == 2
  94. assert len(_TaskManager._get_all(version_number="all")) == 1
  95. assert len(_SequenceManager._get_all(version_number="all")) == 1
  96. assert len(_ScenarioManager._get_all(version_number="all")) == 1
  97. assert len(_CycleManager._get_all(version_number="all")) == 1
  98. assert len(_JobManager._get_all(version_number="all")) == 1
  99. # Create a new scenario in experiment mode
  100. with patch("sys.argv", ["prog", "--experiment"]):
  101. core = Core()
  102. core.run()
  103. scenario = _ScenarioManager._create(scenario_config)
  104. _ScenarioManager._submit(scenario)
  105. core.stop()
  106. # Assert number of entities in 2nd version
  107. assert len(_DataManager._get_all(version_number="all")) == 4
  108. assert len(_TaskManager._get_all(version_number="all")) == 2
  109. assert len(_SequenceManager._get_all(version_number="all")) == 2
  110. assert len(_ScenarioManager._get_all(version_number="all")) == 2
  111. assert (
  112. len(_CycleManager._get_all(version_number="all")) == 1
  113. ) # No new cycle is created since old dev version use the same cycle
  114. assert len(_JobManager._get_all(version_number="all")) == 2
  115. # Run development mode again
  116. with patch("sys.argv", ["prog", "--development"]):
  117. core = Core()
  118. core.run()
  119. # The 1st dev version should be deleted run with development mode
  120. assert len(_DataManager._get_all(version_number="all")) == 2
  121. assert len(_TaskManager._get_all(version_number="all")) == 1
  122. assert len(_SequenceManager._get_all(version_number="all")) == 1
  123. assert len(_ScenarioManager._get_all(version_number="all")) == 1
  124. assert len(_CycleManager._get_all(version_number="all")) == 1
  125. assert len(_JobManager._get_all(version_number="all")) == 1
  126. # Submit new dev version
  127. scenario = _ScenarioManager._create(scenario_config)
  128. _ScenarioManager._submit(scenario)
  129. core.stop()
  130. # Assert number of entities with 1 dev version and 1 exp version
  131. assert len(_DataManager._get_all(version_number="all")) == 4
  132. assert len(_TaskManager._get_all(version_number="all")) == 2
  133. assert len(_SequenceManager._get_all(version_number="all")) == 2
  134. assert len(_ScenarioManager._get_all(version_number="all")) == 2
  135. assert len(_CycleManager._get_all(version_number="all")) == 1
  136. assert len(_JobManager._get_all(version_number="all")) == 2
  137. # Assert number of entities of the latest version only
  138. assert len(_DataManager._get_all(version_number="latest")) == 2
  139. assert len(_TaskManager._get_all(version_number="latest")) == 1
  140. assert len(_SequenceManager._get_all(version_number="latest")) == 1
  141. assert len(_ScenarioManager._get_all(version_number="latest")) == 1
  142. assert len(_JobManager._get_all(version_number="latest")) == 1
  143. # Assert number of entities of the development version only
  144. assert len(_DataManager._get_all(version_number="development")) == 2
  145. assert len(_TaskManager._get_all(version_number="development")) == 1
  146. assert len(_SequenceManager._get_all(version_number="development")) == 1
  147. assert len(_ScenarioManager._get_all(version_number="development")) == 1
  148. assert len(_JobManager._get_all(version_number="development")) == 1
  149. # Assert number of entities of an unknown version
  150. with pytest.raises(NonExistingVersion):
  151. assert _DataManager._get_all(version_number="foo")
  152. with pytest.raises(NonExistingVersion):
  153. assert _TaskManager._get_all(version_number="foo")
  154. with pytest.raises(NonExistingVersion):
  155. assert _SequenceManager._get_all(version_number="foo")
  156. with pytest.raises(NonExistingVersion):
  157. assert _ScenarioManager._get_all(version_number="foo")
  158. with pytest.raises(NonExistingVersion):
  159. assert _JobManager._get_all(version_number="foo")
  160. def twice_doppelganger(a):
  161. return a * 2
  162. def test_dev_mode_clean_all_entities_when_config_is_alternated():
  163. data_node_1_config = Config.configure_data_node(
  164. id="d1", storage_type="pickle", default_data="abc", scope=Scope.SCENARIO
  165. )
  166. data_node_2_config = Config.configure_data_node(id="d2", storage_type="csv", default_path="foo.csv")
  167. task_config = Config.configure_task("my_task", twice_doppelganger, data_node_1_config, data_node_2_config)
  168. scenario_config = Config.configure_scenario("my_scenario", [task_config], frequency=Frequency.DAILY)
  169. # Create a scenario in development mode with the doppelganger function
  170. with patch("sys.argv", ["prog"]):
  171. core = Core()
  172. core.run()
  173. scenario = _ScenarioManager._create(scenario_config)
  174. _ScenarioManager._submit(scenario)
  175. core.stop()
  176. # Delete the twice_doppelganger function
  177. # and clear cache of _load_fct() to simulate a new run
  178. del globals()["twice_doppelganger"]
  179. _load_fct.cache_clear()
  180. # Create a scenario in development mode with another function
  181. scenario_config = config_scenario()
  182. with patch("sys.argv", ["prog"]):
  183. core = Core()
  184. core.run()
  185. scenario = _ScenarioManager._create(scenario_config)
  186. _ScenarioManager._submit(scenario)
  187. core.stop()
  188. def test_version_number_when_switching_mode():
  189. with patch("sys.argv", ["prog", "--development"]):
  190. core = Core()
  191. core.run()
  192. ver_1 = _VersionManager._get_latest_version()
  193. ver_dev = _VersionManager._get_development_version()
  194. assert ver_1 == ver_dev
  195. assert len(_VersionManager._get_all()) == 1
  196. core.stop()
  197. # Run with dev mode, the version number is the same
  198. with patch("sys.argv", ["prog", "--development"]):
  199. core = Core()
  200. core.run()
  201. ver_2 = _VersionManager._get_latest_version()
  202. assert ver_2 == ver_dev
  203. assert len(_VersionManager._get_all()) == 1
  204. core.stop()
  205. # When run with experiment mode, a new version is created
  206. with patch("sys.argv", ["prog", "--experiment"]):
  207. core = Core()
  208. core.run()
  209. ver_3 = _VersionManager._get_latest_version()
  210. assert ver_3 != ver_dev
  211. assert len(_VersionManager._get_all()) == 2
  212. core.stop()
  213. with patch("sys.argv", ["prog", "--experiment", "2.1"]):
  214. core = Core()
  215. core.run()
  216. ver_4 = _VersionManager._get_latest_version()
  217. assert ver_4 == "2.1"
  218. assert len(_VersionManager._get_all()) == 3
  219. core.stop()
  220. with patch("sys.argv", ["prog", "--experiment"]):
  221. core = Core()
  222. core.run()
  223. ver_5 = _VersionManager._get_latest_version()
  224. assert ver_5 != ver_3
  225. assert ver_5 != ver_4
  226. assert ver_5 != ver_dev
  227. assert len(_VersionManager._get_all()) == 4
  228. core.stop()
  229. # When run with production mode, the latest version is used as production
  230. with patch("sys.argv", ["prog", "--production"]):
  231. core = Core()
  232. core.run()
  233. ver_6 = _VersionManager._get_latest_version()
  234. production_versions = _VersionManager._get_production_versions()
  235. assert ver_6 == ver_5
  236. assert production_versions == [ver_6]
  237. assert len(_VersionManager._get_all()) == 4
  238. core.stop()
  239. # When run with production mode, the "2.1" version is used as production
  240. with patch("sys.argv", ["prog", "--production", "2.1"]):
  241. core = Core()
  242. core.run()
  243. ver_7 = _VersionManager._get_latest_version()
  244. production_versions = _VersionManager._get_production_versions()
  245. assert ver_7 == "2.1"
  246. assert production_versions == [ver_6, ver_7]
  247. assert len(_VersionManager._get_all()) == 4
  248. core.stop()
  249. # Run with dev mode, the version number is the same as the first dev version to overide it
  250. with patch("sys.argv", ["prog", "--development"]):
  251. core = Core()
  252. core.run()
  253. ver_7 = _VersionManager._get_latest_version()
  254. assert ver_1 == ver_7
  255. assert len(_VersionManager._get_all()) == 4
  256. core.stop()
  257. def test_production_mode_load_all_entities_from_previous_production_version():
  258. scenario_config = config_scenario()
  259. with patch("sys.argv", ["prog", "--development"]):
  260. core = Core()
  261. core.run()
  262. scenario = _ScenarioManager._create(scenario_config)
  263. _ScenarioManager._submit(scenario)
  264. core.stop()
  265. with patch("sys.argv", ["prog", "--production", "1.0"]):
  266. core = Core()
  267. core.run()
  268. production_ver_1 = _VersionManager._get_latest_version()
  269. assert _VersionManager._get_production_versions() == [production_ver_1]
  270. # When run production mode on a new app, a dev version is created alongside
  271. assert _VersionManager._get_development_version() not in _VersionManager._get_production_versions()
  272. assert len(_VersionManager._get_all()) == 2
  273. scenario = _ScenarioManager._create(scenario_config)
  274. _ScenarioManager._submit(scenario)
  275. assert len(_DataManager._get_all()) == 2
  276. assert len(_TaskManager._get_all()) == 1
  277. assert len(_SequenceManager._get_all()) == 1
  278. assert len(_ScenarioManager._get_all()) == 1
  279. assert len(_CycleManager._get_all()) == 1
  280. assert len(_JobManager._get_all()) == 1
  281. core.stop()
  282. with patch("sys.argv", ["prog", "--production", "2.0"]):
  283. core = Core()
  284. core.run()
  285. production_ver_2 = _VersionManager._get_latest_version()
  286. assert _VersionManager._get_production_versions() == [production_ver_1, production_ver_2]
  287. assert len(_VersionManager._get_all()) == 3
  288. # All entities from previous production version should be saved
  289. scenario = _ScenarioManager._create(scenario_config)
  290. _ScenarioManager._submit(scenario)
  291. assert len(_DataManager._get_all()) == 4
  292. assert len(_TaskManager._get_all()) == 2
  293. assert len(_SequenceManager._get_all()) == 2
  294. assert len(_ScenarioManager._get_all()) == 2
  295. assert len(_CycleManager._get_all()) == 1
  296. assert len(_JobManager._get_all()) == 2
  297. core.stop()
  298. def test_force_override_experiment_version():
  299. scenario_config = config_scenario()
  300. with patch("sys.argv", ["prog", "--experiment", "1.0"]):
  301. core = Core()
  302. core.run()
  303. ver_1 = _VersionManager._get_latest_version()
  304. assert ver_1 == "1.0"
  305. # When create new experiment version, a development version entity is also created as a placeholder
  306. assert len(_VersionManager._get_all()) == 2 # 2 version include 1 experiment 1 development
  307. scenario = _ScenarioManager._create(scenario_config)
  308. _ScenarioManager._submit(scenario)
  309. assert len(_DataManager._get_all()) == 2
  310. assert len(_TaskManager._get_all()) == 1
  311. assert len(_SequenceManager._get_all()) == 1
  312. assert len(_ScenarioManager._get_all()) == 1
  313. assert len(_CycleManager._get_all()) == 1
  314. assert len(_JobManager._get_all()) == 1
  315. core.stop()
  316. Config.configure_global_app(foo="bar")
  317. # Without --taipy-force parameter, a SystemExit will be raised
  318. with pytest.raises(SystemExit):
  319. with patch("sys.argv", ["prog", "--experiment", "1.0"]):
  320. core = Core()
  321. core.run()
  322. core.stop()
  323. # With --taipy-force parameter
  324. with patch("sys.argv", ["prog", "--experiment", "1.0", "--taipy-force"]):
  325. core = Core()
  326. core.run()
  327. ver_2 = _VersionManager._get_latest_version()
  328. assert ver_2 == "1.0"
  329. assert len(_VersionManager._get_all()) == 2 # 2 version include 1 experiment 1 development
  330. # All entities from previous submit should be saved
  331. scenario = _ScenarioManager._create(scenario_config)
  332. _ScenarioManager._submit(scenario)
  333. assert len(_DataManager._get_all()) == 4
  334. assert len(_TaskManager._get_all()) == 2
  335. assert len(_SequenceManager._get_all()) == 2
  336. assert len(_ScenarioManager._get_all()) == 2
  337. assert len(_CycleManager._get_all()) == 1
  338. assert len(_JobManager._get_all()) == 2
  339. core.stop()
  340. def test_force_override_production_version():
  341. scenario_config = config_scenario()
  342. with patch("sys.argv", ["prog", "--production", "1.0"]):
  343. core = Core()
  344. core.run()
  345. ver_1 = _VersionManager._get_latest_version()
  346. production_versions = _VersionManager._get_production_versions()
  347. assert ver_1 == "1.0"
  348. assert production_versions == ["1.0"]
  349. # When create new production version, a development version entity is also created as a placeholder
  350. assert len(_VersionManager._get_all()) == 2 # 2 version include 1 production 1 development
  351. scenario = _ScenarioManager._create(scenario_config)
  352. _ScenarioManager._submit(scenario)
  353. assert len(_DataManager._get_all()) == 2
  354. assert len(_TaskManager._get_all()) == 1
  355. assert len(_SequenceManager._get_all()) == 1
  356. assert len(_ScenarioManager._get_all()) == 1
  357. assert len(_CycleManager._get_all()) == 1
  358. assert len(_JobManager._get_all()) == 1
  359. core.stop()
  360. Config.configure_global_app(foo="bar")
  361. # Without --taipy-force parameter, a SystemExit will be raised
  362. with pytest.raises(SystemExit):
  363. with patch("sys.argv", ["prog", "--production", "1.0"]):
  364. core = Core()
  365. core.run()
  366. core.stop()
  367. # With --taipy-force parameter
  368. with patch("sys.argv", ["prog", "--production", "1.0", "--taipy-force"]):
  369. core = Core()
  370. core.run()
  371. ver_2 = _VersionManager._get_latest_version()
  372. assert ver_2 == "1.0"
  373. assert len(_VersionManager._get_all()) == 2 # 2 version include 1 production 1 development
  374. # All entities from previous submit should be saved
  375. scenario = _ScenarioManager._create(scenario_config)
  376. _ScenarioManager._submit(scenario)
  377. assert len(_DataManager._get_all()) == 4
  378. assert len(_TaskManager._get_all()) == 2
  379. assert len(_SequenceManager._get_all()) == 2
  380. assert len(_ScenarioManager._get_all()) == 2
  381. assert len(_CycleManager._get_all()) == 1
  382. assert len(_JobManager._get_all()) == 2
  383. core.stop()
  384. def test_modify_job_configuration_dont_stop_application(caplog, init_config):
  385. scenario_config = config_scenario()
  386. with patch("sys.argv", ["prog", "--experiment", "1.0"]):
  387. core = Core()
  388. Config.configure_job_executions(mode="development")
  389. core.run(force_restart=True)
  390. scenario = _ScenarioManager._create(scenario_config)
  391. jobs = _ScenarioManager._submit(scenario)
  392. assert all([job.is_finished() for job in jobs])
  393. core.stop()
  394. init_config()
  395. scenario_config = config_scenario()
  396. with patch("sys.argv", ["prog", "--experiment", "1.0"]):
  397. core = Core()
  398. Config.configure_job_executions(mode="standalone", max_nb_of_workers=5)
  399. core.run(force_restart=True)
  400. scenario = _ScenarioManager._create(scenario_config)
  401. jobs = _ScenarioManager._submit(scenario)
  402. assert_true_after_time(lambda: all(job.is_finished() for job in jobs))
  403. error_message = str(caplog.text)
  404. assert 'JOB "mode" was modified' in error_message
  405. assert 'JOB "max_nb_of_workers" was modified' in error_message
  406. core.stop()
  407. def test_modify_config_properties_without_force(caplog, init_config):
  408. scenario_config = config_scenario()
  409. with patch("sys.argv", ["prog", "--experiment", "1.0"]):
  410. core = Core()
  411. core.run()
  412. scenario = _ScenarioManager._create(scenario_config)
  413. _ScenarioManager._submit(scenario)
  414. core.stop()
  415. init_config()
  416. scenario_config_2 = config_scenario_2()
  417. with pytest.raises(SystemExit):
  418. with patch("sys.argv", ["prog", "--experiment", "1.0"]):
  419. core = Core()
  420. core.run()
  421. scenario = _ScenarioManager._create(scenario_config_2)
  422. _ScenarioManager._submit(scenario)
  423. core.stop()
  424. error_message = str(caplog.text)
  425. assert 'DATA_NODE "d3" was added' in error_message
  426. assert 'DATA_NODE "d0" was removed' in error_message
  427. assert 'DATA_NODE "d2" has attribute "default_path" modified' in error_message
  428. assert 'CORE "root_folder" was modified' in error_message
  429. assert 'CORE "repository_type" was modified' in error_message
  430. assert 'JOB "mode" was modified' in error_message
  431. assert 'JOB "max_nb_of_workers" was modified' in error_message
  432. assert 'SCENARIO "my_scenario" has attribute "frequency" modified' in error_message
  433. assert 'SCENARIO "my_scenario" has attribute "tasks" modified' in error_message
  434. assert 'TASK "my_task" has attribute "inputs" modified' in error_message
  435. assert 'TASK "my_task" has attribute "function" modified' in error_message
  436. assert 'TASK "my_task" has attribute "outputs" modified' in error_message
  437. assert 'DATA_NODE "d2" has attribute "has_header" modified' in error_message
  438. assert 'DATA_NODE "d2" has attribute "exposed_type" modified' in error_message
  439. assert 'CORE "repository_properties" was added' in error_message
  440. def twice(a):
  441. return a * 2
  442. def config_scenario():
  443. Config.configure_data_node(id="d0")
  444. data_node_1_config = Config.configure_data_node(
  445. id="d1", storage_type="pickle", default_data="abc", scope=Scope.SCENARIO
  446. )
  447. data_node_2_config = Config.configure_data_node(id="d2", storage_type="csv", default_path="foo.csv")
  448. task_config = Config.configure_task("my_task", twice, data_node_1_config, data_node_2_config)
  449. scenario_config = Config.configure_scenario("my_scenario", [task_config], frequency=Frequency.DAILY)
  450. scenario_config.add_sequences({"my_sequence": [task_config]})
  451. return scenario_config
  452. def double_twice(a):
  453. return a * 2, a * 2
  454. def config_scenario_2():
  455. Config.configure_core(
  456. root_folder="foo_root",
  457. # Changing the "storage_folder" will fail since older versions are stored in older folder
  458. # storage_folder="foo_storage",
  459. repository_type="bar",
  460. repository_properties={"foo": "bar"},
  461. )
  462. Config.configure_job_executions(mode="standalone", max_nb_of_workers=5)
  463. data_node_1_config = Config.configure_data_node(
  464. id="d1", storage_type="pickle", default_data="abc", scope=Scope.SCENARIO
  465. )
  466. # Modify properties of "d2"
  467. data_node_2_config = Config.configure_data_node(
  468. id="d2", storage_type="csv", default_path="bar.csv", has_header=False, exposed_type="numpy"
  469. )
  470. # Add new data node "d3"
  471. data_node_3_config = Config.configure_data_node(
  472. id="d3", storage_type="csv", default_path="baz.csv", has_header=False, exposed_type="numpy"
  473. )
  474. # Modify properties of "my_task", including the function and outputs list
  475. Config.configure_task("my_task", double_twice, data_node_3_config, [data_node_1_config, data_node_2_config])
  476. task_config_1 = Config.configure_task("my_task_1", double_twice, data_node_3_config, [data_node_2_config])
  477. # Modify properties of "my_scenario", where tasks is now my_task_1
  478. scenario_config = Config.configure_scenario("my_scenario", [task_config_1], frequency=Frequency.MONTHLY)
  479. scenario_config.add_sequences({"my_sequence": [task_config_1]})
  480. return scenario_config