Skip to content

Commit deac74d

Browse files
authored
Add unwrap method to recursive convert to plain old python objects (#187)
* add as_ppo method for elementary types * rename as_popo to unwrap; add recursive arg * add unwrap methods to collection types + Null * remove dangling 'def' * Container loops through items() instead of _body * add Integer and Item unit tests * added passing tests for everything up to Array * remove recursive option * remove unused is_ppo function * refactor Container unwrap to reuse code * add AbstractTable test * add string, null, and aot tests * add test for Container * type(..) to isinstance(..) in Container unwrap * try/catch ensure v has unwrap in Container.unwrap * replace 'type' with 'isinstance' in test_items.py * refactor assert_is_ppo to pass only ppo type * refactory test_document_is_a_dict to not call 'type' * remove elementary_fail function * minor change in Container unwrap method * use isinstance not is_tomlkit in AoT.unwrap * run pre-commit
1 parent 721acca commit deac74d

File tree

6 files changed

+290
-0
lines changed

6 files changed

+290
-0
lines changed

tests/test_items.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,32 @@
1010

1111
from tomlkit import api
1212
from tomlkit import parse
13+
from tomlkit.check import is_tomlkit
1314
from tomlkit.exceptions import NonExistentKey
15+
from tomlkit.items import AoT
16+
from tomlkit.items import Array
1417
from tomlkit.items import Bool
1518
from tomlkit.items import Comment
19+
from tomlkit.items import Date
20+
from tomlkit.items import DateTime
21+
from tomlkit.items import Float
1622
from tomlkit.items import InlineTable
1723
from tomlkit.items import Integer
24+
from tomlkit.items import Item
1825
from tomlkit.items import KeyType
26+
from tomlkit.items import Null
1927
from tomlkit.items import SingleKey as Key
2028
from tomlkit.items import String
2129
from tomlkit.items import StringType
2230
from tomlkit.items import Table
31+
from tomlkit.items import Time
2332
from tomlkit.items import Trivia
2433
from tomlkit.items import item
2534
from tomlkit.parser import Parser
2635

36+
from .util import assert_is_ppo
37+
from .util import elementary_test
38+
2739

2840
@pytest.fixture()
2941
def tz_pst():
@@ -69,6 +81,98 @@ def dst(self, dt):
6981
return UTC()
7082

7183

84+
def test_item_base_has_no_unwrap():
85+
trivia = Trivia(indent="\t", comment_ws=" ", comment="For unit test")
86+
item = Item(trivia)
87+
try:
88+
item.unwrap()
89+
except NotImplementedError:
90+
pass
91+
else:
92+
raise AssertionError("`items.Item` should not implement `unwrap`")
93+
94+
95+
def test_integer_unwrap():
96+
elementary_test(item(666), int)
97+
98+
99+
def test_float_unwrap():
100+
elementary_test(item(2.78), float)
101+
102+
103+
def test_false_unwrap():
104+
elementary_test(item(False), bool)
105+
106+
107+
def test_true_unwrap():
108+
elementary_test(item(True), bool)
109+
110+
111+
def test_datetime_unwrap():
112+
dt = datetime.utcnow()
113+
elementary_test(item(dt), datetime)
114+
115+
116+
def test_string_unwrap():
117+
elementary_test(item("hello"), str)
118+
119+
120+
def test_null_unwrap():
121+
n = Null()
122+
elementary_test(n, type(None))
123+
124+
125+
def test_aot_unwrap():
126+
d = item([{"a": "A"}, {"b": "B"}])
127+
assert is_tomlkit(d)
128+
unwrapped = d.unwrap()
129+
assert_is_ppo(unwrapped, list)
130+
for du, dw in zip(unwrapped, d):
131+
assert_is_ppo(du, dict)
132+
for ku in du:
133+
vu = du[ku]
134+
assert_is_ppo(ku, str)
135+
assert_is_ppo(vu, str)
136+
137+
138+
def test_time_unwrap():
139+
t = time(3, 8, 14)
140+
elementary_test(item(t), time)
141+
142+
143+
def test_date_unwrap():
144+
d = date.today()
145+
elementary_test(item(d), date)
146+
147+
148+
def test_array_unwrap():
149+
trivia = Trivia(indent="\t", comment_ws=" ", comment="For unit test")
150+
i = item(666)
151+
f = item(2.78)
152+
b = item(False)
153+
a = Array([i, f, b], trivia)
154+
a_unwrapped = a.unwrap()
155+
assert_is_ppo(a_unwrapped, list)
156+
assert_is_ppo(a_unwrapped[0], int)
157+
assert_is_ppo(a_unwrapped[1], float)
158+
assert_is_ppo(a_unwrapped[2], bool)
159+
160+
161+
def test_abstract_table_unwrap():
162+
table = item({"foo": "bar"})
163+
super_table = item({"table": table, "baz": "borg"})
164+
assert is_tomlkit(super_table["table"])
165+
166+
table_unwrapped = super_table.unwrap()
167+
sub_table = table_unwrapped["table"]
168+
assert_is_ppo(table_unwrapped, dict)
169+
assert_is_ppo(sub_table, dict)
170+
for ku in sub_table:
171+
vu = sub_table[ku]
172+
assert_is_ppo(ku, str)
173+
assert_is_ppo(vu, str)
174+
175+
72176
def test_key_comparison():
73177
k = Key("foo")
74178

tests/test_toml_document.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
from tomlkit._utils import _utc
1515
from tomlkit.api import document
1616
from tomlkit.exceptions import NonExistentKey
17+
from tomlkit.toml_document import TOMLDocument
18+
19+
from .util import assert_is_ppo
20+
from .util import elementary_test
1721

1822

1923
def test_document_is_a_dict(example):
@@ -154,6 +158,20 @@ def test_toml_document_without_super_tables():
154158
assert "tool" in d
155159

156160

161+
def test_toml_document_unwrap():
162+
content = """[tool.poetry]
163+
name = "foo"
164+
"""
165+
166+
doc = parse(content)
167+
unwrapped = doc.unwrap()
168+
assert_is_ppo(unwrapped, dict)
169+
assert_is_ppo(list(unwrapped.keys())[0], str)
170+
assert_is_ppo(unwrapped["tool"], dict)
171+
assert_is_ppo(list(unwrapped["tool"].keys())[0], str)
172+
assert_is_ppo(unwrapped["tool"]["poetry"]["name"], str)
173+
174+
157175
def test_toml_document_with_dotted_keys(example):
158176
content = example("0.5.0")
159177

tests/util.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from tomlkit.items import AoT
2+
from tomlkit.items import Array
3+
from tomlkit.items import Bool
4+
from tomlkit.items import Comment
5+
from tomlkit.items import Date
6+
from tomlkit.items import DateTime
7+
from tomlkit.items import Float
8+
from tomlkit.items import InlineTable
9+
from tomlkit.items import Integer
10+
from tomlkit.items import Item
11+
from tomlkit.items import KeyType
12+
from tomlkit.items import Null
13+
from tomlkit.items import SingleKey as Key
14+
from tomlkit.items import String
15+
from tomlkit.items import StringType
16+
from tomlkit.items import Table
17+
from tomlkit.items import Time
18+
from tomlkit.items import Trivia
19+
from tomlkit.toml_document import TOMLDocument
20+
21+
22+
TOMLKIT_TYPES = [
23+
Bool,
24+
Comment,
25+
InlineTable,
26+
Integer,
27+
Float,
28+
DateTime,
29+
Date,
30+
Time,
31+
Array,
32+
KeyType,
33+
Key,
34+
String,
35+
StringType,
36+
Table,
37+
Trivia,
38+
Item,
39+
AoT,
40+
Null,
41+
TOMLDocument,
42+
]
43+
44+
45+
def assert_not_tomlkit_type(v):
46+
for i, T in enumerate(TOMLKIT_TYPES):
47+
assert not isinstance(v, T)
48+
49+
50+
def assert_is_ppo(v_unwrapped, unwrappedType):
51+
assert_not_tomlkit_type(v_unwrapped)
52+
assert isinstance(v_unwrapped, unwrappedType)
53+
54+
55+
def elementary_test(v, unwrappedType):
56+
v_unwrapped = v.unwrap()
57+
assert_is_ppo(v_unwrapped, unwrappedType)

tomlkit/check.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
def is_tomlkit(v):
2+
from .container import Container
3+
from .container import OutOfOrderTableProxy
4+
from .items import Item as _Item
5+
6+
if isinstance(v, _Item):
7+
return True
8+
if isinstance(v, Container):
9+
return True
10+
if isinstance(v, OutOfOrderTableProxy):
11+
return True
12+
return False

tomlkit/container.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from ._compat import decode
1212
from ._utils import merge_dicts
13+
from .check import is_tomlkit
1314
from .exceptions import KeyAlreadyPresent
1415
from .exceptions import NonExistentKey
1516
from .exceptions import TOMLKitError
@@ -46,6 +47,25 @@ def __init__(self, parsed: bool = False) -> None:
4647
def body(self) -> List[Tuple[Optional[Key], Item]]:
4748
return self._body
4849

50+
def unwrap(self) -> str:
51+
unwrapped = {}
52+
for k, v in self.items():
53+
if k is None:
54+
continue
55+
56+
if not isinstance(k, str):
57+
k = k.key
58+
59+
if isinstance(v, Item):
60+
v = v.unwrap()
61+
62+
if k in unwrapped:
63+
merge_dicts(unwrapped[k], v)
64+
else:
65+
unwrapped[k] = v
66+
67+
return unwrapped
68+
4969
@property
5070
def value(self) -> Dict[Any, Any]:
5171
d = {}
@@ -796,6 +816,9 @@ def __init__(self, container: Container, indices: Tuple[int]) -> None:
796816
if k is not None:
797817
dict.__setitem__(self, k.key, v)
798818

819+
def unwrap(self) -> str:
820+
return self._internal_container.unwrap()
821+
799822
@property
800823
def value(self):
801824
return self._internal_container.value

0 commit comments

Comments
 (0)