|
from __future__ import annotations |
|
|
|
import contextlib |
|
import datetime as _datetime |
|
|
|
from collections.abc import Mapping |
|
from typing import IO |
|
from typing import Iterable |
|
from typing import TypeVar |
|
|
|
from tomlkit._utils import parse_rfc3339 |
|
from tomlkit.container import Container |
|
from tomlkit.exceptions import UnexpectedCharError |
|
from tomlkit.items import CUSTOM_ENCODERS |
|
from tomlkit.items import AoT |
|
from tomlkit.items import Array |
|
from tomlkit.items import Bool |
|
from tomlkit.items import Comment |
|
from tomlkit.items import Date |
|
from tomlkit.items import DateTime |
|
from tomlkit.items import DottedKey |
|
from tomlkit.items import Encoder |
|
from tomlkit.items import Float |
|
from tomlkit.items import InlineTable |
|
from tomlkit.items import Integer |
|
from tomlkit.items import Item as _Item |
|
from tomlkit.items import Key |
|
from tomlkit.items import SingleKey |
|
from tomlkit.items import String |
|
from tomlkit.items import StringType as _StringType |
|
from tomlkit.items import Table |
|
from tomlkit.items import Time |
|
from tomlkit.items import Trivia |
|
from tomlkit.items import Whitespace |
|
from tomlkit.items import item |
|
from tomlkit.parser import Parser |
|
from tomlkit.toml_document import TOMLDocument |
|
|
|
|
|
def loads(string: str | bytes) -> TOMLDocument: |
|
""" |
|
Parses a string into a TOMLDocument. |
|
|
|
Alias for parse(). |
|
""" |
|
return parse(string) |
|
|
|
|
|
def dumps(data: Mapping, sort_keys: bool = False) -> str: |
|
""" |
|
Dumps a TOMLDocument into a string. |
|
""" |
|
if not isinstance(data, (Table, InlineTable, Container)) and isinstance( |
|
data, Mapping |
|
): |
|
data = item(dict(data), _sort_keys=sort_keys) |
|
|
|
try: |
|
|
|
|
|
return data.as_string() |
|
except AttributeError as ex: |
|
msg = f"Expecting Mapping or TOML Table or Container, {type(data)} given" |
|
raise TypeError(msg) from ex |
|
|
|
|
|
def load(fp: IO[str] | IO[bytes]) -> TOMLDocument: |
|
""" |
|
Load toml document from a file-like object. |
|
""" |
|
return parse(fp.read()) |
|
|
|
|
|
def dump(data: Mapping, fp: IO[str], *, sort_keys: bool = False) -> None: |
|
""" |
|
Dump a TOMLDocument into a writable file stream. |
|
|
|
:param data: a dict-like object to dump |
|
:param sort_keys: if true, sort the keys in alphabetic order |
|
|
|
:Example: |
|
|
|
>>> with open("output.toml", "w") as fp: |
|
... tomlkit.dump(data, fp) |
|
""" |
|
fp.write(dumps(data, sort_keys=sort_keys)) |
|
|
|
|
|
def parse(string: str | bytes) -> TOMLDocument: |
|
""" |
|
Parses a string or bytes into a TOMLDocument. |
|
""" |
|
return Parser(string).parse() |
|
|
|
|
|
def document() -> TOMLDocument: |
|
""" |
|
Returns a new TOMLDocument instance. |
|
""" |
|
return TOMLDocument() |
|
|
|
|
|
|
|
def integer(raw: str | int) -> Integer: |
|
"""Create an integer item from a number or string.""" |
|
return item(int(raw)) |
|
|
|
|
|
def float_(raw: str | float) -> Float: |
|
"""Create an float item from a number or string.""" |
|
return item(float(raw)) |
|
|
|
|
|
def boolean(raw: str) -> Bool: |
|
"""Turn `true` or `false` into a boolean item.""" |
|
return item(raw == "true") |
|
|
|
|
|
def string( |
|
raw: str, |
|
*, |
|
literal: bool = False, |
|
multiline: bool = False, |
|
escape: bool = True, |
|
) -> String: |
|
"""Create a string item. |
|
|
|
By default, this function will create *single line basic* strings, but |
|
boolean flags (e.g. ``literal=True`` and/or ``multiline=True``) |
|
can be used for personalization. |
|
|
|
For more information, please check the spec: `<https://toml.io/en/v1.0.0#string>`__. |
|
|
|
Common escaping rules will be applied for basic strings. |
|
This can be controlled by explicitly setting ``escape=False``. |
|
Please note that, if you disable escaping, you will have to make sure that |
|
the given strings don't contain any forbidden character or sequence. |
|
""" |
|
type_ = _StringType.select(literal, multiline) |
|
return String.from_raw(raw, type_, escape) |
|
|
|
|
|
def date(raw: str) -> Date: |
|
"""Create a TOML date.""" |
|
value = parse_rfc3339(raw) |
|
if not isinstance(value, _datetime.date): |
|
raise ValueError("date() only accepts date strings.") |
|
|
|
return item(value) |
|
|
|
|
|
def time(raw: str) -> Time: |
|
"""Create a TOML time.""" |
|
value = parse_rfc3339(raw) |
|
if not isinstance(value, _datetime.time): |
|
raise ValueError("time() only accepts time strings.") |
|
|
|
return item(value) |
|
|
|
|
|
def datetime(raw: str) -> DateTime: |
|
"""Create a TOML datetime.""" |
|
value = parse_rfc3339(raw) |
|
if not isinstance(value, _datetime.datetime): |
|
raise ValueError("datetime() only accepts datetime strings.") |
|
|
|
return item(value) |
|
|
|
|
|
def array(raw: str = "[]") -> Array: |
|
"""Create an array item for its string representation. |
|
|
|
:Example: |
|
|
|
>>> array("[1, 2, 3]") # Create from a string |
|
[1, 2, 3] |
|
>>> a = array() |
|
>>> a.extend([1, 2, 3]) # Create from a list |
|
>>> a |
|
[1, 2, 3] |
|
""" |
|
return value(raw) |
|
|
|
|
|
def table(is_super_table: bool | None = None) -> Table: |
|
"""Create an empty table. |
|
|
|
:param is_super_table: if true, the table is a super table |
|
|
|
:Example: |
|
|
|
>>> doc = document() |
|
>>> foo = table(True) |
|
>>> bar = table() |
|
>>> bar.update({'x': 1}) |
|
>>> foo.append('bar', bar) |
|
>>> doc.append('foo', foo) |
|
>>> print(doc.as_string()) |
|
[foo.bar] |
|
x = 1 |
|
""" |
|
return Table(Container(), Trivia(), False, is_super_table) |
|
|
|
|
|
def inline_table() -> InlineTable: |
|
"""Create an inline table. |
|
|
|
:Example: |
|
|
|
>>> table = inline_table() |
|
>>> table.update({'x': 1, 'y': 2}) |
|
>>> print(table.as_string()) |
|
{x = 1, y = 2} |
|
""" |
|
return InlineTable(Container(), Trivia(), new=True) |
|
|
|
|
|
def aot() -> AoT: |
|
"""Create an array of table. |
|
|
|
:Example: |
|
|
|
>>> doc = document() |
|
>>> aot = aot() |
|
>>> aot.append(item({'x': 1})) |
|
>>> doc.append('foo', aot) |
|
>>> print(doc.as_string()) |
|
[[foo]] |
|
x = 1 |
|
""" |
|
return AoT([]) |
|
|
|
|
|
def key(k: str | Iterable[str]) -> Key: |
|
"""Create a key from a string. When a list of string is given, |
|
it will create a dotted key. |
|
|
|
:Example: |
|
|
|
>>> doc = document() |
|
>>> doc.append(key('foo'), 1) |
|
>>> doc.append(key(['bar', 'baz']), 2) |
|
>>> print(doc.as_string()) |
|
foo = 1 |
|
bar.baz = 2 |
|
""" |
|
if isinstance(k, str): |
|
return SingleKey(k) |
|
return DottedKey([key(_k) for _k in k]) |
|
|
|
|
|
def value(raw: str) -> _Item: |
|
"""Parse a simple value from a string. |
|
|
|
:Example: |
|
|
|
>>> value("1") |
|
1 |
|
>>> value("true") |
|
True |
|
>>> value("[1, 2, 3]") |
|
[1, 2, 3] |
|
""" |
|
parser = Parser(raw) |
|
v = parser._parse_value() |
|
if not parser.end(): |
|
raise parser.parse_error(UnexpectedCharError, char=parser._current) |
|
return v |
|
|
|
|
|
def key_value(src: str) -> tuple[Key, _Item]: |
|
"""Parse a key-value pair from a string. |
|
|
|
:Example: |
|
|
|
>>> key_value("foo = 1") |
|
(Key('foo'), 1) |
|
""" |
|
return Parser(src)._parse_key_value() |
|
|
|
|
|
def ws(src: str) -> Whitespace: |
|
"""Create a whitespace from a string.""" |
|
return Whitespace(src, fixed=True) |
|
|
|
|
|
def nl() -> Whitespace: |
|
"""Create a newline item.""" |
|
return ws("\n") |
|
|
|
|
|
def comment(string: str) -> Comment: |
|
"""Create a comment item.""" |
|
return Comment(Trivia(comment_ws=" ", comment="# " + string)) |
|
|
|
|
|
E = TypeVar("E", bound=Encoder) |
|
|
|
|
|
def register_encoder(encoder: E) -> E: |
|
"""Add a custom encoder, which should be a function that will be called |
|
if the value can't otherwise be converted. It should takes a single value |
|
and return a TOMLKit item or raise a ``ConvertError``. |
|
""" |
|
CUSTOM_ENCODERS.append(encoder) |
|
return encoder |
|
|
|
|
|
def unregister_encoder(encoder: Encoder) -> None: |
|
"""Unregister a custom encoder.""" |
|
with contextlib.suppress(ValueError): |
|
CUSTOM_ENCODERS.remove(encoder) |
|
|