Skip to content

Commit b7033ac

Browse files
authored
Update for dom-toml v2 (#60)
1 parent 1496c55 commit b7033ac

36 files changed

+209
-115
lines changed

formate.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ known_third_party = [
4848
"pytest_randomly",
4949
"pytest_timeout",
5050
"shippinglabel",
51-
"toml",
5251
"tomli",
5352
"typing_extensions",
5453
]

pyproject_parser/__init__.py

Lines changed: 151 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
#
88
# Copyright © 2021 Dominic Davis-Foster <dominic@davis-foster.co.uk>
99
#
10+
# PyProjectTomlEncoder.dumps based on https://github.com/hukkin/tomli-w
11+
# MIT Licensed
12+
# Copyright (c) 2021 Taneli Hukkinen
13+
#
1014
# Permission is hereby granted, free of charge, to any person obtaining a copy
1115
# of this software and associated documentation files (the "Software"), to deal
1216
# in the Software without restriction, including without limitation the rights
@@ -27,13 +31,26 @@
2731
#
2832

2933
# stdlib
30-
from typing import Any, ClassVar, Dict, Mapping, MutableMapping, Optional, Type, TypeVar, Union
34+
from typing import (
35+
Any,
36+
ClassVar,
37+
Dict,
38+
Iterator,
39+
List,
40+
Mapping,
41+
MutableMapping,
42+
Optional,
43+
Tuple,
44+
Type,
45+
TypeVar,
46+
Union
47+
)
3148

3249
# 3rd party
3350
import attr
3451
import dom_toml
35-
import toml
36-
from dom_toml.encoder import _dump_str
52+
from dom_toml.decoder import InlineTableDict
53+
from dom_toml.encoder import TomlEncoder
3754
from dom_toml.parser import AbstractConfigParser, BadConfigError
3855
from domdf_python_tools.paths import PathPlus, in_directory
3956
from domdf_python_tools.typing import PathLike
@@ -66,6 +83,29 @@
6683

6784
_PP = TypeVar("_PP", bound="PyProject")
6885

86+
_translation_table = {
87+
8: "\\b",
88+
9: "\\t",
89+
10: "\\n",
90+
12: "\\f",
91+
13: "\\r",
92+
92: "\\\\",
93+
}
94+
95+
96+
def _dump_str(v: str) -> str:
97+
v = str(v).translate(_translation_table)
98+
99+
if "'" in v and '"' not in v:
100+
quote_char = '"'
101+
elif '"' in v and "'" not in v:
102+
quote_char = "'"
103+
else:
104+
quote_char = '"'
105+
v = v.replace('"', '\\"')
106+
107+
return f"{quote_char}{v}{quote_char}"
108+
69109

70110
class PyProjectTomlEncoder(dom_toml.TomlEncoder):
71111
"""
@@ -76,14 +116,108 @@ class PyProjectTomlEncoder(dom_toml.TomlEncoder):
76116
.. autosummary-widths:: 23/64
77117
"""
78118

79-
def __init__(self, _dict=dict, preserve: bool = False) -> None: # noqa: MAN001
80-
super().__init__(_dict=_dict, preserve=preserve)
81-
self.dump_funcs[str] = _dump_str
82-
self.dump_funcs[_NormalisedName] = _dump_str
83-
self.dump_funcs[Version] = self.dump_packaging_types
84-
self.dump_funcs[Requirement] = self.dump_packaging_types
85-
self.dump_funcs[Marker] = self.dump_packaging_types
86-
self.dump_funcs[SpecifierSet] = self.dump_packaging_types
119+
def __init__(self, preserve: bool = False) -> None:
120+
super().__init__(preserve=preserve)
121+
122+
def dumps(
123+
self,
124+
table: Mapping[str, Any],
125+
*,
126+
name: str,
127+
inside_aot: bool = False,
128+
) -> Iterator[str]:
129+
"""
130+
Serialise the given table.
131+
132+
:param name: The table name.
133+
:param inside_aot:
134+
135+
:rtype:
136+
137+
.. versionadded:: 0.11.0
138+
"""
139+
140+
yielded = False
141+
literals = []
142+
tables: List[Tuple[str, Any, bool]] = []
143+
for k, v in table.items():
144+
if v is None:
145+
continue
146+
if self.preserve and isinstance(v, InlineTableDict):
147+
literals.append((k, v))
148+
elif isinstance(v, dict):
149+
tables.append((k, v, False))
150+
elif self._is_aot(v):
151+
tables.extend((k, t, True) for t in v)
152+
else:
153+
literals.append((k, v))
154+
155+
if inside_aot or name and (literals or not tables):
156+
yielded = True
157+
yield f"[[{name}]]\n" if inside_aot else f"[{name}]\n"
158+
159+
if literals:
160+
yielded = True
161+
for k, v in literals:
162+
yield f"{self.format_key_part(k)} = {self.format_literal(v)}\n"
163+
164+
for k, v, in_aot in tables:
165+
if yielded:
166+
yield '\n'
167+
else:
168+
yielded = True
169+
key_part = self.format_key_part(k)
170+
display_name = f"{name}.{key_part}" if name else key_part
171+
172+
yield from self.dumps(v, name=display_name, inside_aot=in_aot)
173+
174+
def format_literal(self, obj: object, *, nest_level: int = 0) -> str:
175+
"""
176+
Format a literal value.
177+
178+
:param obj:
179+
:param nest_level:
180+
181+
:rtype:
182+
183+
.. versionadded:: 0.11.0
184+
"""
185+
186+
if isinstance(obj, (str, _NormalisedName)):
187+
return _dump_str(obj)
188+
elif isinstance(obj, (Version, Requirement, Marker, SpecifierSet)):
189+
return self.dump_packaging_types(obj)
190+
else:
191+
return super().format_literal(obj, nest_level=nest_level)
192+
193+
def format_inline_array(self, obj: Union[Tuple, List], nest_level: int) -> str:
194+
"""
195+
Format an inline array.
196+
197+
:param obj:
198+
:param nest_level:
199+
200+
:rtype:
201+
202+
.. versionadded:: 0.11.0
203+
"""
204+
205+
if not len(obj):
206+
return "[]"
207+
208+
item_indent = " " * (1 + nest_level)
209+
closing_bracket_indent = " " * nest_level
210+
single_line = "[ " + ", ".join(
211+
self.format_literal(item, nest_level=nest_level + 1) for item in obj
212+
) + f",]"
213+
214+
if len(single_line) <= self.max_width:
215+
return single_line
216+
else:
217+
start = "[\n"
218+
body = ",\n".join(item_indent + self.format_literal(item, nest_level=nest_level + 1) for item in obj)
219+
end = f",\n{closing_bracket_indent}]"
220+
return start + body + end
87221

88222
@staticmethod
89223
def dump_packaging_types(obj: Union[Version, Requirement, Marker, SpecifierSet]) -> str:
@@ -227,12 +361,12 @@ def load(
227361

228362
def dumps(
229363
self,
230-
encoder: Union[Type[toml.TomlEncoder], toml.TomlEncoder] = PyProjectTomlEncoder,
364+
encoder: Union[Type[TomlEncoder], TomlEncoder] = PyProjectTomlEncoder,
231365
) -> str:
232366
"""
233367
Serialise to TOML.
234368
235-
:param encoder: The :class:`toml.TomlEncoder` to use for constructing the output string.
369+
:param encoder: The :class:`~dom_toml.encoder.TomlEncoder` to use for constructing the output string.
236370
"""
237371

238372
# TODO: filter out default values (lists and dicts)
@@ -250,7 +384,6 @@ def dumps(
250384
"license": toml_dict["project"]["license"].to_pep621_dict()
251385
}
252386

253-
if toml_dict["project"] is not None:
254387
if "readme" in toml_dict["project"] and toml_dict["project"]["readme"] is not None:
255388
readme_dict = toml_dict["project"]["readme"].to_pep621_dict()
256389

@@ -268,13 +401,13 @@ def dumps(
268401
def dump(
269402
self,
270403
filename: PathLike,
271-
encoder: Union[Type[toml.TomlEncoder], toml.TomlEncoder] = PyProjectTomlEncoder,
404+
encoder: Union[Type[TomlEncoder], TomlEncoder] = PyProjectTomlEncoder,
272405
) -> str:
273406
"""
274407
Write as TOML to the given file.
275408
276409
:param filename: The filename to write to.
277-
:param encoder: The :class:`toml.TomlEncoder` to use for constructing the output string.
410+
:param encoder: The :class:`~dom_toml.encoder.TomlEncoder` to use for constructing the output string.
278411
279412
:returns: A string containing the TOML representation.
280413
"""
@@ -288,13 +421,13 @@ def dump(
288421
def reformat(
289422
cls: Type[_PP],
290423
filename: PathLike,
291-
encoder: Union[Type[toml.TomlEncoder], toml.TomlEncoder] = PyProjectTomlEncoder,
424+
encoder: Union[Type[TomlEncoder], TomlEncoder] = PyProjectTomlEncoder,
292425
) -> str:
293426
"""
294427
Reformat the given ``pyproject.toml`` file.
295428
296429
:param filename: The file to reformat.
297-
:param encoder: The :class:`toml.TomlEncoder` to use for constructing the output string.
430+
:param encoder: The :class:`~dom_toml.encoder.TomlEncoder` to use for constructing the output string.
298431
299432
:returns: A string containing the reformatted TOML.
300433

pyproject_parser/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@
5252
if TYPE_CHECKING:
5353
# 3rd party
5454
from consolekit.terminal_colours import ColourTrilean
55+
from dom_toml.encoder import TomlEncoder
5556
from domdf_python_tools.typing import PathLike
56-
from toml import TomlEncoder
5757

5858
__all__ = ["main", "reformat", "check"]
5959

pyproject_parser/utils.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,11 @@
3434
from typing import TYPE_CHECKING, Any, Dict, Optional
3535

3636
# 3rd party
37+
import dom_toml
3738
from dom_toml.parser import BadConfigError
3839
from domdf_python_tools.paths import PathPlus
3940
from domdf_python_tools.typing import PathLike
4041

41-
if sys.version_info < (3, 11):
42-
# 3rd party
43-
import tomli as tomllib
44-
else:
45-
# stdlib
46-
import tomllib
47-
4842
if TYPE_CHECKING:
4943
# this package
5044
from pyproject_parser.type_hints import ContentTypes
@@ -192,4 +186,4 @@ def _load_toml(filename: PathLike, ) -> Dict[str, Any]:
192186
:returns: A mapping containing the ``TOML`` data.
193187
"""
194188

195-
return tomllib.loads(PathPlus(filename).read_text())
189+
return dom_toml.load(filename)

requirements.txt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
apeye-core>=1.0.0
22
attrs>=20.3.0
3-
dom-toml>=0.4.0
3+
dom-toml>=2.0.0b1
44
domdf-python-tools>=2.8.0
55
natsort>=7.1.1
66
packaging>=20.9
77
shippinglabel>=1.0.0
8-
toml>=0.10.2
9-
tomli>=1.2.3; python_version < "3.11"
108
typing-extensions!=4.7.0,>=3.7.4.3

tests/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# git+https://github.com/repo-helper/pyproject-examples
2+
git+https://github.com/domdfcoding/dom_toml@v2
23
coincidence>=0.2.0
34
coverage>=5.1
45
coverage-pyver-pragma>=0.2.1

tests/test_cli_/test_reformat_False_COMPLETE_A_.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ dynamic = [ "classifiers", "requires-python",]
1414
name = "Dominic Davis-Foster"
1515
email = "dominic@davis-foster.co.uk"
1616

17-
1817
[project.urls]
1918
Homepage = "https://whey.readthedocs.io/en/latest"
2019
Documentation = "https://whey.readthedocs.io/en/latest"

tests/test_cli_/test_reformat_False_COMPLETE_A_WITH_FILES_.toml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,13 @@ keywords = [ "build", "distribution", "packaging", "pep517", "pep621", "sdist",
1111
dependencies = [ 'django>2.1; os_name != "nt"', 'django>2.0; os_name == "nt"', "gidgethub[httpx]>4.0.0", "httpx",]
1212
dynamic = [ "classifiers", "requires-python",]
1313

14+
[project.license]
15+
file = "LICENSE"
16+
1417
[[project.authors]]
1518
name = "Dominic Davis-Foster"
1619
email = "dominic@davis-foster.co.uk"
1720

18-
19-
[project.license]
20-
file = "LICENSE"
21-
2221
[project.urls]
2322
Homepage = "https://whey.readthedocs.io/en/latest"
2423
Documentation = "https://whey.readthedocs.io/en/latest"

tests/test_cli_/test_reformat_False_COMPLETE_B_.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ dynamic = [ "classifiers", "requires-python",]
1414
name = "Dominic Davis-Foster"
1515
email = "dominic@davis-foster.co.uk"
1616

17-
1817
[project.urls]
1918
Homepage = "https://whey.readthedocs.io/en/latest"
2019
Documentation = "https://whey.readthedocs.io/en/latest"

tests/test_cli_/test_reformat_False_COMPLETE_PROJECT_A_.toml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ name = "Tzu-Ping Chung"
1818
name = "Brett Cannon"
1919
email = "brett@python.org"
2020

21-
22-
[tool]
23-
2421
[project.urls]
2522
homepage = "example.com"
2623
documentation = "readthedocs.org"
@@ -33,8 +30,10 @@ spam-cli = "spam:main_cli"
3330
[project.gui-scripts]
3431
spam-gui = "spam:main_gui"
3532

33+
[project.entry-points."spam.magical"]
34+
tomatoes = "spam:main_tomatoes"
35+
3636
[project.optional-dependencies]
3737
test = [ "pytest<5.0.0", "pytest-cov[all]",]
3838

39-
[project.entry-points."spam.magical"]
40-
tomatoes = "spam:main_tomatoes"
39+
[tool]

tests/test_cli_/test_reformat_False_COMPLETE_UNDERSCORE_NAME_.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ dynamic = [ "classifiers", "requires-python",]
1414
name = "Dominic Davis-Foster"
1515
email = "dominic@davis-foster.co.uk"
1616

17-
1817
[project.urls]
1918
Homepage = "https://whey.readthedocs.io/en/latest"
2019
Documentation = "https://whey.readthedocs.io/en/latest"

tests/test_cli_/test_reformat_False_UNORDERED_.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ dynamic = [ "classifiers", "requires-python",]
1414
name = "Dominic Davis-Foster"
1515
email = "dominic@davis-foster.co.uk"
1616

17-
1817
[project.urls]
1918
Homepage = "https://whey.readthedocs.io/en/latest"
2019
Documentation = "https://whey.readthedocs.io/en/latest"

tests/test_cli_/test_reformat_True_COMPLETE_A_.diff

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Reformatting 'pyproject.toml'
22
--- pyproject.toml (original)
33
+++ pyproject.toml (reformatted)
4-
@@ -6,18 +6,14 @@
4+
@@ -6,18 +6,13 @@
55
name = "whey"
66
version = "2021.0.0"
77
description = "A simple Python wheel builder for simple projects."
@@ -20,7 +20,6 @@ Reformatting 'pyproject.toml'
2020
+name = "Dominic Davis-Foster"
2121
email = "dominic@davis-foster.co.uk"
2222
-name = "Dominic Davis-Foster"
23-
+
2423

2524
[project.urls]
2625
Homepage = "https://whey.readthedocs.io/en/latest"

0 commit comments

Comments
 (0)