Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/tomlkit/api.py: 50%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from __future__ import annotations
3import contextlib
4import datetime as _datetime
5import re
7from collections.abc import Iterable
8from collections.abc import Mapping
9from typing import IO
10from typing import TYPE_CHECKING
11from typing import Any
12from typing import TypeVar
14from tomlkit._utils import parse_rfc3339
15from tomlkit.container import Container
16from tomlkit.exceptions import UnexpectedCharError
17from tomlkit.items import CUSTOM_ENCODERS
18from tomlkit.items import AoT
19from tomlkit.items import Array
20from tomlkit.items import Bool
21from tomlkit.items import Comment
22from tomlkit.items import Date
23from tomlkit.items import DateTime
24from tomlkit.items import DottedKey
25from tomlkit.items import Float
26from tomlkit.items import InlineTable
27from tomlkit.items import Integer
28from tomlkit.items import Item as _Item
29from tomlkit.items import Key
30from tomlkit.items import SingleKey
31from tomlkit.items import String
32from tomlkit.items import StringType as _StringType
33from tomlkit.items import Table
34from tomlkit.items import Time
35from tomlkit.items import Trivia
36from tomlkit.items import Whitespace
37from tomlkit.items import item as item
38from tomlkit.parser import Parser
39from tomlkit.toml_document import TOMLDocument as TOMLDocument
42if TYPE_CHECKING:
43 from tomlkit.items import Encoder
45 E = TypeVar("E", bound=Encoder)
48def loads(string: str | bytes) -> TOMLDocument:
49 """
50 Parses a string into a TOMLDocument.
52 Alias for parse().
53 """
54 return parse(string)
57def dumps(data: Mapping[str, Any], sort_keys: bool = False) -> str:
58 """
59 Dumps a TOMLDocument into a string.
60 """
61 if isinstance(data, (Table, InlineTable, Container)):
62 if not sort_keys:
63 return data.as_string()
65 return item(data, _sort_keys=True).as_string()
67 if isinstance(data, Mapping):
68 return item(dict(data), _sort_keys=sort_keys).as_string()
70 try:
71 # mapping-like wrappers (e.g. dotty_dict's Dotty) delegate
72 # ``as_string`` to the document they wrap; rendering it directly
73 # preserves the original layout, which re-encoding through a plain
74 # dict would lose
75 return data.as_string() # type: ignore[attr-defined]
76 except AttributeError as ex:
77 msg = f"Expecting Mapping or TOML Table or Container, {type(data)} given"
78 raise TypeError(msg) from ex
81def load(fp: IO[str] | IO[bytes]) -> TOMLDocument:
82 """
83 Load toml document from a file-like object.
84 """
85 return parse(fp.read())
88def dump(data: Mapping[str, Any], fp: IO[str], *, sort_keys: bool = False) -> None:
89 """
90 Dump a TOMLDocument into a writable file stream.
92 :param data: a dict-like object to dump
93 :param sort_keys: if true, sort the keys in alphabetic order
95 :Example:
97 >>> with open("output.toml", "w") as fp:
98 ... tomlkit.dump(data, fp)
99 """
100 fp.write(dumps(data, sort_keys=sort_keys))
103def parse(string: str | bytes) -> TOMLDocument:
104 """
105 Parses a string or bytes into a TOMLDocument.
106 """
107 return Parser(string).parse()
110def document() -> TOMLDocument:
111 """
112 Returns a new TOMLDocument instance.
113 """
114 return TOMLDocument()
117# Items
118_INTEGER_RE = re.compile(
119 r"(?:"
120 r"0x[0-9a-fA-F](?:_?[0-9a-fA-F])*" # hex: lowercase prefix only, no sign
121 r"|0o[0-7](?:_?[0-7])*" # octal: lowercase prefix only, no sign
122 r"|0b[01](?:_?[01])*" # binary: lowercase prefix only, no sign
123 r"|[+-]?(?:0|[1-9](?:_?[0-9])*)" # decimal: optional sign, no leading zeros
124 r")"
125)
126"""Valid TOML v1.0.0 integer literals (https://toml.io/en/v1.0.0#integer).
128Underscores may only appear between digits; hex/octal/binary use a
129lowercase prefix and take no sign, mirroring ``Parser._parse_number``.
130"""
133def integer(raw: str | int) -> Integer:
134 """Create an integer item from a number or string.
136 When ``raw`` is a string it must be a valid TOML v1.0.0 integer
137 literal; the original text is then preserved verbatim so grouping
138 underscores (e.g. ``"1_000"``) survive ``dumps()``. Anything else
139 raises ``ValueError``.
140 """
141 if isinstance(raw, str):
142 if not _INTEGER_RE.fullmatch(raw):
143 raise ValueError(f"Invalid TOML integer: {raw!r}")
144 return Integer(int(raw, 0), Trivia(), raw)
145 return item(int(raw))
148def float_(raw: str | float) -> Float:
149 """Create an float item from a number or string."""
150 return item(float(raw))
153def boolean(raw: str | bool) -> Bool:
154 """Turn `true` or `false` into a boolean item."""
155 return item(raw == "true" if isinstance(raw, str) else raw)
158def string(
159 raw: str,
160 *,
161 literal: bool = False,
162 multiline: bool = False,
163 escape: bool = True,
164) -> String:
165 """Create a string item.
167 By default, this function will create *single line basic* strings, but
168 boolean flags (e.g. ``literal=True`` and/or ``multiline=True``)
169 can be used for personalization.
171 For more information, please check the spec: `<https://toml.io/en/v1.0.0#string>`__.
173 Dumping produces a TOML string that faithfully represents the given string.
174 This includes character escaping for basic strings.
175 This behaviour can be disabled by setting ``escape=False``.
176 In that case, the given string is used verbatim for the content of the TOML string.
177 Please note that this makes you responsible for ensuring the given string does not
178 contain any forbidden character or sequence.
179 """
180 type_ = _StringType.select(literal, multiline)
181 return String.from_raw(raw, type_, escape)
184def date(raw: str) -> Date:
185 """Create a TOML date."""
186 value = parse_rfc3339(raw)
187 if isinstance(value, _datetime.datetime) or not isinstance(value, _datetime.date):
188 raise ValueError("date() only accepts date strings.")
190 return item(value)
193def time(raw: str) -> Time:
194 """Create a TOML time."""
195 value = parse_rfc3339(raw)
196 if not isinstance(value, _datetime.time):
197 raise ValueError("time() only accepts time strings.")
199 return item(value)
202def datetime(raw: str) -> DateTime:
203 """Create a TOML datetime."""
204 value = parse_rfc3339(raw)
205 if not isinstance(value, _datetime.datetime):
206 raise ValueError("datetime() only accepts datetime strings.")
208 return item(value)
211def array(raw: str = "[]") -> Array:
212 """Create an array item for its string representation.
214 :Example:
216 >>> array("[1, 2, 3]") # Create from a string
217 [1, 2, 3]
218 >>> a = array()
219 >>> a.extend([1, 2, 3]) # Create from a list
220 >>> a
221 [1, 2, 3]
222 """
223 v = value(raw)
224 if not isinstance(v, Array):
225 raise ValueError(f"Expected an array, got {type(v)}")
226 return v
229def table(is_super_table: bool | None = None) -> Table:
230 """Create an empty table.
232 :param is_super_table: if true, the table is a super table
234 :Example:
236 >>> doc = document()
237 >>> foo = table(True)
238 >>> bar = table()
239 >>> bar.update({'x': 1})
240 >>> foo.append('bar', bar)
241 >>> doc.append('foo', foo)
242 >>> print(doc.as_string())
243 [foo.bar]
244 x = 1
245 """
246 return Table(Container(), Trivia(), False, is_super_table)
249def inline_table() -> InlineTable:
250 """Create an inline table.
252 :Example:
254 >>> table = inline_table()
255 >>> table.update({'x': 1, 'y': 2})
256 >>> print(table.as_string())
257 {x = 1, y = 2}
258 """
259 return InlineTable(Container(), Trivia(), new=True)
262def aot() -> AoT:
263 """Create an array of table.
265 :Example:
267 >>> doc = document()
268 >>> aot = aot()
269 >>> aot.append(item({'x': 1}))
270 >>> doc.append('foo', aot)
271 >>> print(doc.as_string())
272 [[foo]]
273 x = 1
274 """
275 return AoT([])
278def key(k: str | Iterable[str]) -> Key:
279 """Create a key from a string. When a list of string is given,
280 it will create a dotted key.
282 :Example:
284 >>> doc = document()
285 >>> doc.append(key('foo'), 1)
286 >>> doc.append(key(['bar', 'baz']), 2)
287 >>> print(doc.as_string())
288 foo = 1
289 bar.baz = 2
290 """
291 if isinstance(k, str):
292 return SingleKey(k)
293 keys = [SingleKey(_k) for _k in k]
294 if len(keys) == 1:
295 return keys[0]
296 return DottedKey(keys)
299def value(raw: str) -> _Item:
300 """Parse a simple value from a string.
302 :Example:
304 >>> value("1")
305 1
306 >>> value("true")
307 True
308 >>> value("[1, 2, 3]")
309 [1, 2, 3]
310 """
311 parser = Parser(raw)
312 v = parser._parse_value()
313 if not parser.end():
314 raise parser.parse_error(UnexpectedCharError, char=parser._current)
315 return v
318def key_value(src: str) -> tuple[Key, _Item]:
319 """Parse a key-value pair from a string.
321 Anything other than whitespace or a comment after the pair is an error,
322 as it is when the same text is given to :func:`parse`.
324 :Example:
326 >>> key_value("foo = 1")
327 (Key('foo'), 1)
328 """
329 parser = Parser(src)
330 k, v = parser._parse_key_value(parse_comment=True)
331 if not parser.end():
332 raise parser.parse_error(UnexpectedCharError, char=parser._current)
333 return k, v
336def ws(src: str) -> Whitespace:
337 """Create a whitespace from a string."""
338 return Whitespace(src, fixed=True)
341def nl() -> Whitespace:
342 """Create a newline item."""
343 return ws("\n")
346def comment(string: str) -> Comment:
347 """Create a comment item.
349 A multiline string produces one ``#``-prefixed line per line so that the
350 result is still valid TOML.
351 """
352 lines = string.split("\n")
353 rendered = "\n".join(f"# {line}" if line else "#" for line in lines)
354 return Comment(Trivia(comment_ws=" ", comment=rendered))
357def register_encoder(encoder: E) -> E:
358 """Add a custom encoder, which should be a function that will be called
359 if the value can't otherwise be converted.
361 The encoder should return a TOMLKit item or raise a ``ConvertError``.
363 Example::
365 @register_encoder
366 def encode_custom_dict(obj, _parent=None, _sort_keys=False):
367 if isinstance(obj, CustomDict):
368 tbl = table()
369 for key, value in obj.items():
370 # Pass along parameters when encoding nested values
371 tbl[key] = item(value, _parent=tbl, _sort_keys=_sort_keys)
372 return tbl
373 raise ConvertError("Not a CustomDict")
374 """
375 CUSTOM_ENCODERS.append(encoder)
376 return encoder
379def unregister_encoder(encoder: Encoder) -> None:
380 """Unregister a custom encoder."""
381 with contextlib.suppress(ValueError):
382 CUSTOM_ENCODERS.remove(encoder)