test_section_serialization.py 14 KB

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