| # SPDX-License-Identifier: MIT | |
| # SPDX-FileCopyrightText: 2021 Taneli Hukkinen | |
| # Licensed to PSF under a Contributor Agreement. | |
| import copy | |
| import datetime | |
| from decimal import Decimal as D | |
| import importlib | |
| from pathlib import Path | |
| import sys | |
| import tempfile | |
| import textwrap | |
| import unittest | |
| from test import support | |
| from test.support.script_helper import assert_python_ok | |
| from . import tomllib | |
| class TestMiscellaneous(unittest.TestCase): | |
| def test_load(self): | |
| content = "one=1 \n two='two' \n arr=[]" | |
| expected = {"one": 1, "two": "two", "arr": []} | |
| with tempfile.TemporaryDirectory() as tmp_dir_path: | |
| file_path = Path(tmp_dir_path) / "test.toml" | |
| file_path.write_text(content) | |
| with open(file_path, "rb") as bin_f: | |
| actual = tomllib.load(bin_f) | |
| self.assertEqual(actual, expected) | |
| def test_incorrect_load(self): | |
| content = "one=1" | |
| with tempfile.TemporaryDirectory() as tmp_dir_path: | |
| file_path = Path(tmp_dir_path) / "test.toml" | |
| file_path.write_text(content) | |
| with open(file_path, "r") as txt_f: | |
| with self.assertRaises(TypeError): | |
| tomllib.load(txt_f) # type: ignore[arg-type] | |
| def test_parse_float(self): | |
| doc = """ | |
| val=0.1 | |
| biggest1=inf | |
| biggest2=+inf | |
| smallest=-inf | |
| notnum1=nan | |
| notnum2=-nan | |
| notnum3=+nan | |
| """ | |
| obj = tomllib.loads(doc, parse_float=D) | |
| expected = { | |
| "val": D("0.1"), | |
| "biggest1": D("inf"), | |
| "biggest2": D("inf"), | |
| "smallest": D("-inf"), | |
| "notnum1": D("nan"), | |
| "notnum2": D("-nan"), | |
| "notnum3": D("nan"), | |
| } | |
| for k, expected_val in expected.items(): | |
| actual_val = obj[k] | |
| self.assertIsInstance(actual_val, D) | |
| if actual_val.is_nan(): | |
| self.assertTrue(expected_val.is_nan()) | |
| else: | |
| self.assertEqual(actual_val, expected_val) | |
| def test_deepcopy(self): | |
| doc = """ | |
| [bliibaa.diibaa] | |
| offsettime=[1979-05-27T00:32:00.999999-07:00] | |
| """ | |
| obj = tomllib.loads(doc) | |
| obj_copy = copy.deepcopy(obj) | |
| self.assertEqual(obj_copy, obj) | |
| expected_obj = { | |
| "bliibaa": { | |
| "diibaa": { | |
| "offsettime": [ | |
| datetime.datetime( | |
| 1979, | |
| 5, | |
| 27, | |
| 0, | |
| 32, | |
| 0, | |
| 999999, | |
| tzinfo=datetime.timezone(datetime.timedelta(hours=-7)), | |
| ) | |
| ] | |
| } | |
| } | |
| } | |
| self.assertEqual(obj_copy, expected_obj) | |
| @support.skip_if_unlimited_stack_size | |
| def test_inline_array_recursion_limit(self): | |
| with support.infinite_recursion(max_depth=100): | |
| available = support.get_recursion_available() | |
| nest_count = (available // 2) - 2 | |
| # Add details if the test fails | |
| with self.subTest(limit=sys.getrecursionlimit(), | |
| available=available, | |
| nest_count=nest_count): | |
| recursive_array_toml = "arr = " + nest_count * "[" + nest_count * "]" | |
| tomllib.loads(recursive_array_toml) | |
| @support.skip_if_unlimited_stack_size | |
| def test_inline_table_recursion_limit(self): | |
| with support.infinite_recursion(max_depth=100): | |
| available = support.get_recursion_available() | |
| nest_count = (available // 3) - 1 | |
| # Add details if the test fails | |
| with self.subTest(limit=sys.getrecursionlimit(), | |
| available=available, | |
| nest_count=nest_count): | |
| recursive_table_toml = nest_count * "key = {" + nest_count * "}" | |
| tomllib.loads(recursive_table_toml) | |
| def test_key_recursion_limit(self): | |
| nest_count = tomllib._parser.MAX_KEY_PARTS - 2 | |
| nested_key_toml = "a." * nest_count + "a = 1" | |
| tomllib.loads(nested_key_toml) | |
| nest_count = tomllib._parser.MAX_KEY_PARTS + 2 | |
| nested_key_toml = "a." * nest_count + "a = 1" | |
| with self.assertRaisesRegex( | |
| RecursionError, | |
| r"TOML key has more than the allowed [0-9]+ parts", | |
| ): | |
| tomllib.loads(nested_key_toml) | |
| def test_types_import(self): | |
| """Test that `_types` module runs. | |
| The module is for type annotations only, so it is otherwise | |
| never imported by tests. | |
| """ | |
| importlib.import_module(f"{tomllib.__name__}._types") | |
| def test_lazy_import(self): | |
| # Test the TOML file can be parsed without importing regular | |
| # expressions (tomllib._re) | |
| code = textwrap.dedent(""" | |
| import sys, tomllib, textwrap | |
| document = textwrap.dedent(''' | |
| [metadata] | |
| key = "text" | |
| array = ["array", "of", "text"] | |
| booleans = [true, false] | |
| ''') | |
| tomllib.loads(document) | |
| print("lazy import?", 'tomllib._re' not in sys.modules) | |
| """) | |
| proc = assert_python_ok("-c", code) | |
| self.assertIn(b"lazy import? True", proc.out) |