| # 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) |