test_section_serialization.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  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. import datetime
  12. import json
  13. import os
  14. from unittest import mock
  15. from taipy.common.config import Config
  16. from taipy.common.config._serializer._json_serializer import _JsonSerializer
  17. from taipy.core.common.frequency import Frequency
  18. from taipy.core.common.scope import Scope
  19. from tests.common.config.utils.named_temporary_file import NamedTemporaryFile
  20. from tests.common.config.utils.section_for_tests import SectionForTest
  21. from tests.common.config.utils.unique_section_for_tests import UniqueSectionForTest
  22. def add(a, b):
  23. return a + b
  24. class CustomClass:
  25. a = None
  26. b = None
  27. class CustomEncoder(json.JSONEncoder):
  28. def default(self, o):
  29. if isinstance(o, datetime):
  30. result = {"__type__": "Datetime", "__value__": o.isoformat()}
  31. else:
  32. result = json.JSONEncoder.default(self, o)
  33. return result
  34. class CustomDecoder(json.JSONDecoder):
  35. def __init__(self, *args, **kwargs):
  36. json.JSONDecoder.__init__(self, *args, **kwargs, object_hook=self.object_hook)
  37. def object_hook(self, source):
  38. if source.get("__type__") == "Datetime":
  39. return datetime.fromisoformat(source.get("__value__"))
  40. else:
  41. return source
  42. def test_write_toml_configuration_file():
  43. expected_toml_config = """
  44. [TAIPY]
  45. [unique_section_name]
  46. attribute = "my_attribute"
  47. prop = "my_prop"
  48. prop_int = "1:int"
  49. prop_bool = "False:bool"
  50. prop_list = [ "p1", "1991-01-01T00:00:00:datetime", "1d0h0m0s:timedelta",]
  51. prop_scope = "SCENARIO:SCOPE"
  52. prop_freq = "QUARTERLY:FREQUENCY"
  53. baz = "ENV[QUX]"
  54. quux = "ENV[QUUZ]:bool"
  55. corge = [ "grault", "ENV[GARPLY]", "ENV[WALDO]:int", "3.0:float",]
  56. [section_name.default]
  57. attribute = "default_attribute"
  58. prop = "default_prop"
  59. prop_int = "0:int"
  60. [section_name.my_id]
  61. attribute = "my_attribute"
  62. prop = "default_prop"
  63. prop_int = "1:int"
  64. prop_bool = "False:bool"
  65. prop_list = [ "unique_section_name:SECTION",]
  66. prop_scope = "SCENARIO"
  67. baz = "ENV[QUX]"
  68. """.strip()
  69. tf = NamedTemporaryFile()
  70. with mock.patch.dict(
  71. os.environ, {"FOO": "in_memory", "QUX": "qux", "QUUZ": "true", "GARPLY": "garply", "WALDO": "17"}
  72. ):
  73. unique_section = Config.configure_unique_section_for_tests(
  74. attribute="my_attribute",
  75. prop="my_prop",
  76. prop_int=1,
  77. prop_bool=False,
  78. prop_list=["p1", datetime.datetime(1991, 1, 1), datetime.timedelta(days=1)],
  79. prop_scope=Scope.SCENARIO,
  80. prop_freq=Frequency.QUARTERLY,
  81. baz="ENV[QUX]",
  82. quux="ENV[QUUZ]:bool",
  83. corge=("grault", "ENV[GARPLY]", "ENV[WALDO]:int", 3.0),
  84. )
  85. Config.configure_section_for_tests(
  86. "my_id",
  87. "my_attribute",
  88. prop_int=1,
  89. prop_bool=False,
  90. prop_list=[unique_section],
  91. prop_scope="SCENARIO",
  92. baz="ENV[QUX]",
  93. )
  94. Config.backup(tf.filename)
  95. actual_config = tf.read().strip()
  96. assert actual_config == expected_toml_config
  97. def test_read_toml_configuration_file():
  98. toml_config = """
  99. [TAIPY]
  100. foo = "bar"
  101. [unique_section_name]
  102. attribute = "my_attribute"
  103. prop = "my_prop"
  104. prop_int = "1:int"
  105. prop_bool = "False:bool"
  106. prop_list = [ "p1", "1991-01-01T00:00:00:datetime", "1d0h0m0s:timedelta",]
  107. prop_scope = "SCENARIO:SCOPE"
  108. prop_freq = "QUARTERLY:FREQUENCY"
  109. baz = "ENV[QUX]"
  110. quux = "ENV[QUUZ]:bool"
  111. corge = [ "grault", "ENV[GARPLY]", "ENV[WALDO]:int", "3.0:float",]
  112. [TAIPY.custom_properties]
  113. bar = "baz"
  114. [section_name.default]
  115. attribute = "default_attribute"
  116. prop = "default_prop"
  117. prop_int = "0:int"
  118. [section_name.my_id]
  119. attribute = "my_attribute"
  120. prop = "default_prop"
  121. prop_int = "1:int"
  122. prop_bool = "False:bool"
  123. prop_list = [ "unique_section_name", "section_name.my_id",]
  124. prop_scope = "SCENARIO:SCOPE"
  125. baz = "ENV[QUX]"
  126. """.strip()
  127. tf = NamedTemporaryFile(toml_config)
  128. with mock.patch.dict(
  129. os.environ, {"FOO": "in_memory", "QUX": "qux", "QUUZ": "true", "GARPLY": "garply", "WALDO": "17"}
  130. ):
  131. Config.override(tf.filename)
  132. assert Config.global_config.foo == "bar"
  133. assert Config.global_config.custom_properties.get("bar") == "baz"
  134. assert Config.unique_sections is not None
  135. assert Config.unique_sections[UniqueSectionForTest.name] is not None
  136. assert Config.unique_sections[UniqueSectionForTest.name].attribute == "my_attribute"
  137. assert Config.unique_sections[UniqueSectionForTest.name].prop == "my_prop"
  138. assert Config.unique_sections[UniqueSectionForTest.name].prop_int == 1
  139. assert Config.unique_sections[UniqueSectionForTest.name].prop_bool is False
  140. assert Config.unique_sections[UniqueSectionForTest.name].prop_list == [
  141. "p1",
  142. datetime.datetime(1991, 1, 1),
  143. datetime.timedelta(days=1),
  144. ]
  145. assert Config.unique_sections[UniqueSectionForTest.name].prop_scope == Scope.SCENARIO
  146. assert Config.unique_sections[UniqueSectionForTest.name].prop_freq == Frequency.QUARTERLY
  147. assert Config.unique_sections[UniqueSectionForTest.name].baz == "qux"
  148. assert Config.unique_sections[UniqueSectionForTest.name].quux is True
  149. assert Config.unique_sections[UniqueSectionForTest.name].corge == [
  150. "grault",
  151. "garply",
  152. 17,
  153. 3.0,
  154. ]
  155. assert Config.sections is not None
  156. assert len(Config.sections) == 1
  157. assert Config.sections[SectionForTest.name] is not None
  158. assert len(Config.sections[SectionForTest.name]) == 2
  159. assert Config.sections[SectionForTest.name]["default"] is not None
  160. assert Config.sections[SectionForTest.name]["default"].attribute == "default_attribute"
  161. assert Config.sections[SectionForTest.name]["default"].prop == "default_prop"
  162. assert Config.sections[SectionForTest.name]["default"].prop_int == 0
  163. assert Config.sections[SectionForTest.name]["my_id"] is not None
  164. assert Config.sections[SectionForTest.name]["my_id"].attribute == "my_attribute"
  165. assert Config.sections[SectionForTest.name]["my_id"].prop == "default_prop"
  166. assert Config.sections[SectionForTest.name]["my_id"].prop_int == 1
  167. assert Config.sections[SectionForTest.name]["my_id"].prop_bool is False
  168. assert Config.sections[SectionForTest.name]["my_id"].prop_list == ["unique_section_name", "section_name.my_id"]
  169. assert Config.sections[SectionForTest.name]["my_id"].prop_scope == Scope.SCENARIO
  170. assert Config.sections[SectionForTest.name]["my_id"].baz == "qux"
  171. tf2 = NamedTemporaryFile()
  172. Config.backup(tf2.filename)
  173. actual_config_2 = tf2.read().strip()
  174. assert actual_config_2 == toml_config
  175. def test_read_write_toml_configuration_file_with_function_and_class():
  176. expected_toml_config = """
  177. [TAIPY]
  178. [unique_section_name]
  179. attribute = "my_attribute"
  180. prop = "my_prop"
  181. prop_list = [ "tests.common.config.test_section_serialization.CustomEncoder:class", \
  182. "tests.common.config.test_section_serialization.CustomDecoder:class",]
  183. [section_name.default]
  184. attribute = "default_attribute"
  185. prop = "default_prop"
  186. prop_int = "0:int"
  187. [section_name.my_id]
  188. attribute = "my_attribute"
  189. prop = "default_prop"
  190. prop_int = "0:int"
  191. prop_fct_list = [ "tests.common.config.test_section_serialization.add:function",]
  192. prop_class_list = [ "tests.common.config.test_section_serialization.CustomClass:class",]
  193. [section_name.my_id_2]
  194. attribute = "my_attribute_2"
  195. prop = "default_prop"
  196. prop_int = "0:int"
  197. prop_fct_list = [ "builtins.print:function", "builtins.pow:function",]
  198. """.strip()
  199. tf = NamedTemporaryFile()
  200. Config.configure_unique_section_for_tests(
  201. attribute="my_attribute",
  202. prop="my_prop",
  203. prop_list=[CustomEncoder, CustomDecoder],
  204. )
  205. Config.configure_section_for_tests(
  206. "my_id",
  207. "my_attribute",
  208. prop_fct_list=[add],
  209. prop_class_list=[CustomClass],
  210. )
  211. Config.configure_section_for_tests(
  212. "my_id_2",
  213. "my_attribute_2",
  214. prop_fct_list=[print, pow],
  215. )
  216. Config.backup(tf.filename)
  217. actual_exported_toml = tf.read().strip()
  218. assert actual_exported_toml == expected_toml_config
  219. Config.override(tf.filename)
  220. tf2 = NamedTemporaryFile()
  221. Config.backup(tf2.filename)
  222. actual_exported_toml_2 = tf2.read().strip()
  223. assert actual_exported_toml_2 == expected_toml_config
  224. def test_write_json_configuration_file():
  225. expected_json_config = """
  226. {
  227. "TAIPY": {},
  228. "unique_section_name": {
  229. "attribute": "my_attribute",
  230. "prop": "my_prop",
  231. "prop_int": "1:int",
  232. "prop_bool": "False:bool",
  233. "prop_list": [
  234. "p1",
  235. "1991-01-01T00:00:00:datetime",
  236. "1d0h0m0s:timedelta"
  237. ],
  238. "prop_scope": "SCENARIO:SCOPE",
  239. "prop_freq": "QUARTERLY:FREQUENCY"
  240. },
  241. "section_name": {
  242. "default": {
  243. "attribute": "default_attribute",
  244. "prop": "default_prop",
  245. "prop_int": "0:int"
  246. },
  247. "my_id": {
  248. "attribute": "my_attribute",
  249. "prop": "default_prop",
  250. "prop_int": "1:int",
  251. "prop_bool": "False:bool",
  252. "prop_list": [
  253. "unique_section_name:SECTION"
  254. ],
  255. "prop_scope": "SCENARIO",
  256. "baz": "ENV[QUX]"
  257. }
  258. }
  259. }
  260. """.strip()
  261. tf = NamedTemporaryFile()
  262. Config._serializer = _JsonSerializer()
  263. unique_section = Config.configure_unique_section_for_tests(
  264. attribute="my_attribute",
  265. prop="my_prop",
  266. prop_int=1,
  267. prop_bool=False,
  268. prop_list=["p1", datetime.datetime(1991, 1, 1), datetime.timedelta(days=1)],
  269. prop_scope=Scope.SCENARIO,
  270. prop_freq=Frequency.QUARTERLY,
  271. )
  272. Config.configure_section_for_tests(
  273. "my_id",
  274. "my_attribute",
  275. prop_int=1,
  276. prop_bool=False,
  277. prop_list=[unique_section],
  278. prop_scope="SCENARIO",
  279. baz="ENV[QUX]",
  280. )
  281. Config.backup(tf.filename)
  282. actual_config = tf.read()
  283. assert actual_config == expected_json_config
  284. def test_read_json_configuration_file():
  285. json_config = """
  286. {
  287. "TAIPY": {
  288. "root_folder": "./taipy/",
  289. "storage_folder": ".data/",
  290. "repository_type": "filesystem"
  291. },
  292. "unique_section_name": {
  293. "attribute": "my_attribute",
  294. "prop": "my_prop",
  295. "prop_int": "1:int",
  296. "prop_bool": "False:bool",
  297. "prop_list": [
  298. "p1",
  299. "1991-01-01T00:00:00:datetime",
  300. "1d0h0m0s:timedelta"
  301. ],
  302. "prop_scope": "SCENARIO:SCOPE",
  303. "prop_freq": "QUARTERLY:FREQUENCY"
  304. },
  305. "section_name": {
  306. "default": {
  307. "attribute": "default_attribute",
  308. "prop": "default_prop",
  309. "prop_int": "0:int"
  310. },
  311. "my_id": {
  312. "attribute": "my_attribute",
  313. "prop": "default_prop",
  314. "prop_int": "1:int",
  315. "prop_bool": "False:bool",
  316. "prop_list": [
  317. "unique_section_name"
  318. ],
  319. "prop_scope": "SCENARIO"
  320. }
  321. }
  322. }
  323. """.strip()
  324. Config._serializer = _JsonSerializer()
  325. tf = NamedTemporaryFile(json_config)
  326. Config.override(tf.filename)
  327. assert Config.unique_sections is not None
  328. assert Config.unique_sections[UniqueSectionForTest.name] is not None
  329. assert Config.unique_sections[UniqueSectionForTest.name].attribute == "my_attribute"
  330. assert Config.unique_sections[UniqueSectionForTest.name].prop == "my_prop"
  331. assert Config.unique_sections[UniqueSectionForTest.name].prop_int == 1
  332. assert Config.unique_sections[UniqueSectionForTest.name].prop_bool is False
  333. assert Config.unique_sections[UniqueSectionForTest.name].prop_list == [
  334. "p1",
  335. datetime.datetime(1991, 1, 1),
  336. datetime.timedelta(days=1),
  337. ]
  338. assert Config.unique_sections[UniqueSectionForTest.name].prop_scope == Scope.SCENARIO
  339. assert Config.unique_sections[UniqueSectionForTest.name].prop_freq == Frequency.QUARTERLY
  340. assert Config.sections is not None
  341. assert len(Config.sections) == 1
  342. assert Config.sections[SectionForTest.name] is not None
  343. assert len(Config.sections[SectionForTest.name]) == 2
  344. assert Config.sections[SectionForTest.name]["default"] is not None
  345. assert Config.sections[SectionForTest.name]["default"].attribute == "default_attribute"
  346. assert Config.sections[SectionForTest.name]["default"].prop == "default_prop"
  347. assert Config.sections[SectionForTest.name]["default"].prop_int == 0
  348. assert Config.sections[SectionForTest.name]["my_id"] is not None
  349. assert Config.sections[SectionForTest.name]["my_id"].attribute == "my_attribute"
  350. assert Config.sections[SectionForTest.name]["my_id"].prop == "default_prop"
  351. assert Config.sections[SectionForTest.name]["my_id"].prop_int == 1
  352. assert Config.sections[SectionForTest.name]["my_id"].prop_bool is False
  353. assert Config.sections[SectionForTest.name]["my_id"].prop_list == ["unique_section_name"]
  354. tf2 = NamedTemporaryFile()
  355. Config.backup(tf2.filename)
  356. actual_config_2 = tf2.read().strip()
  357. assert actual_config_2 == json_config
  358. def test_read_write_json_configuration_file_with_function_and_class():
  359. expected_json_config = """
  360. {
  361. "TAIPY": {},
  362. "unique_section_name": {
  363. "attribute": "my_attribute",
  364. "prop": "my_prop",
  365. "prop_list": [
  366. "tests.common.config.test_section_serialization.CustomEncoder:class",
  367. "tests.common.config.test_section_serialization.CustomDecoder:class"
  368. ]
  369. },
  370. "section_name": {
  371. "default": {
  372. "attribute": "default_attribute",
  373. "prop": "default_prop",
  374. "prop_int": "0:int"
  375. },
  376. "my_id": {
  377. "attribute": "my_attribute",
  378. "prop": "default_prop",
  379. "prop_int": "0:int",
  380. "prop_fct_list": [
  381. "tests.common.config.test_section_serialization.add:function"
  382. ],
  383. "prop_class_list": [
  384. "tests.common.config.test_section_serialization.CustomClass:class"
  385. ]
  386. },
  387. "my_id_2": {
  388. "attribute": "my_attribute_2",
  389. "prop": "default_prop",
  390. "prop_int": "0:int",
  391. "prop_fct_list": [
  392. "builtins.print:function",
  393. "builtins.pow:function"
  394. ]
  395. }
  396. }
  397. }
  398. """.strip()
  399. Config._serializer = _JsonSerializer()
  400. tf = NamedTemporaryFile()
  401. Config.configure_unique_section_for_tests(
  402. attribute="my_attribute",
  403. prop="my_prop",
  404. prop_list=[CustomEncoder, CustomDecoder],
  405. )
  406. Config.configure_section_for_tests(
  407. "my_id",
  408. "my_attribute",
  409. prop_fct_list=[add],
  410. prop_class_list=[CustomClass],
  411. )
  412. Config.configure_section_for_tests(
  413. "my_id_2",
  414. "my_attribute_2",
  415. prop_fct_list=[print, pow],
  416. )
  417. Config.backup(tf.filename)
  418. actual_exported_json = tf.read().strip()
  419. assert actual_exported_json == expected_json_config
  420. Config.override(tf.filename)
  421. tf2 = NamedTemporaryFile()
  422. Config.backup(tf2.filename)
  423. actual_exported_json_2 = tf2.read().strip()
  424. assert actual_exported_json_2 == expected_json_config