mirror of
https://github.com/kennethreitz/pipenv.git
synced 2026-06-05 22:50:18 +00:00
Update tomlkit==0.9.2
Used: python -m invoke vendoring.update --package=tomlkit
This commit is contained in:
Vendored
+30
-1
@@ -1,3 +1,4 @@
|
||||
from .api import TOMLDocument
|
||||
from .api import aot
|
||||
from .api import array
|
||||
from .api import boolean
|
||||
@@ -5,6 +6,7 @@ from .api import comment
|
||||
from .api import date
|
||||
from .api import datetime
|
||||
from .api import document
|
||||
from .api import dump
|
||||
from .api import dumps
|
||||
from .api import float_
|
||||
from .api import inline_table
|
||||
@@ -12,6 +14,7 @@ from .api import integer
|
||||
from .api import item
|
||||
from .api import key
|
||||
from .api import key_value
|
||||
from .api import load
|
||||
from .api import loads
|
||||
from .api import nl
|
||||
from .api import parse
|
||||
@@ -22,4 +25,30 @@ from .api import value
|
||||
from .api import ws
|
||||
|
||||
|
||||
__version__ = "0.7.2"
|
||||
__version__ = "0.9.2"
|
||||
__all__ = [
|
||||
"aot",
|
||||
"array",
|
||||
"boolean",
|
||||
"comment",
|
||||
"date",
|
||||
"datetime",
|
||||
"document",
|
||||
"dump",
|
||||
"dumps",
|
||||
"float_",
|
||||
"inline_table",
|
||||
"integer",
|
||||
"item",
|
||||
"key",
|
||||
"key_value",
|
||||
"load",
|
||||
"loads",
|
||||
"nl",
|
||||
"parse",
|
||||
"string",
|
||||
"table",
|
||||
"time",
|
||||
"value",
|
||||
"ws",
|
||||
]
|
||||
|
||||
Vendored
+5
-161
@@ -1,171 +1,15 @@
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
try:
|
||||
from datetime import timezone
|
||||
except ImportError:
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
from datetime import tzinfo
|
||||
|
||||
class timezone(tzinfo):
|
||||
__slots__ = "_offset", "_name"
|
||||
|
||||
# Sentinel value to disallow None
|
||||
_Omitted = object()
|
||||
|
||||
def __new__(cls, offset, name=_Omitted):
|
||||
if not isinstance(offset, timedelta):
|
||||
raise TypeError("offset must be a timedelta")
|
||||
if name is cls._Omitted:
|
||||
if not offset:
|
||||
return cls.utc
|
||||
name = None
|
||||
elif not isinstance(name, str):
|
||||
raise TypeError("name must be a string")
|
||||
if not cls._minoffset <= offset <= cls._maxoffset:
|
||||
raise ValueError(
|
||||
"offset must be a timedelta "
|
||||
"strictly between -timedelta(hours=24) and "
|
||||
"timedelta(hours=24)."
|
||||
)
|
||||
return cls._create(offset, name)
|
||||
|
||||
@classmethod
|
||||
def _create(cls, offset, name=None):
|
||||
self = tzinfo.__new__(cls)
|
||||
self._offset = offset
|
||||
self._name = name
|
||||
return self
|
||||
|
||||
def __getinitargs__(self):
|
||||
"""pickle support"""
|
||||
if self._name is None:
|
||||
return (self._offset,)
|
||||
return (self._offset, self._name)
|
||||
|
||||
def __eq__(self, other):
|
||||
if type(other) != timezone:
|
||||
return False
|
||||
return self._offset == other._offset
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self._offset)
|
||||
|
||||
def __repr__(self):
|
||||
"""Convert to formal string, for repr().
|
||||
|
||||
>>> tz = timezone.utc
|
||||
>>> repr(tz)
|
||||
'datetime.timezone.utc'
|
||||
>>> tz = timezone(timedelta(hours=-5), 'EST')
|
||||
>>> repr(tz)
|
||||
"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')"
|
||||
"""
|
||||
if self is self.utc:
|
||||
return "datetime.timezone.utc"
|
||||
if self._name is None:
|
||||
return "%s.%s(%r)" % (
|
||||
self.__class__.__module__,
|
||||
self.__class__.__name__,
|
||||
self._offset,
|
||||
)
|
||||
return "%s.%s(%r, %r)" % (
|
||||
self.__class__.__module__,
|
||||
self.__class__.__name__,
|
||||
self._offset,
|
||||
self._name,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.tzname(None)
|
||||
|
||||
def utcoffset(self, dt):
|
||||
if isinstance(dt, datetime) or dt is None:
|
||||
return self._offset
|
||||
raise TypeError(
|
||||
"utcoffset() argument must be a datetime instance" " or None"
|
||||
)
|
||||
|
||||
def tzname(self, dt):
|
||||
if isinstance(dt, datetime) or dt is None:
|
||||
if self._name is None:
|
||||
return self._name_from_offset(self._offset)
|
||||
return self._name
|
||||
raise TypeError("tzname() argument must be a datetime instance" " or None")
|
||||
|
||||
def dst(self, dt):
|
||||
if isinstance(dt, datetime) or dt is None:
|
||||
return None
|
||||
raise TypeError("dst() argument must be a datetime instance" " or None")
|
||||
|
||||
def fromutc(self, dt):
|
||||
if isinstance(dt, datetime):
|
||||
if dt.tzinfo is not self:
|
||||
raise ValueError("fromutc: dt.tzinfo " "is not self")
|
||||
return dt + self._offset
|
||||
raise TypeError("fromutc() argument must be a datetime instance" " or None")
|
||||
|
||||
_maxoffset = timedelta(hours=23, minutes=59)
|
||||
_minoffset = -_maxoffset
|
||||
|
||||
@staticmethod
|
||||
def _name_from_offset(delta):
|
||||
if not delta:
|
||||
return "UTC"
|
||||
if delta < timedelta(0):
|
||||
sign = "-"
|
||||
delta = -delta
|
||||
else:
|
||||
sign = "+"
|
||||
hours, rest = divmod(delta, timedelta(hours=1))
|
||||
minutes, rest = divmod(rest, timedelta(minutes=1))
|
||||
seconds = rest.seconds
|
||||
microseconds = rest.microseconds
|
||||
if microseconds:
|
||||
return ("UTC{}{:02d}:{:02d}:{:02d}.{:06d}").format(
|
||||
sign, hours, minutes, seconds, microseconds
|
||||
)
|
||||
if seconds:
|
||||
return "UTC{}{:02d}:{:02d}:{:02d}".format(sign, hours, minutes, seconds)
|
||||
return "UTC{}{:02d}:{:02d}".format(sign, hours, minutes)
|
||||
|
||||
timezone.utc = timezone._create(timedelta(0))
|
||||
timezone.min = timezone._create(timezone._minoffset)
|
||||
timezone.max = timezone._create(timezone._maxoffset)
|
||||
from typing import Any
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
PY36 = sys.version_info >= (3, 6)
|
||||
PY38 = sys.version_info >= (3, 8)
|
||||
|
||||
if PY2:
|
||||
unicode = unicode
|
||||
chr = unichr
|
||||
long = long
|
||||
else:
|
||||
unicode = str
|
||||
chr = chr
|
||||
long = int
|
||||
|
||||
|
||||
if PY36:
|
||||
OrderedDict = dict
|
||||
else:
|
||||
from collections import OrderedDict
|
||||
|
||||
try:
|
||||
from collections.abc import MutableMapping
|
||||
except ImportError:
|
||||
from collections import MutableMapping
|
||||
|
||||
|
||||
def decode(string, encodings=None):
|
||||
if not PY2 and not isinstance(string, bytes):
|
||||
return string
|
||||
|
||||
if PY2 and isinstance(string, unicode):
|
||||
def decode(string: Any, encodings: Optional[List[str]] = None):
|
||||
if not isinstance(string, bytes):
|
||||
return string
|
||||
|
||||
encodings = encodings or ["utf-8", "latin1", "ascii"]
|
||||
|
||||
Vendored
+15
-22
@@ -1,28 +1,23 @@
|
||||
import re
|
||||
|
||||
from collections.abc import Mapping
|
||||
from datetime import date
|
||||
from datetime import datetime
|
||||
from datetime import time
|
||||
from datetime import timedelta
|
||||
from datetime import timezone
|
||||
from typing import Union
|
||||
|
||||
from ._compat import decode
|
||||
from ._compat import timezone
|
||||
|
||||
|
||||
try:
|
||||
from collections.abc import Mapping
|
||||
except ImportError:
|
||||
from collections import Mapping
|
||||
|
||||
|
||||
RFC_3339_LOOSE = re.compile(
|
||||
"^"
|
||||
r"(([0-9]+)-(\d{2})-(\d{2}))?" # Date
|
||||
"("
|
||||
"([T ])?" # Separator
|
||||
"([Tt ])?" # Separator
|
||||
r"(\d{2}):(\d{2}):(\d{2})(\.([0-9]+))?" # Time
|
||||
r"((Z)|([\+|\-]([01][0-9]|2[0-3]):([0-5][0-9])))?" # Timezone
|
||||
r"(([Zz])|([\+|\-]([01][0-9]|2[0-3]):([0-5][0-9])))?" # Timezone
|
||||
")?"
|
||||
"$"
|
||||
)
|
||||
@@ -30,9 +25,9 @@ RFC_3339_LOOSE = re.compile(
|
||||
RFC_3339_DATETIME = re.compile(
|
||||
"^"
|
||||
"([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])" # Date
|
||||
"[T ]" # Separator
|
||||
"[Tt ]" # Separator
|
||||
r"([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.([0-9]+))?" # Time
|
||||
r"((Z)|([\+|\-]([01][0-9]|2[0-3]):([0-5][0-9])))?" # Timezone
|
||||
r"(([Zz])|([\+|\-]([01][0-9]|2[0-3]):([0-5][0-9])))?" # Timezone
|
||||
"$"
|
||||
)
|
||||
|
||||
@@ -45,7 +40,7 @@ RFC_3339_TIME = re.compile(
|
||||
_utc = timezone(timedelta(), "UTC")
|
||||
|
||||
|
||||
def parse_rfc3339(string): # type: (str) -> Union[datetime, date, time]
|
||||
def parse_rfc3339(string: str) -> Union[datetime, date, time]:
|
||||
m = RFC_3339_DATETIME.match(string)
|
||||
if m:
|
||||
year = int(m.group(1))
|
||||
@@ -57,12 +52,12 @@ def parse_rfc3339(string): # type: (str) -> Union[datetime, date, time]
|
||||
microsecond = 0
|
||||
|
||||
if m.group(7):
|
||||
microsecond = int(("{:<06s}".format(m.group(8)))[:6])
|
||||
microsecond = int((f"{m.group(8):<06s}")[:6])
|
||||
|
||||
if m.group(9):
|
||||
# Timezone
|
||||
tz = m.group(9)
|
||||
if tz == "Z":
|
||||
if tz.upper() == "Z":
|
||||
tzinfo = _utc
|
||||
else:
|
||||
sign = m.group(11)[0]
|
||||
@@ -71,9 +66,7 @@ def parse_rfc3339(string): # type: (str) -> Union[datetime, date, time]
|
||||
if sign == "-":
|
||||
offset = -offset
|
||||
|
||||
tzinfo = timezone(
|
||||
offset, "{}{}:{}".format(sign, m.group(12), m.group(13))
|
||||
)
|
||||
tzinfo = timezone(offset, f"{sign}{m.group(12)}:{m.group(13)}")
|
||||
|
||||
return datetime(
|
||||
year, month, day, hour, minute, second, microsecond, tzinfo=tzinfo
|
||||
@@ -97,7 +90,7 @@ def parse_rfc3339(string): # type: (str) -> Union[datetime, date, time]
|
||||
microsecond = 0
|
||||
|
||||
if m.group(4):
|
||||
microsecond = int(("{:<06s}".format(m.group(5)))[:6])
|
||||
microsecond = int((f"{m.group(5):<06s}")[:6])
|
||||
|
||||
return time(hour, minute, second, microsecond)
|
||||
|
||||
@@ -108,7 +101,7 @@ _escaped = {"b": "\b", "t": "\t", "n": "\n", "f": "\f", "r": "\r", '"': '"', "\\
|
||||
_escapes = {v: k for k, v in _escaped.items()}
|
||||
|
||||
|
||||
def escape_string(s):
|
||||
def escape_string(s: str) -> str:
|
||||
s = decode(s)
|
||||
|
||||
res = []
|
||||
@@ -136,9 +129,9 @@ def escape_string(s):
|
||||
return "".join(res)
|
||||
|
||||
|
||||
def merge_dicts(d1, d2):
|
||||
def merge_dicts(d1: dict, d2: dict) -> dict:
|
||||
for k, v in d2.items():
|
||||
if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], Mapping):
|
||||
merge_dicts(d1[k], d2[k])
|
||||
if k in d1 and isinstance(d1[k], dict) and isinstance(v, Mapping):
|
||||
merge_dicts(d1[k], v)
|
||||
else:
|
||||
d1[k] = d2[k]
|
||||
|
||||
Vendored
+154
-30
@@ -1,20 +1,27 @@
|
||||
import datetime as _datetime
|
||||
|
||||
from collections.abc import Mapping
|
||||
from typing import IO
|
||||
from typing import Iterable
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
from ._utils import parse_rfc3339
|
||||
from .container import Container
|
||||
from .exceptions import UnexpectedCharError
|
||||
from .items import AoT
|
||||
from .items import Array
|
||||
from .items import Bool
|
||||
from .items import Comment
|
||||
from .items import Date
|
||||
from .items import DateTime
|
||||
from .items import DottedKey
|
||||
from .items import Float
|
||||
from .items import InlineTable
|
||||
from .items import Integer
|
||||
from .items import Item as _Item
|
||||
from .items import Key
|
||||
from .items import SingleKey
|
||||
from .items import String
|
||||
from .items import Table
|
||||
from .items import Time
|
||||
@@ -22,10 +29,10 @@ from .items import Trivia
|
||||
from .items import Whitespace
|
||||
from .items import item
|
||||
from .parser import Parser
|
||||
from .toml_document import TOMLDocument as _TOMLDocument
|
||||
from .toml_document import TOMLDocument
|
||||
|
||||
|
||||
def loads(string): # type: (str) -> _TOMLDocument
|
||||
def loads(string: Union[str, bytes]) -> TOMLDocument:
|
||||
"""
|
||||
Parses a string into a TOMLDocument.
|
||||
|
||||
@@ -34,48 +41,76 @@ def loads(string): # type: (str) -> _TOMLDocument
|
||||
return parse(string)
|
||||
|
||||
|
||||
def dumps(data, sort_keys=False): # type: (_TOMLDocument, bool) -> str
|
||||
def dumps(data: Mapping, sort_keys: bool = False) -> str:
|
||||
"""
|
||||
Dumps a TOMLDocument into a string.
|
||||
"""
|
||||
if not isinstance(data, _TOMLDocument) and isinstance(data, dict):
|
||||
data = item(data, _sort_keys=sort_keys)
|
||||
if not isinstance(data, Container) and isinstance(data, Mapping):
|
||||
data = item(dict(data), _sort_keys=sort_keys)
|
||||
|
||||
return data.as_string()
|
||||
try:
|
||||
# data should be a `Container` (and therefore implement `as_string`)
|
||||
# for all type safe invocations of this function
|
||||
return data.as_string() # type: ignore[attr-defined]
|
||||
except AttributeError as ex:
|
||||
msg = f"Expecting Mapping or TOML Container, {type(data)} given"
|
||||
raise TypeError(msg) from ex
|
||||
|
||||
|
||||
def parse(string): # type: (str) -> _TOMLDocument
|
||||
def load(fp: IO) -> TOMLDocument:
|
||||
"""
|
||||
Parses a string into a 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
|
||||
"""
|
||||
fp.write(dumps(data, sort_keys=sort_keys))
|
||||
|
||||
|
||||
def parse(string: Union[str, bytes]) -> TOMLDocument:
|
||||
"""
|
||||
Parses a string or bytes into a TOMLDocument.
|
||||
"""
|
||||
return Parser(string).parse()
|
||||
|
||||
|
||||
def document(): # type: () -> _TOMLDocument
|
||||
def document() -> TOMLDocument:
|
||||
"""
|
||||
Returns a new TOMLDocument instance.
|
||||
"""
|
||||
return _TOMLDocument()
|
||||
return TOMLDocument()
|
||||
|
||||
|
||||
# Items
|
||||
def integer(raw): # type: (str) -> Integer
|
||||
def integer(raw: Union[str, int]) -> Integer:
|
||||
"""Create an integer item from a number or string."""
|
||||
return item(int(raw))
|
||||
|
||||
|
||||
def float_(raw): # type: (str) -> Float
|
||||
def float_(raw: Union[str, float]) -> Float:
|
||||
"""Create an float item from a number or string."""
|
||||
return item(float(raw))
|
||||
|
||||
|
||||
def boolean(raw): # type: (str) -> Bool
|
||||
def boolean(raw: str) -> Bool:
|
||||
"""Turn `true` or `false` into a boolean item."""
|
||||
return item(raw == "true")
|
||||
|
||||
|
||||
def string(raw): # type: (str) -> String
|
||||
def string(raw: str) -> String:
|
||||
"""Create a string item."""
|
||||
return item(raw)
|
||||
|
||||
|
||||
def date(raw): # type: (str) -> Date
|
||||
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.")
|
||||
@@ -83,7 +118,8 @@ def date(raw): # type: (str) -> Date
|
||||
return item(value)
|
||||
|
||||
|
||||
def time(raw): # type: (str) -> Time
|
||||
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.")
|
||||
@@ -91,7 +127,8 @@ def time(raw): # type: (str) -> Time
|
||||
return item(value)
|
||||
|
||||
|
||||
def datetime(raw): # type: (str) -> DateTime
|
||||
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.")
|
||||
@@ -99,44 +136,131 @@ def datetime(raw): # type: (str) -> DateTime
|
||||
return item(value)
|
||||
|
||||
|
||||
def array(raw=None): # type: (str) -> Array
|
||||
def array(raw: str = None) -> 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]
|
||||
"""
|
||||
if raw is None:
|
||||
raw = "[]"
|
||||
|
||||
return value(raw)
|
||||
|
||||
|
||||
def table(): # type: () -> Table
|
||||
return Table(Container(), Trivia(), False)
|
||||
def table(is_super_table: bool = False) -> 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(): # type: () -> InlineTable
|
||||
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(): # type: () -> AoT
|
||||
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): # type: (str) -> Key
|
||||
return Key(k)
|
||||
def key(k: Union[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): # type: (str) -> _Item
|
||||
return Parser(raw)._parse_value()
|
||||
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): # type: (str) -> Tuple[Key, _Item]
|
||||
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): # type: (str) -> Whitespace
|
||||
def ws(src: str) -> Whitespace:
|
||||
"""Create a whitespace from a string."""
|
||||
return Whitespace(src, fixed=True)
|
||||
|
||||
|
||||
def nl(): # type: () -> Whitespace
|
||||
def nl() -> Whitespace:
|
||||
"""Create a newline item."""
|
||||
return ws("\n")
|
||||
|
||||
|
||||
def comment(string): # type: (str) -> Comment
|
||||
def comment(string: str) -> Comment:
|
||||
"""Create a comment item."""
|
||||
return Comment(Trivia(comment_ws=" ", comment="# " + string))
|
||||
|
||||
Vendored
+253
-191
@@ -1,52 +1,53 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import copy
|
||||
|
||||
from typing import Any
|
||||
from typing import Dict
|
||||
from typing import Generator
|
||||
from typing import Iterator
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
from ._compat import MutableMapping
|
||||
from ._compat import decode
|
||||
from ._utils import merge_dicts
|
||||
from .exceptions import KeyAlreadyPresent
|
||||
from .exceptions import NonExistentKey
|
||||
from .exceptions import ParseError
|
||||
from .exceptions import TOMLKitError
|
||||
from .items import AoT
|
||||
from .items import Comment
|
||||
from .items import Item
|
||||
from .items import Key
|
||||
from .items import Null
|
||||
from .items import SingleKey
|
||||
from .items import Table
|
||||
from .items import Trivia
|
||||
from .items import Whitespace
|
||||
from .items import _CustomDict
|
||||
from .items import item as _item
|
||||
|
||||
|
||||
_NOT_SET = object()
|
||||
|
||||
|
||||
class Container(MutableMapping, dict):
|
||||
class Container(_CustomDict):
|
||||
"""
|
||||
A container for items within a TOMLDocument.
|
||||
|
||||
This class implements the `dict` interface with copy/deepcopy protocol.
|
||||
"""
|
||||
|
||||
def __init__(self, parsed=False): # type: (bool) -> None
|
||||
self._map = {} # type: Dict[Key, int]
|
||||
self._body = [] # type: List[Tuple[Optional[Key], Item]]
|
||||
def __init__(self, parsed: bool = False) -> None:
|
||||
self._map: Dict[Key, int] = {}
|
||||
self._body: List[Tuple[Optional[Key], Item]] = []
|
||||
self._parsed = parsed
|
||||
self._table_keys = []
|
||||
|
||||
@property
|
||||
def body(self): # type: () -> List[Tuple[Optional[Key], Item]]
|
||||
def body(self) -> List[Tuple[Optional[Key], Item]]:
|
||||
return self._body
|
||||
|
||||
@property
|
||||
def value(self): # type: () -> Dict[Any, Any]
|
||||
def value(self) -> Dict[Any, Any]:
|
||||
d = {}
|
||||
for k, v in self._body:
|
||||
if k is None:
|
||||
@@ -65,10 +66,10 @@ class Container(MutableMapping, dict):
|
||||
|
||||
return d
|
||||
|
||||
def parsing(self, parsing): # type: (bool) -> None
|
||||
def parsing(self, parsing: bool) -> None:
|
||||
self._parsed = parsing
|
||||
|
||||
for k, v in self._body:
|
||||
for _, v in self._body:
|
||||
if isinstance(v, Table):
|
||||
v.value.parsing(parsing)
|
||||
elif isinstance(v, AoT):
|
||||
@@ -76,10 +77,17 @@ class Container(MutableMapping, dict):
|
||||
t.value.parsing(parsing)
|
||||
|
||||
def add(
|
||||
self, key, item=None
|
||||
): # type: (Union[Key, Item, str], Optional[Item]) -> Container
|
||||
self, key: Union[Key, Item, str], item: Optional[Item] = None
|
||||
) -> "Container":
|
||||
"""
|
||||
Adds an item to the current Container.
|
||||
|
||||
:Example:
|
||||
|
||||
>>> # add a key-value pair
|
||||
>>> doc.add('key', 'value')
|
||||
>>> # add a comment or whitespace or newline
|
||||
>>> doc.add(comment('# comment'))
|
||||
"""
|
||||
if item is None:
|
||||
if not isinstance(key, (Comment, Whitespace)):
|
||||
@@ -91,26 +99,90 @@ class Container(MutableMapping, dict):
|
||||
|
||||
return self.append(key, item)
|
||||
|
||||
def append(self, key, item): # type: (Union[Key, str, None], Item) -> Container
|
||||
def _handle_dotted_key(self, key: Key, value: Item) -> None:
|
||||
names = tuple(iter(key))
|
||||
name = names[0]
|
||||
name._dotted = True
|
||||
if name in self:
|
||||
if not isinstance(value, Table):
|
||||
table = Table(Container(True), Trivia(), False, is_super_table=True)
|
||||
_table = table
|
||||
for i, _name in enumerate(names[1:]):
|
||||
if i == len(names) - 2:
|
||||
_name.sep = key.sep
|
||||
|
||||
_table.append(_name, value)
|
||||
else:
|
||||
_name._dotted = True
|
||||
_table.append(
|
||||
_name,
|
||||
Table(
|
||||
Container(True),
|
||||
Trivia(),
|
||||
False,
|
||||
is_super_table=i < len(names) - 2,
|
||||
),
|
||||
)
|
||||
|
||||
_table = _table[_name]
|
||||
|
||||
value = table
|
||||
|
||||
self.append(name, value)
|
||||
|
||||
return
|
||||
else:
|
||||
table = Table(Container(True), Trivia(), False, is_super_table=True)
|
||||
self.append(name, table)
|
||||
|
||||
for i, _name in enumerate(names[1:]):
|
||||
if i == len(names) - 2:
|
||||
_name.sep = key.sep
|
||||
|
||||
table.append(_name, value)
|
||||
else:
|
||||
_name._dotted = True
|
||||
if _name in table.value:
|
||||
table = table.value[_name]
|
||||
else:
|
||||
table.append(
|
||||
_name,
|
||||
Table(
|
||||
Container(True),
|
||||
Trivia(),
|
||||
False,
|
||||
is_super_table=i < len(names) - 2,
|
||||
),
|
||||
)
|
||||
|
||||
table = table[_name]
|
||||
|
||||
def append(self, key: Union[Key, str, None], item: Item) -> "Container":
|
||||
"""Similar to :meth:`add` but both key and value must be given."""
|
||||
if not isinstance(key, Key) and key is not None:
|
||||
key = Key(key)
|
||||
key = SingleKey(key)
|
||||
|
||||
if not isinstance(item, Item):
|
||||
item = _item(item)
|
||||
|
||||
if key is not None and key.is_multi():
|
||||
self._handle_dotted_key(key, item)
|
||||
return self
|
||||
|
||||
if isinstance(item, (AoT, Table)) and item.name is None:
|
||||
item.name = key.key
|
||||
|
||||
if (
|
||||
isinstance(item, Table)
|
||||
and self._body
|
||||
and not self._parsed
|
||||
and not item.trivia.indent
|
||||
):
|
||||
item.trivia.indent = "\n"
|
||||
prev = self._previous_item()
|
||||
prev_ws = isinstance(prev, Whitespace) or ends_with_withespace(prev)
|
||||
if isinstance(item, Table):
|
||||
if item.name != key.key:
|
||||
item.invalidate_display_name()
|
||||
if self._body and not (self._parsed or item.trivia.indent or prev_ws):
|
||||
item.trivia.indent = "\n"
|
||||
|
||||
if isinstance(item, AoT) and self._body and not self._parsed:
|
||||
if item and "\n" not in item[0].trivia.indent:
|
||||
item.invalidate_display_name()
|
||||
if item and not ("\n" in item[0].trivia.indent or prev_ws):
|
||||
item[0].trivia.indent = "\n" + item[0].trivia.indent
|
||||
|
||||
if key is not None and key in self:
|
||||
@@ -165,8 +237,15 @@ class Container(MutableMapping, dict):
|
||||
|
||||
return self
|
||||
|
||||
# Create a new element to replace the old one
|
||||
current = copy.deepcopy(current)
|
||||
for k, v in item.value.body:
|
||||
current.append(k, v)
|
||||
self._body[
|
||||
current_idx[-1]
|
||||
if isinstance(current_idx, tuple)
|
||||
else current_idx
|
||||
] = (current_body_element[0], current)
|
||||
|
||||
return self
|
||||
elif current_body_element[0].is_dotted():
|
||||
@@ -192,11 +271,9 @@ class Container(MutableMapping, dict):
|
||||
# item that is not a table and insert after it
|
||||
# If no such item exists, insert at the top of the table
|
||||
key_after = None
|
||||
idx = 0
|
||||
for k, v in self._body:
|
||||
for i, (k, v) in enumerate(self._body):
|
||||
if isinstance(v, Null):
|
||||
# This happens only after deletion
|
||||
continue
|
||||
continue # Null elements are inserted after deletion
|
||||
|
||||
if isinstance(v, Whitespace) and not v.is_fixed():
|
||||
continue
|
||||
@@ -204,8 +281,7 @@ class Container(MutableMapping, dict):
|
||||
if not is_table and isinstance(v, (Table, AoT)):
|
||||
break
|
||||
|
||||
key_after = k or idx
|
||||
idx += 1
|
||||
key_after = k or i # last scalar, Array or InlineTable value
|
||||
|
||||
if key_after is not None:
|
||||
if isinstance(key_after, int):
|
||||
@@ -213,10 +289,11 @@ class Container(MutableMapping, dict):
|
||||
return self._insert_at(key_after + 1, key, item)
|
||||
else:
|
||||
previous_item = self._body[-1][1]
|
||||
if (
|
||||
not isinstance(previous_item, Whitespace)
|
||||
and not is_table
|
||||
and "\n" not in previous_item.trivia.trail
|
||||
if not (
|
||||
isinstance(previous_item, Whitespace)
|
||||
or ends_with_withespace(previous_item)
|
||||
or is_table
|
||||
or "\n" in previous_item.trivia.trail
|
||||
):
|
||||
previous_item.trivia.trail += "\n"
|
||||
else:
|
||||
@@ -250,9 +327,10 @@ class Container(MutableMapping, dict):
|
||||
|
||||
return self
|
||||
|
||||
def remove(self, key): # type: (Union[Key, str]) -> Container
|
||||
def remove(self, key: Union[Key, str]) -> "Container":
|
||||
"""Remove a key from the container."""
|
||||
if not isinstance(key, Key):
|
||||
key = Key(key)
|
||||
key = SingleKey(key)
|
||||
|
||||
idx = self._map.pop(key, None)
|
||||
if idx is None:
|
||||
@@ -269,8 +347,8 @@ class Container(MutableMapping, dict):
|
||||
return self
|
||||
|
||||
def _insert_after(
|
||||
self, key, other_key, item
|
||||
): # type: (Union[str, Key], Union[str, Key], Union[Item, Any]) -> Container
|
||||
self, key: Union[Key, str], other_key: Union[Key, str], item: Any
|
||||
) -> "Container":
|
||||
if key is None:
|
||||
raise ValueError("Key cannot be null in insert_after()")
|
||||
|
||||
@@ -278,10 +356,10 @@ class Container(MutableMapping, dict):
|
||||
raise NonExistentKey(key)
|
||||
|
||||
if not isinstance(key, Key):
|
||||
key = Key(key)
|
||||
key = SingleKey(key)
|
||||
|
||||
if not isinstance(other_key, Key):
|
||||
other_key = Key(other_key)
|
||||
other_key = SingleKey(other_key)
|
||||
|
||||
item = _item(item)
|
||||
|
||||
@@ -315,23 +393,22 @@ class Container(MutableMapping, dict):
|
||||
|
||||
return self
|
||||
|
||||
def _insert_at(
|
||||
self, idx, key, item
|
||||
): # type: (int, Union[str, Key], Union[Item, Any]) -> Container
|
||||
def _insert_at(self, idx: int, key: Union[Key, str], item: Any) -> "Container":
|
||||
if idx > len(self._body) - 1:
|
||||
raise ValueError("Unable to insert at position {}".format(idx))
|
||||
raise ValueError(f"Unable to insert at position {idx}")
|
||||
|
||||
if not isinstance(key, Key):
|
||||
key = Key(key)
|
||||
key = SingleKey(key)
|
||||
|
||||
item = _item(item)
|
||||
|
||||
if idx > 0:
|
||||
previous_item = self._body[idx - 1][1]
|
||||
if (
|
||||
not isinstance(previous_item, Whitespace)
|
||||
and not isinstance(item, (AoT, Table))
|
||||
and "\n" not in previous_item.trivia.trail
|
||||
if not (
|
||||
isinstance(previous_item, Whitespace)
|
||||
or ends_with_withespace(previous_item)
|
||||
or isinstance(item, (AoT, Table))
|
||||
or "\n" in previous_item.trivia.trail
|
||||
):
|
||||
previous_item.trivia.trail += "\n"
|
||||
|
||||
@@ -357,9 +434,10 @@ class Container(MutableMapping, dict):
|
||||
|
||||
return self
|
||||
|
||||
def item(self, key): # type: (Union[Key, str]) -> Item
|
||||
def item(self, key: Union[Key, str]) -> Item:
|
||||
"""Get an item for the given key."""
|
||||
if not isinstance(key, Key):
|
||||
key = Key(key)
|
||||
key = SingleKey(key)
|
||||
|
||||
idx = self._map.get(key, None)
|
||||
if idx is None:
|
||||
@@ -373,11 +451,13 @@ class Container(MutableMapping, dict):
|
||||
|
||||
return self._body[idx][1]
|
||||
|
||||
def last_item(self): # type: () -> Optional[Item]
|
||||
def last_item(self) -> Optional[Item]:
|
||||
"""Get the last item."""
|
||||
if self._body:
|
||||
return self._body[-1][1]
|
||||
|
||||
def as_string(self): # type: () -> str
|
||||
def as_string(self) -> str:
|
||||
"""Render as TOML string."""
|
||||
s = ""
|
||||
for k, v in self._body:
|
||||
if k is not None:
|
||||
@@ -393,8 +473,8 @@ class Container(MutableMapping, dict):
|
||||
return s
|
||||
|
||||
def _render_table(
|
||||
self, key, table, prefix=None
|
||||
): # (Key, Table, Optional[str]) -> str
|
||||
self, key: Key, table: Table, prefix: Optional[str] = None
|
||||
) -> str:
|
||||
cur = ""
|
||||
|
||||
if table.display_name is not None:
|
||||
@@ -457,7 +537,7 @@ class Container(MutableMapping, dict):
|
||||
|
||||
return cur
|
||||
|
||||
def _render_aot_table(self, table, prefix=None): # (Table, Optional[str]) -> str
|
||||
def _render_aot_table(self, table: Table, prefix: Optional[str] = None) -> str:
|
||||
cur = ""
|
||||
|
||||
_key = prefix or ""
|
||||
@@ -510,43 +590,16 @@ class Container(MutableMapping, dict):
|
||||
item.trivia.trail,
|
||||
)
|
||||
|
||||
def __len__(self) -> int:
|
||||
return dict.__len__(self)
|
||||
|
||||
def __iter__(self) -> Iterator[str]:
|
||||
return iter(dict.keys(self))
|
||||
|
||||
# Dictionary methods
|
||||
|
||||
def pop(self, key, default=_NOT_SET):
|
||||
try:
|
||||
value = self[key]
|
||||
except KeyError:
|
||||
if default is _NOT_SET:
|
||||
raise
|
||||
|
||||
return default
|
||||
|
||||
del self[key]
|
||||
|
||||
return value
|
||||
|
||||
def setdefault(
|
||||
self, key, default=None
|
||||
): # type: (Union[Key, str], Any) -> Union[Item, Container]
|
||||
super(Container, self).setdefault(key, default=default)
|
||||
|
||||
return self[key]
|
||||
|
||||
def __contains__(self, key): # type: (Union[Key, str]) -> bool
|
||||
def __getitem__(self, key: Union[Key, str]) -> Union[Item, "Container"]:
|
||||
if not isinstance(key, Key):
|
||||
key = Key(key)
|
||||
|
||||
return key in self._map
|
||||
|
||||
def __setitem__(self, key, value): # type: (Union[Key, str], Any) -> None
|
||||
if key is not None and key in self:
|
||||
self._replace(key, key, value)
|
||||
else:
|
||||
self.append(key, value)
|
||||
|
||||
def __getitem__(self, key): # type: (Union[Key, str]) -> Union[Item, Container]
|
||||
if not isinstance(key, Key):
|
||||
key = Key(key)
|
||||
key = SingleKey(key)
|
||||
|
||||
idx = self._map.get(key, None)
|
||||
if idx is None:
|
||||
@@ -564,29 +617,25 @@ class Container(MutableMapping, dict):
|
||||
|
||||
return item
|
||||
|
||||
def __setitem__(self, key, value): # type: (Union[Key, str], Any) -> None
|
||||
def __setitem__(self, key: Union[Key, str], value: Any) -> None:
|
||||
if key is not None and key in self:
|
||||
self._replace(key, key, value)
|
||||
old_key = next(filter(lambda k: k == key, self._map))
|
||||
self._replace(old_key, key, value)
|
||||
else:
|
||||
self.append(key, value)
|
||||
|
||||
def __delitem__(self, key): # type: (Union[Key, str]) -> None
|
||||
def __delitem__(self, key: Union[Key, str]) -> None:
|
||||
self.remove(key)
|
||||
|
||||
def __len__(self): # type: () -> int
|
||||
return dict.__len__(self)
|
||||
|
||||
def __iter__(self): # type: () -> Iterator[str]
|
||||
return iter(dict.keys(self))
|
||||
def setdefault(self, key: Union[Key, str], default: Any) -> Any:
|
||||
super().setdefault(key, default=default)
|
||||
return self[key]
|
||||
|
||||
def _replace(
|
||||
self, key, new_key, value
|
||||
): # type: (Union[Key, str], Union[Key, str], Item) -> None
|
||||
self, key: Union[Key, str], new_key: Union[Key, str], value: Item
|
||||
) -> None:
|
||||
if not isinstance(key, Key):
|
||||
key = Key(key)
|
||||
|
||||
if not isinstance(new_key, Key):
|
||||
new_key = Key(new_key)
|
||||
key = SingleKey(key)
|
||||
|
||||
idx = self._map.get(key, None)
|
||||
if idx is None:
|
||||
@@ -595,10 +644,9 @@ class Container(MutableMapping, dict):
|
||||
self._replace_at(idx, new_key, value)
|
||||
|
||||
def _replace_at(
|
||||
self, idx, new_key, value
|
||||
): # type: (Union[int, Tuple[int]], Union[Key, str], Item) -> None
|
||||
if not isinstance(new_key, Key):
|
||||
new_key = Key(new_key)
|
||||
self, idx: Union[int, Tuple[int]], new_key: Union[Key, str], value: Item
|
||||
) -> None:
|
||||
value = _item(value)
|
||||
|
||||
if isinstance(idx, tuple):
|
||||
for i in idx[1:]:
|
||||
@@ -607,38 +655,63 @@ class Container(MutableMapping, dict):
|
||||
idx = idx[0]
|
||||
|
||||
k, v = self._body[idx]
|
||||
if not isinstance(new_key, Key):
|
||||
if (
|
||||
isinstance(value, (AoT, Table)) != isinstance(v, (AoT, Table))
|
||||
or new_key != k.key
|
||||
):
|
||||
new_key = SingleKey(new_key)
|
||||
else: # Inherit the sep of the old key
|
||||
new_key = k
|
||||
|
||||
self._map[new_key] = self._map.pop(k)
|
||||
del self._map[k]
|
||||
self._map[new_key] = idx
|
||||
if new_key != k:
|
||||
dict.__delitem__(self, k)
|
||||
|
||||
if isinstance(self._map[new_key], tuple):
|
||||
self._map[new_key] = self._map[new_key][0]
|
||||
if isinstance(value, (AoT, Table)) != isinstance(v, (AoT, Table)):
|
||||
# new tables should appear after all non-table values
|
||||
self.remove(k)
|
||||
for i in range(idx, len(self._body)):
|
||||
if isinstance(self._body[i][1], (AoT, Table)):
|
||||
self._insert_at(i, new_key, value)
|
||||
idx = i
|
||||
break
|
||||
else:
|
||||
idx = -1
|
||||
self.append(new_key, value)
|
||||
else:
|
||||
# Copying trivia
|
||||
if not isinstance(value, (Whitespace, AoT)):
|
||||
value.trivia.indent = v.trivia.indent
|
||||
value.trivia.comment_ws = value.trivia.comment_ws or v.trivia.comment_ws
|
||||
value.trivia.comment = value.trivia.comment or v.trivia.comment
|
||||
value.trivia.trail = v.trivia.trail
|
||||
self._body[idx] = (new_key, value)
|
||||
|
||||
value = _item(value)
|
||||
|
||||
# Copying trivia
|
||||
if not isinstance(value, (Whitespace, AoT)):
|
||||
value.trivia.indent = v.trivia.indent
|
||||
value.trivia.comment_ws = v.trivia.comment_ws
|
||||
value.trivia.comment = v.trivia.comment
|
||||
value.trivia.trail = v.trivia.trail
|
||||
if hasattr(value, "invalidate_display_name"):
|
||||
value.invalidate_display_name() # type: ignore[attr-defined]
|
||||
|
||||
if isinstance(value, Table):
|
||||
# Insert a cosmetic new line for tables
|
||||
value.append(None, Whitespace("\n"))
|
||||
# Insert a cosmetic new line for tables if:
|
||||
# - it does not have it yet OR is not followed by one
|
||||
# - it is not the last item
|
||||
last, _ = self._previous_item_with_index()
|
||||
idx = last if idx < 0 else idx
|
||||
has_ws = ends_with_withespace(value)
|
||||
next_ws = idx < last and isinstance(self._body[idx + 1][1], Whitespace)
|
||||
if idx < last and not (next_ws or has_ws):
|
||||
value.append(None, Whitespace("\n"))
|
||||
|
||||
self._body[idx] = (new_key, value)
|
||||
dict.__setitem__(self, new_key.key, value.value)
|
||||
|
||||
dict.__setitem__(self, new_key.key, value.value)
|
||||
|
||||
def __str__(self): # type: () -> str
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
||||
|
||||
def __repr__(self): # type: () -> str
|
||||
def __repr__(self) -> str:
|
||||
return repr(self.value)
|
||||
|
||||
def __eq__(self, other): # type: (Dict) -> bool
|
||||
def __eq__(self, other: dict) -> bool:
|
||||
if not isinstance(other, dict):
|
||||
return NotImplemented
|
||||
|
||||
@@ -667,10 +740,10 @@ class Container(MutableMapping, dict):
|
||||
if key is not None:
|
||||
dict.__setitem__(self, key.key, item.value)
|
||||
|
||||
def copy(self): # type: () -> Container
|
||||
def copy(self) -> "Container":
|
||||
return copy.copy(self)
|
||||
|
||||
def __copy__(self): # type: () -> Container
|
||||
def __copy__(self) -> "Container":
|
||||
c = self.__class__(self._parsed)
|
||||
for k, v in dict.items(self):
|
||||
dict.__setitem__(c, k, v)
|
||||
@@ -680,14 +753,34 @@ class Container(MutableMapping, dict):
|
||||
|
||||
return c
|
||||
|
||||
def _previous_item_with_index(
|
||||
self, idx: Optional[int] = None, ignore=(Null,)
|
||||
) -> Optional[Tuple[int, Item]]:
|
||||
"""Find the immediate previous item before index ``idx``"""
|
||||
if idx is None or idx > len(self._body):
|
||||
idx = len(self._body)
|
||||
for i in range(idx - 1, -1, -1):
|
||||
v = self._body[i][-1]
|
||||
if not isinstance(v, ignore):
|
||||
return i, v
|
||||
return None
|
||||
|
||||
class OutOfOrderTableProxy(MutableMapping, dict):
|
||||
def __init__(self, container, indices): # type: (Container, Tuple) -> None
|
||||
def _previous_item(
|
||||
self, idx: Optional[int] = None, ignore=(Null,)
|
||||
) -> Optional[Item]:
|
||||
"""Find the immediate previous item before index ``idx``.
|
||||
If ``idx`` is not given, the last item is returned.
|
||||
"""
|
||||
prev = self._previous_item_with_index(idx, ignore)
|
||||
return prev[-1] if prev else None
|
||||
|
||||
|
||||
class OutOfOrderTableProxy(_CustomDict):
|
||||
def __init__(self, container: Container, indices: Tuple[int]) -> None:
|
||||
self._container = container
|
||||
self._internal_container = Container(self._container.parsing)
|
||||
self._internal_container = Container(True)
|
||||
self._tables = []
|
||||
self._tables_map = {}
|
||||
self._map = {}
|
||||
|
||||
for i in indices:
|
||||
key, item = self._container._body[i]
|
||||
@@ -700,27 +793,19 @@ class OutOfOrderTableProxy(MutableMapping, dict):
|
||||
self._tables_map[k] = table_idx
|
||||
if k is not None:
|
||||
dict.__setitem__(self, k.key, v)
|
||||
else:
|
||||
self._internal_container.append(key, item)
|
||||
self._map[key] = i
|
||||
if key is not None:
|
||||
dict.__setitem__(self, key.key, item)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self._internal_container.value
|
||||
|
||||
def __getitem__(self, key): # type: (Union[Key, str]) -> Any
|
||||
def __getitem__(self, key: Union[Key, str]) -> Any:
|
||||
if key not in self._internal_container:
|
||||
raise NonExistentKey(key)
|
||||
|
||||
return self._internal_container[key]
|
||||
|
||||
def __setitem__(self, key, item): # type: (Union[Key, str], Any) -> None
|
||||
if key in self._map:
|
||||
idx = self._map[key]
|
||||
self._container._replace_at(idx, key, item)
|
||||
elif key in self._tables_map:
|
||||
def __setitem__(self, key: Union[Key, str], item: Any) -> None:
|
||||
if key in self._tables_map:
|
||||
table = self._tables[self._tables_map[key]]
|
||||
table[key] = item
|
||||
elif self._tables:
|
||||
@@ -729,15 +814,12 @@ class OutOfOrderTableProxy(MutableMapping, dict):
|
||||
else:
|
||||
self._container[key] = item
|
||||
|
||||
self._internal_container[key] = item
|
||||
if key is not None:
|
||||
dict.__setitem__(self, key, item)
|
||||
|
||||
def __delitem__(self, key): # type: (Union[Key, str]) -> None
|
||||
if key in self._map:
|
||||
idx = self._map[key]
|
||||
del self._container[key]
|
||||
del self._map[key]
|
||||
elif key in self._tables_map:
|
||||
def __delitem__(self, key: Union[Key, str]) -> None:
|
||||
if key in self._tables_map:
|
||||
table = self._tables[self._tables_map[key]]
|
||||
del table[key]
|
||||
del self._tables_map[key]
|
||||
@@ -745,47 +827,27 @@ class OutOfOrderTableProxy(MutableMapping, dict):
|
||||
raise NonExistentKey(key)
|
||||
|
||||
del self._internal_container[key]
|
||||
if key is not None:
|
||||
dict.__delitem__(self, key)
|
||||
|
||||
def keys(self):
|
||||
return self._internal_container.keys()
|
||||
def __iter__(self) -> Iterator[str]:
|
||||
return iter(dict.keys(self))
|
||||
|
||||
def values(self):
|
||||
return self._internal_container.values()
|
||||
|
||||
def items(self): # type: () -> Generator[Item]
|
||||
return self._internal_container.items()
|
||||
|
||||
def update(self, other): # type: (Dict) -> None
|
||||
self._internal_container.update(other)
|
||||
|
||||
def get(self, key, default=None): # type: (Any, Optional[Any]) -> Any
|
||||
return self._internal_container.get(key, default=default)
|
||||
|
||||
def pop(self, key, default=_NOT_SET):
|
||||
return self._internal_container.pop(key, default=default)
|
||||
|
||||
def setdefault(
|
||||
self, key, default=None
|
||||
): # type: (Union[Key, str], Any) -> Union[Item, Container]
|
||||
return self._internal_container.setdefault(key, default=default)
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self._internal_container
|
||||
|
||||
def __iter__(self): # type: () -> Iterator[str]
|
||||
return iter(self._internal_container)
|
||||
|
||||
def __str__(self):
|
||||
return str(self._internal_container)
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self._internal_container)
|
||||
|
||||
def __eq__(self, other): # type: (Dict) -> bool
|
||||
if not isinstance(other, dict):
|
||||
return NotImplemented
|
||||
|
||||
return self._internal_container == other
|
||||
def __len__(self) -> int:
|
||||
return dict.__len__(self)
|
||||
|
||||
def __getattr__(self, attribute):
|
||||
return getattr(self._internal_container, attribute)
|
||||
|
||||
def setdefault(self, key: Union[Key, str], default: Any) -> Any:
|
||||
super().setdefault(key, default=default)
|
||||
return self[key]
|
||||
|
||||
|
||||
def ends_with_withespace(it: Any) -> bool:
|
||||
"""Returns ``True`` if the given item ``it`` is a ``Table`` or ``AoT`` object
|
||||
ending with a ``Whitespace``.
|
||||
"""
|
||||
return (
|
||||
isinstance(it, Table) and isinstance(it.value._previous_item(), Whitespace)
|
||||
) or (isinstance(it, AoT) and len(it) > 0 and isinstance(it[-1], Whitespace))
|
||||
|
||||
Vendored
+38
-46
@@ -1,5 +1,3 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@@ -15,18 +13,14 @@ class ParseError(ValueError, TOMLKitError):
|
||||
location within the line where the error was encountered.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, line, col, message=None
|
||||
): # type: (int, int, Optional[str]) -> None
|
||||
def __init__(self, line: int, col: int, message: Optional[str] = None) -> None:
|
||||
self._line = line
|
||||
self._col = col
|
||||
|
||||
if message is None:
|
||||
message = "TOML parse error"
|
||||
|
||||
super(ParseError, self).__init__(
|
||||
"{} at line {} col {}".format(message, self._line, self._col)
|
||||
)
|
||||
super().__init__(f"{message} at line {self._line} col {self._col}")
|
||||
|
||||
@property
|
||||
def line(self):
|
||||
@@ -42,10 +36,10 @@ class MixedArrayTypesError(ParseError):
|
||||
An array was found that had two or more element types.
|
||||
"""
|
||||
|
||||
def __init__(self, line, col): # type: (int, int) -> None
|
||||
def __init__(self, line: int, col: int) -> None:
|
||||
message = "Mixed types found in array"
|
||||
|
||||
super(MixedArrayTypesError, self).__init__(line, col, message=message)
|
||||
super().__init__(line, col, message=message)
|
||||
|
||||
|
||||
class InvalidNumberError(ParseError):
|
||||
@@ -53,10 +47,10 @@ class InvalidNumberError(ParseError):
|
||||
A numeric field was improperly specified.
|
||||
"""
|
||||
|
||||
def __init__(self, line, col): # type: (int, int) -> None
|
||||
def __init__(self, line: int, col: int) -> None:
|
||||
message = "Invalid number"
|
||||
|
||||
super(InvalidNumberError, self).__init__(line, col, message=message)
|
||||
super().__init__(line, col, message=message)
|
||||
|
||||
|
||||
class InvalidDateTimeError(ParseError):
|
||||
@@ -64,10 +58,10 @@ class InvalidDateTimeError(ParseError):
|
||||
A datetime field was improperly specified.
|
||||
"""
|
||||
|
||||
def __init__(self, line, col): # type: (int, int) -> None
|
||||
def __init__(self, line: int, col: int) -> None:
|
||||
message = "Invalid datetime"
|
||||
|
||||
super(InvalidDateTimeError, self).__init__(line, col, message=message)
|
||||
super().__init__(line, col, message=message)
|
||||
|
||||
|
||||
class InvalidDateError(ParseError):
|
||||
@@ -75,10 +69,10 @@ class InvalidDateError(ParseError):
|
||||
A date field was improperly specified.
|
||||
"""
|
||||
|
||||
def __init__(self, line, col): # type: (int, int) -> None
|
||||
def __init__(self, line: int, col: int) -> None:
|
||||
message = "Invalid date"
|
||||
|
||||
super(InvalidDateError, self).__init__(line, col, message=message)
|
||||
super().__init__(line, col, message=message)
|
||||
|
||||
|
||||
class InvalidTimeError(ParseError):
|
||||
@@ -86,10 +80,10 @@ class InvalidTimeError(ParseError):
|
||||
A date field was improperly specified.
|
||||
"""
|
||||
|
||||
def __init__(self, line, col): # type: (int, int) -> None
|
||||
def __init__(self, line: int, col: int) -> None:
|
||||
message = "Invalid time"
|
||||
|
||||
super(InvalidTimeError, self).__init__(line, col, message=message)
|
||||
super().__init__(line, col, message=message)
|
||||
|
||||
|
||||
class InvalidNumberOrDateError(ParseError):
|
||||
@@ -97,10 +91,10 @@ class InvalidNumberOrDateError(ParseError):
|
||||
A numeric or date field was improperly specified.
|
||||
"""
|
||||
|
||||
def __init__(self, line, col): # type: (int, int) -> None
|
||||
def __init__(self, line: int, col: int) -> None:
|
||||
message = "Invalid number or date format"
|
||||
|
||||
super(InvalidNumberOrDateError, self).__init__(line, col, message=message)
|
||||
super().__init__(line, col, message=message)
|
||||
|
||||
|
||||
class InvalidUnicodeValueError(ParseError):
|
||||
@@ -108,10 +102,10 @@ class InvalidUnicodeValueError(ParseError):
|
||||
A unicode code was improperly specified.
|
||||
"""
|
||||
|
||||
def __init__(self, line, col): # type: (int, int) -> None
|
||||
def __init__(self, line: int, col: int) -> None:
|
||||
message = "Invalid unicode value"
|
||||
|
||||
super(InvalidUnicodeValueError, self).__init__(line, col, message=message)
|
||||
super().__init__(line, col, message=message)
|
||||
|
||||
|
||||
class UnexpectedCharError(ParseError):
|
||||
@@ -119,10 +113,10 @@ class UnexpectedCharError(ParseError):
|
||||
An unexpected character was found during parsing.
|
||||
"""
|
||||
|
||||
def __init__(self, line, col, char): # type: (int, int, str) -> None
|
||||
message = "Unexpected character: {}".format(repr(char))
|
||||
def __init__(self, line: int, col: int, char: str) -> None:
|
||||
message = f"Unexpected character: {repr(char)}"
|
||||
|
||||
super(UnexpectedCharError, self).__init__(line, col, message=message)
|
||||
super().__init__(line, col, message=message)
|
||||
|
||||
|
||||
class EmptyKeyError(ParseError):
|
||||
@@ -130,10 +124,10 @@ class EmptyKeyError(ParseError):
|
||||
An empty key was found during parsing.
|
||||
"""
|
||||
|
||||
def __init__(self, line, col): # type: (int, int) -> None
|
||||
def __init__(self, line: int, col: int) -> None:
|
||||
message = "Empty key"
|
||||
|
||||
super(EmptyKeyError, self).__init__(line, col, message=message)
|
||||
super().__init__(line, col, message=message)
|
||||
|
||||
|
||||
class EmptyTableNameError(ParseError):
|
||||
@@ -141,10 +135,10 @@ class EmptyTableNameError(ParseError):
|
||||
An empty table name was found during parsing.
|
||||
"""
|
||||
|
||||
def __init__(self, line, col): # type: (int, int) -> None
|
||||
def __init__(self, line: int, col: int) -> None:
|
||||
message = "Empty table name"
|
||||
|
||||
super(EmptyTableNameError, self).__init__(line, col, message=message)
|
||||
super().__init__(line, col, message=message)
|
||||
|
||||
|
||||
class InvalidCharInStringError(ParseError):
|
||||
@@ -152,10 +146,10 @@ class InvalidCharInStringError(ParseError):
|
||||
The string being parsed contains an invalid character.
|
||||
"""
|
||||
|
||||
def __init__(self, line, col, char): # type: (int, int, str) -> None
|
||||
message = "Invalid character {} in string".format(repr(char))
|
||||
def __init__(self, line: int, col: int, char: str) -> None:
|
||||
message = f"Invalid character {repr(char)} in string"
|
||||
|
||||
super(InvalidCharInStringError, self).__init__(line, col, message=message)
|
||||
super().__init__(line, col, message=message)
|
||||
|
||||
|
||||
class UnexpectedEofError(ParseError):
|
||||
@@ -163,10 +157,10 @@ class UnexpectedEofError(ParseError):
|
||||
The TOML being parsed ended before the end of a statement.
|
||||
"""
|
||||
|
||||
def __init__(self, line, col): # type: (int, int) -> None
|
||||
def __init__(self, line: int, col: int) -> None:
|
||||
message = "Unexpected end of file"
|
||||
|
||||
super(UnexpectedEofError, self).__init__(line, col, message=message)
|
||||
super().__init__(line, col, message=message)
|
||||
|
||||
|
||||
class InternalParserError(ParseError):
|
||||
@@ -174,14 +168,12 @@ class InternalParserError(ParseError):
|
||||
An error that indicates a bug in the parser.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, line, col, message=None
|
||||
): # type: (int, int, Optional[str]) -> None
|
||||
def __init__(self, line: int, col: int, message: Optional[str] = None) -> None:
|
||||
msg = "Internal parser error"
|
||||
if message:
|
||||
msg += " ({})".format(message)
|
||||
msg += f" ({message})"
|
||||
|
||||
super(InternalParserError, self).__init__(line, col, message=msg)
|
||||
super().__init__(line, col, message=msg)
|
||||
|
||||
|
||||
class NonExistentKey(KeyError, TOMLKitError):
|
||||
@@ -190,9 +182,9 @@ class NonExistentKey(KeyError, TOMLKitError):
|
||||
"""
|
||||
|
||||
def __init__(self, key):
|
||||
message = 'Key "{}" does not exist.'.format(key)
|
||||
message = f'Key "{key}" does not exist.'
|
||||
|
||||
super(NonExistentKey, self).__init__(message)
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class KeyAlreadyPresent(TOMLKitError):
|
||||
@@ -201,23 +193,23 @@ class KeyAlreadyPresent(TOMLKitError):
|
||||
"""
|
||||
|
||||
def __init__(self, key):
|
||||
message = 'Key "{}" already exists.'.format(key)
|
||||
message = f'Key "{key}" already exists.'
|
||||
|
||||
super(KeyAlreadyPresent, self).__init__(message)
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class InvalidControlChar(ParseError):
|
||||
def __init__(self, line, col, char, type): # type: (int, int, int, str) -> None
|
||||
def __init__(self, line: int, col: int, char: int, type: str) -> None:
|
||||
display_code = "\\u00"
|
||||
|
||||
if char < 16:
|
||||
display_code += "0"
|
||||
|
||||
display_code += str(char)
|
||||
display_code += hex(char)[2:]
|
||||
|
||||
message = (
|
||||
"Control characters (codes less than 0x1f and 0x7f) are not allowed in {}, "
|
||||
"use {} instead".format(type, display_code)
|
||||
)
|
||||
|
||||
super(InvalidControlChar, self).__init__(line, col, message=message)
|
||||
super().__init__(line, col, message=message)
|
||||
|
||||
Vendored
+733
-424
File diff suppressed because it is too large
Load Diff
Vendored
+147
-321
@@ -1,17 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
import string
|
||||
|
||||
from typing import Any
|
||||
from typing import Generator
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
from typing import Type
|
||||
from typing import Union
|
||||
|
||||
from ._compat import chr
|
||||
from ._compat import decode
|
||||
from ._utils import RFC_3339_LOOSE
|
||||
from ._utils import _escaped
|
||||
@@ -44,6 +39,7 @@ from .items import Item
|
||||
from .items import Key
|
||||
from .items import KeyType
|
||||
from .items import Null
|
||||
from .items import SingleKey
|
||||
from .items import String
|
||||
from .items import StringType
|
||||
from .items import Table
|
||||
@@ -67,11 +63,11 @@ class Parser:
|
||||
Parser for TOML documents.
|
||||
"""
|
||||
|
||||
def __init__(self, string): # type: (str) -> None
|
||||
def __init__(self, string: str) -> None:
|
||||
# Input to parse
|
||||
self._src = Source(decode(string))
|
||||
|
||||
self._aot_stack = []
|
||||
self._aot_stack: List[Key] = []
|
||||
|
||||
@property
|
||||
def _state(self):
|
||||
@@ -89,20 +85,20 @@ class Parser:
|
||||
def _marker(self):
|
||||
return self._src.marker
|
||||
|
||||
def extract(self): # type: () -> str
|
||||
def extract(self) -> str:
|
||||
"""
|
||||
Extracts the value between marker and index
|
||||
"""
|
||||
return self._src.extract()
|
||||
|
||||
def inc(self, exception=None): # type: (Optional[ParseError.__class__]) -> bool
|
||||
def inc(self, exception: Optional[Type[ParseError]] = None) -> bool:
|
||||
"""
|
||||
Increments the parser if the end of the input has not been reached.
|
||||
Returns whether or not it was able to advance.
|
||||
"""
|
||||
return self._src.inc(exception=exception)
|
||||
|
||||
def inc_n(self, n, exception=None): # type: (int, Optional[ParseError]) -> bool
|
||||
def inc_n(self, n: int, exception: Optional[Type[ParseError]] = None) -> bool:
|
||||
"""
|
||||
Increments the parser by n characters
|
||||
if the end of the input has not been reached.
|
||||
@@ -115,25 +111,25 @@ class Parser:
|
||||
"""
|
||||
return self._src.consume(chars=chars, min=min, max=max)
|
||||
|
||||
def end(self): # type: () -> bool
|
||||
def end(self) -> bool:
|
||||
"""
|
||||
Returns True if the parser has reached the end of the input.
|
||||
"""
|
||||
return self._src.end()
|
||||
|
||||
def mark(self): # type: () -> None
|
||||
def mark(self) -> None:
|
||||
"""
|
||||
Sets the marker to the index's current position
|
||||
"""
|
||||
self._src.mark()
|
||||
|
||||
def parse_error(self, exception=ParseError, *args):
|
||||
def parse_error(self, exception=ParseError, *args, **kwargs):
|
||||
"""
|
||||
Creates a generic "parse error" at the current position.
|
||||
"""
|
||||
return self._src.parse_error(exception, *args)
|
||||
return self._src.parse_error(exception, *args, **kwargs)
|
||||
|
||||
def parse(self): # type: () -> TOMLDocument
|
||||
def parse(self) -> TOMLDocument:
|
||||
body = TOMLDocument(True)
|
||||
|
||||
# Take all keyvals outside of tables/AoT's.
|
||||
@@ -148,10 +144,8 @@ class Parser:
|
||||
break
|
||||
|
||||
key, value = item
|
||||
if key is not None and key.is_dotted():
|
||||
if (key is not None and key.is_multi()) or not self._merge_ws(value, body):
|
||||
# We actually have a table
|
||||
self._handle_dotted_key(body, key, value)
|
||||
elif not self._merge_ws(value, body):
|
||||
body.append(key, value)
|
||||
|
||||
self.mark()
|
||||
@@ -161,7 +155,7 @@ class Parser:
|
||||
if isinstance(value, Table) and value.is_aot_element():
|
||||
# This is just the first table in an AoT. Parse the rest of the array
|
||||
# along with it.
|
||||
value = self._parse_aot(value, key.key)
|
||||
value = self._parse_aot(value, key)
|
||||
|
||||
body.append(key, value)
|
||||
|
||||
@@ -169,7 +163,7 @@ class Parser:
|
||||
|
||||
return body
|
||||
|
||||
def _merge_ws(self, item, container): # type: (Item, Container) -> bool
|
||||
def _merge_ws(self, item: Item, container: Container) -> bool:
|
||||
"""
|
||||
Merges the given Item with the last one currently in the given Container if
|
||||
both are whitespace items.
|
||||
@@ -191,85 +185,20 @@ class Parser:
|
||||
|
||||
return True
|
||||
|
||||
def _is_child(self, parent, child): # type: (str, str) -> bool
|
||||
def _is_child(self, parent: Key, child: Key) -> bool:
|
||||
"""
|
||||
Returns whether a key is strictly a child of another key.
|
||||
AoT siblings are not considered children of one another.
|
||||
"""
|
||||
parent_parts = tuple(self._split_table_name(parent))
|
||||
child_parts = tuple(self._split_table_name(child))
|
||||
parent_parts = tuple(parent)
|
||||
child_parts = tuple(child)
|
||||
|
||||
if parent_parts == child_parts:
|
||||
return False
|
||||
|
||||
return parent_parts == child_parts[: len(parent_parts)]
|
||||
|
||||
def _split_table_name(self, name): # type: (str) -> Generator[Key]
|
||||
in_name = False
|
||||
current = ""
|
||||
t = KeyType.Bare
|
||||
parts = 0
|
||||
for c in name:
|
||||
c = TOMLChar(c)
|
||||
|
||||
if c == ".":
|
||||
if in_name:
|
||||
current += c
|
||||
continue
|
||||
|
||||
if not current:
|
||||
raise self.parse_error()
|
||||
|
||||
yield Key(current.strip(), t=t, sep="", original=current)
|
||||
|
||||
parts += 1
|
||||
|
||||
current = ""
|
||||
t = KeyType.Bare
|
||||
continue
|
||||
elif c in {"'", '"'}:
|
||||
if in_name:
|
||||
if (
|
||||
t == KeyType.Literal
|
||||
and c == '"'
|
||||
or t == KeyType.Basic
|
||||
and c == "'"
|
||||
):
|
||||
current += c
|
||||
continue
|
||||
|
||||
if c != t.value:
|
||||
raise self.parse_error()
|
||||
|
||||
in_name = False
|
||||
else:
|
||||
if (
|
||||
current.strip()
|
||||
and TOMLChar(current[-1]).is_spaces()
|
||||
and not parts
|
||||
):
|
||||
raise self.parse_error()
|
||||
|
||||
in_name = True
|
||||
t = KeyType.Literal if c == "'" else KeyType.Basic
|
||||
|
||||
continue
|
||||
elif in_name or c.is_bare_key_char():
|
||||
current += c
|
||||
elif c.is_spaces():
|
||||
# A space is only valid at this point
|
||||
# if it's in between parts.
|
||||
# We store it for now and will check
|
||||
# later if it's valid
|
||||
current += c
|
||||
continue
|
||||
else:
|
||||
raise self.parse_error()
|
||||
|
||||
if current.strip():
|
||||
yield Key(current.strip(), t=t, sep="", original=current)
|
||||
|
||||
def _parse_item(self): # type: () -> Optional[Tuple[Optional[Key], Item]]
|
||||
def _parse_item(self) -> Optional[Tuple[Optional[Key], Item]]:
|
||||
"""
|
||||
Attempts to parse the next item and returns it, along with its key
|
||||
if the item is value-like.
|
||||
@@ -305,7 +234,7 @@ class Parser:
|
||||
|
||||
return self._parse_key_value(True)
|
||||
|
||||
def _parse_comment_trail(self): # type: () -> Tuple[str, str, str]
|
||||
def _parse_comment_trail(self, parse_trail: bool = True) -> Tuple[str, str, str]:
|
||||
"""
|
||||
Returns (comment_ws, comment, trail)
|
||||
If there is no comment, comment_ws and comment will
|
||||
@@ -350,22 +279,23 @@ class Parser:
|
||||
if self.end():
|
||||
break
|
||||
|
||||
while self._current.is_spaces() and self.inc():
|
||||
pass
|
||||
|
||||
if self._current == "\r":
|
||||
self.inc()
|
||||
|
||||
if self._current == "\n":
|
||||
self.inc()
|
||||
|
||||
trail = ""
|
||||
if self._idx != self._marker or self._current.is_ws():
|
||||
trail = self.extract()
|
||||
if parse_trail:
|
||||
while self._current.is_spaces() and self.inc():
|
||||
pass
|
||||
|
||||
if self._current == "\r":
|
||||
self.inc()
|
||||
|
||||
if self._current == "\n":
|
||||
self.inc()
|
||||
|
||||
if self._idx != self._marker or self._current.is_ws():
|
||||
trail = self.extract()
|
||||
|
||||
return comment_ws, comment, trail
|
||||
|
||||
def _parse_key_value(self, parse_comment=False): # type: (bool) -> (Key, Item)
|
||||
def _parse_key_value(self, parse_comment: bool = False) -> Tuple[Key, Item]:
|
||||
# Leading indent
|
||||
self.mark()
|
||||
|
||||
@@ -386,7 +316,8 @@ class Parser:
|
||||
raise self.parse_error(UnexpectedCharError, "=")
|
||||
else:
|
||||
found_equals = True
|
||||
pass
|
||||
if not found_equals:
|
||||
raise self.parse_error(UnexpectedCharError, self._current)
|
||||
|
||||
if not key.sep:
|
||||
key.sep = self.extract()
|
||||
@@ -411,57 +342,53 @@ class Parser:
|
||||
|
||||
return key, val
|
||||
|
||||
def _parse_key(self): # type: () -> Key
|
||||
def _parse_key(self) -> Key:
|
||||
"""
|
||||
Parses a Key at the current position;
|
||||
WS before the key must be exhausted first at the callsite.
|
||||
"""
|
||||
self.mark()
|
||||
while self._current.is_spaces() and self.inc():
|
||||
# Skip any leading whitespace
|
||||
pass
|
||||
if self._current in "\"'":
|
||||
return self._parse_quoted_key()
|
||||
else:
|
||||
return self._parse_bare_key()
|
||||
|
||||
def _parse_quoted_key(self): # type: () -> Key
|
||||
def _parse_quoted_key(self) -> Key:
|
||||
"""
|
||||
Parses a key enclosed in either single or double quotes.
|
||||
"""
|
||||
# Extract the leading whitespace
|
||||
original = self.extract()
|
||||
quote_style = self._current
|
||||
key_type = None
|
||||
dotted = False
|
||||
for t in KeyType:
|
||||
if t.value == quote_style:
|
||||
key_type = t
|
||||
break
|
||||
key_type = next((t for t in KeyType if t.value == quote_style), None)
|
||||
|
||||
if key_type is None:
|
||||
raise RuntimeError("Should not have entered _parse_quoted_key()")
|
||||
|
||||
self.inc()
|
||||
key_str = self._parse_string(
|
||||
StringType.SLB if key_type == KeyType.Basic else StringType.SLL
|
||||
)
|
||||
if key_str._t.is_multiline():
|
||||
raise self.parse_error(UnexpectedCharError, key_str._t.value)
|
||||
original += key_str.as_string()
|
||||
self.mark()
|
||||
|
||||
while self._current != quote_style and self.inc():
|
||||
while self._current.is_spaces() and self.inc():
|
||||
pass
|
||||
|
||||
key = self.extract()
|
||||
|
||||
original += self.extract()
|
||||
key = SingleKey(str(key_str), t=key_type, sep="", original=original)
|
||||
if self._current == ".":
|
||||
self.inc()
|
||||
dotted = True
|
||||
key += "." + self._parse_key().as_string()
|
||||
key_type = KeyType.Bare
|
||||
else:
|
||||
self.inc()
|
||||
key = key.concat(self._parse_key())
|
||||
|
||||
return Key(key, key_type, "", dotted)
|
||||
return key
|
||||
|
||||
def _parse_bare_key(self): # type: () -> Key
|
||||
def _parse_bare_key(self) -> Key:
|
||||
"""
|
||||
Parses a bare key.
|
||||
"""
|
||||
key_type = None
|
||||
dotted = False
|
||||
|
||||
self.mark()
|
||||
while (
|
||||
self._current.is_bare_key_char() or self._current.is_spaces()
|
||||
) and self.inc():
|
||||
@@ -471,85 +398,21 @@ class Parser:
|
||||
key = original.strip()
|
||||
if not key:
|
||||
# Empty key
|
||||
raise self.parse_error(ParseError, "Empty key found")
|
||||
raise self.parse_error(EmptyKeyError)
|
||||
|
||||
if " " in key:
|
||||
# Bare key with spaces in it
|
||||
raise self.parse_error(ParseError, 'Invalid key "{}"'.format(key))
|
||||
raise self.parse_error(ParseError, f'Invalid key "{key}"')
|
||||
|
||||
key = SingleKey(key, KeyType.Bare, "", original)
|
||||
|
||||
if self._current == ".":
|
||||
self.inc()
|
||||
dotted = True
|
||||
original += "." + self._parse_key().as_string()
|
||||
key = original.strip()
|
||||
key_type = KeyType.Bare
|
||||
key = key.concat(self._parse_key())
|
||||
|
||||
return Key(key, key_type, "", dotted, original=original)
|
||||
return key
|
||||
|
||||
def _handle_dotted_key(
|
||||
self, container, key, value
|
||||
): # type: (Union[Container, Table], Key, Any) -> None
|
||||
names = tuple(self._split_table_name(key.as_string()))
|
||||
name = names[0]
|
||||
name._dotted = True
|
||||
if name in container:
|
||||
if not isinstance(value, Table):
|
||||
table = Table(Container(True), Trivia(), False, is_super_table=True)
|
||||
_table = table
|
||||
for i, _name in enumerate(names[1:]):
|
||||
if i == len(names) - 2:
|
||||
_name.sep = key.sep
|
||||
|
||||
_table.append(_name, value)
|
||||
else:
|
||||
_name._dotted = True
|
||||
_table.append(
|
||||
_name,
|
||||
Table(
|
||||
Container(True),
|
||||
Trivia(),
|
||||
False,
|
||||
is_super_table=i < len(names) - 2,
|
||||
),
|
||||
)
|
||||
|
||||
_table = _table[_name]
|
||||
|
||||
value = table
|
||||
|
||||
container.append(name, value)
|
||||
|
||||
return
|
||||
else:
|
||||
table = Table(Container(True), Trivia(), False, is_super_table=True)
|
||||
if isinstance(container, Table):
|
||||
container.raw_append(name, table)
|
||||
else:
|
||||
container.append(name, table)
|
||||
|
||||
for i, _name in enumerate(names[1:]):
|
||||
if i == len(names) - 2:
|
||||
_name.sep = key.sep
|
||||
|
||||
table.append(_name, value)
|
||||
else:
|
||||
_name._dotted = True
|
||||
if _name in table.value:
|
||||
table = table.value[_name]
|
||||
else:
|
||||
table.append(
|
||||
_name,
|
||||
Table(
|
||||
Container(True),
|
||||
Trivia(),
|
||||
False,
|
||||
is_super_table=i < len(names) - 2,
|
||||
),
|
||||
)
|
||||
|
||||
table = table[_name]
|
||||
|
||||
def _parse_value(self): # type: () -> Item
|
||||
def _parse_value(self) -> Item:
|
||||
"""
|
||||
Attempts to parse a value at the current position.
|
||||
"""
|
||||
@@ -674,7 +537,7 @@ class Parser:
|
||||
def _parse_false(self):
|
||||
return self._parse_bool(BoolType.FALSE)
|
||||
|
||||
def _parse_bool(self, style): # type: (BoolType) -> Bool
|
||||
def _parse_bool(self, style: BoolType) -> Bool:
|
||||
with self._state:
|
||||
style = BoolType(style)
|
||||
|
||||
@@ -685,25 +548,25 @@ class Parser:
|
||||
|
||||
return Bool(style, Trivia())
|
||||
|
||||
def _parse_array(self): # type: () -> Array
|
||||
def _parse_array(self) -> Array:
|
||||
# Consume opening bracket, EOF here is an issue (middle of array)
|
||||
self.inc(exception=UnexpectedEofError)
|
||||
|
||||
elems = [] # type: List[Item]
|
||||
elems: List[Item] = []
|
||||
prev_value = None
|
||||
while True:
|
||||
# consume whitespace
|
||||
mark = self._idx
|
||||
self.consume(TOMLChar.SPACES)
|
||||
newline = self.consume(TOMLChar.NL)
|
||||
self.consume(TOMLChar.SPACES + TOMLChar.NL)
|
||||
indent = self._src[mark : self._idx]
|
||||
newline = set(TOMLChar.NL) & set(indent)
|
||||
if newline:
|
||||
elems.append(Whitespace(indent))
|
||||
continue
|
||||
|
||||
# consume comment
|
||||
if self._current == "#":
|
||||
cws, comment, trail = self._parse_comment_trail()
|
||||
cws, comment, trail = self._parse_comment_trail(parse_trail=False)
|
||||
elems.append(Comment(Trivia(indent, cws, comment, trail)))
|
||||
continue
|
||||
|
||||
@@ -743,7 +606,7 @@ class Parser:
|
||||
else:
|
||||
return res
|
||||
|
||||
def _parse_inline_table(self): # type: () -> InlineTable
|
||||
def _parse_inline_table(self) -> InlineTable:
|
||||
# consume opening bracket, EOF here is an issue (middle of array)
|
||||
self.inc(exception=UnexpectedEofError)
|
||||
|
||||
@@ -779,10 +642,7 @@ class Parser:
|
||||
raise self.parse_error(UnexpectedCharError, self._current)
|
||||
|
||||
key, val = self._parse_key_value(False)
|
||||
if key.is_dotted():
|
||||
self._handle_dotted_key(elems, key, val)
|
||||
else:
|
||||
elems.add(key, val)
|
||||
elems.add(key, val)
|
||||
|
||||
# consume trailing whitespace
|
||||
mark = self._idx
|
||||
@@ -799,17 +659,18 @@ class Parser:
|
||||
|
||||
return InlineTable(elems, Trivia())
|
||||
|
||||
def _parse_number(self, raw, trivia): # type: (str, Trivia) -> Optional[Item]
|
||||
def _parse_number(self, raw: str, trivia: Trivia) -> Optional[Item]:
|
||||
# Leading zeros are not allowed
|
||||
sign = ""
|
||||
if raw.startswith(("+", "-")):
|
||||
sign = raw[0]
|
||||
raw = raw[1:]
|
||||
|
||||
if (
|
||||
len(raw) > 1
|
||||
and raw.startswith("0")
|
||||
if len(raw) > 1 and (
|
||||
raw.startswith("0")
|
||||
and not raw.startswith(("0.", "0o", "0x", "0b", "0e"))
|
||||
or sign
|
||||
and raw.startswith(".")
|
||||
):
|
||||
return
|
||||
|
||||
@@ -829,12 +690,16 @@ class Parser:
|
||||
base = 16
|
||||
|
||||
# Underscores should be surrounded by digits
|
||||
clean = re.sub("(?i)(?<={})_(?={})".format(digits, digits), "", raw)
|
||||
clean = re.sub(f"(?i)(?<={digits})_(?={digits})", "", raw).lower()
|
||||
|
||||
if "_" in clean:
|
||||
return
|
||||
|
||||
if clean.endswith("."):
|
||||
if (
|
||||
clean.endswith(".")
|
||||
or not clean.startswith("0x")
|
||||
and clean.split("e", 1)[0].endswith(".")
|
||||
):
|
||||
return
|
||||
|
||||
try:
|
||||
@@ -845,11 +710,11 @@ class Parser:
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
def _parse_literal_string(self): # type: () -> String
|
||||
def _parse_literal_string(self) -> String:
|
||||
with self._state:
|
||||
return self._parse_string(StringType.SLL)
|
||||
|
||||
def _parse_basic_string(self): # type: () -> String
|
||||
def _parse_basic_string(self) -> String:
|
||||
with self._state:
|
||||
return self._parse_string(StringType.SLB)
|
||||
|
||||
@@ -898,12 +763,12 @@ class Parser:
|
||||
|
||||
raise self.parse_error(InvalidCharInStringError, self._current)
|
||||
|
||||
def _parse_string(self, delim): # type: (StringType) -> String
|
||||
def _parse_string(self, delim: StringType) -> String:
|
||||
# only keep parsing for string if the current character matches the delim
|
||||
if self._current != delim.unit:
|
||||
raise self.parse_error(
|
||||
InternalParserError,
|
||||
"Invalid character for string type {}".format(delim),
|
||||
f"Invalid character for string type {delim}",
|
||||
)
|
||||
|
||||
# consume the opening/first delim, EOF here is an issue
|
||||
@@ -1006,8 +871,8 @@ class Parser:
|
||||
self.inc(exception=UnexpectedEofError)
|
||||
|
||||
def _parse_table(
|
||||
self, parent_name=None, parent=None
|
||||
): # type: (Optional[str], Optional[Table]) -> Tuple[Key, Union[Table, AoT]]
|
||||
self, parent_name: Optional[Key] = None, parent: Optional[Table] = None
|
||||
) -> Tuple[Key, Union[Table, AoT]]:
|
||||
"""
|
||||
Parses a table element.
|
||||
"""
|
||||
@@ -1028,58 +893,28 @@ class Parser:
|
||||
raise self.parse_error(UnexpectedEofError)
|
||||
|
||||
is_aot = True
|
||||
|
||||
# Consume any whitespace
|
||||
self.mark()
|
||||
while self._current.is_spaces() and self.inc():
|
||||
pass
|
||||
|
||||
ws_prefix = self.extract()
|
||||
|
||||
# Key
|
||||
if self._current in [StringType.SLL.value, StringType.SLB.value]:
|
||||
delimiter = (
|
||||
StringType.SLL
|
||||
if self._current == StringType.SLL.value
|
||||
else StringType.SLB
|
||||
)
|
||||
name = self._parse_string(delimiter)
|
||||
name = "{delimiter}{name}{delimiter}".format(
|
||||
delimiter=delimiter.value, name=name
|
||||
)
|
||||
|
||||
self.mark()
|
||||
while self._current != "]" and self.inc():
|
||||
if self.end():
|
||||
raise self.parse_error(UnexpectedEofError)
|
||||
|
||||
pass
|
||||
|
||||
ws_suffix = self.extract()
|
||||
name += ws_suffix
|
||||
else:
|
||||
self.mark()
|
||||
while self._current != "]" and self.inc():
|
||||
if self.end():
|
||||
raise self.parse_error(UnexpectedEofError)
|
||||
|
||||
pass
|
||||
|
||||
name = self.extract()
|
||||
|
||||
name = ws_prefix + name
|
||||
|
||||
if not name.strip():
|
||||
try:
|
||||
key = self._parse_key()
|
||||
except EmptyKeyError:
|
||||
raise self.parse_error(EmptyTableNameError) from None
|
||||
if self.end():
|
||||
raise self.parse_error(UnexpectedEofError)
|
||||
elif self._current != "]":
|
||||
raise self.parse_error(UnexpectedCharError, self._current)
|
||||
elif not key.key.strip():
|
||||
raise self.parse_error(EmptyTableNameError)
|
||||
|
||||
key = Key(name, sep="")
|
||||
name_parts = tuple(self._split_table_name(name))
|
||||
key.sep = ""
|
||||
full_key = key
|
||||
name_parts = tuple(key)
|
||||
if any(" " in part.key.strip() and part.is_bare() for part in name_parts):
|
||||
raise self.parse_error(ParseError, 'Invalid table name "{}"'.format(name))
|
||||
raise self.parse_error(
|
||||
ParseError, f'Invalid table name "{full_key.as_string()}"'
|
||||
)
|
||||
|
||||
missing_table = False
|
||||
if parent_name:
|
||||
parent_name_parts = tuple(self._split_table_name(parent_name))
|
||||
parent_name_parts = tuple(parent_name)
|
||||
else:
|
||||
parent_name_parts = tuple()
|
||||
|
||||
@@ -1102,8 +937,8 @@ class Parser:
|
||||
values,
|
||||
Trivia(indent, cws, comment, trail),
|
||||
is_aot,
|
||||
name=name,
|
||||
display_name=name,
|
||||
name=name_parts[0].key if name_parts else key.key,
|
||||
display_name=full_key.as_string(),
|
||||
)
|
||||
|
||||
if len(name_parts) > 1:
|
||||
@@ -1116,36 +951,36 @@ class Parser:
|
||||
table = Table(
|
||||
Container(True),
|
||||
Trivia(indent, cws, comment, trail),
|
||||
is_aot and name_parts[0].key in self._aot_stack,
|
||||
is_aot and name_parts[0] in self._aot_stack,
|
||||
is_super_table=True,
|
||||
name=name_parts[0].key,
|
||||
)
|
||||
|
||||
result = table
|
||||
key = name_parts[0]
|
||||
result = table
|
||||
key = name_parts[0]
|
||||
|
||||
for i, _name in enumerate(name_parts[1:]):
|
||||
if _name in table:
|
||||
child = table[_name]
|
||||
else:
|
||||
child = Table(
|
||||
Container(True),
|
||||
Trivia(indent, cws, comment, trail),
|
||||
is_aot and i == len(name_parts[1:]) - 1,
|
||||
is_super_table=i < len(name_parts[1:]) - 1,
|
||||
name=_name.key,
|
||||
display_name=name if i == len(name_parts[1:]) - 1 else None,
|
||||
)
|
||||
for i, _name in enumerate(name_parts[1:]):
|
||||
if _name in table:
|
||||
child = table[_name]
|
||||
else:
|
||||
child = Table(
|
||||
Container(True),
|
||||
Trivia(indent, cws, comment, trail),
|
||||
is_aot and i == len(name_parts) - 2,
|
||||
is_super_table=i < len(name_parts) - 2,
|
||||
name=_name.key,
|
||||
display_name=full_key.as_string()
|
||||
if i == len(name_parts) - 2
|
||||
else None,
|
||||
)
|
||||
|
||||
if is_aot and i == len(name_parts[1:]) - 1:
|
||||
table.raw_append(
|
||||
_name, AoT([child], name=table.name, parsed=True)
|
||||
)
|
||||
else:
|
||||
table.raw_append(_name, child)
|
||||
if is_aot and i == len(name_parts) - 2:
|
||||
table.raw_append(_name, AoT([child], name=table.name, parsed=True))
|
||||
else:
|
||||
table.raw_append(_name, child)
|
||||
|
||||
table = child
|
||||
values = table.value
|
||||
table = child
|
||||
values = table.value
|
||||
else:
|
||||
if name_parts:
|
||||
key = name_parts[0]
|
||||
@@ -1155,27 +990,24 @@ class Parser:
|
||||
if item:
|
||||
_key, item = item
|
||||
if not self._merge_ws(item, values):
|
||||
if _key is not None and _key.is_dotted():
|
||||
self._handle_dotted_key(table, _key, item)
|
||||
else:
|
||||
table.raw_append(_key, item)
|
||||
table.raw_append(_key, item)
|
||||
else:
|
||||
if self._current == "[":
|
||||
is_aot_next, name_next = self._peek_table()
|
||||
_, key_next = self._peek_table()
|
||||
|
||||
if self._is_child(name, name_next):
|
||||
key_next, table_next = self._parse_table(name, table)
|
||||
if self._is_child(full_key, key_next):
|
||||
key_next, table_next = self._parse_table(full_key, table)
|
||||
|
||||
table.raw_append(key_next, table_next)
|
||||
|
||||
# Picking up any sibling
|
||||
while not self.end():
|
||||
_, name_next = self._peek_table()
|
||||
_, key_next = self._peek_table()
|
||||
|
||||
if not self._is_child(name, name_next):
|
||||
if not self._is_child(full_key, key_next):
|
||||
break
|
||||
|
||||
key_next, table_next = self._parse_table(name, table)
|
||||
key_next, table_next = self._parse_table(full_key, table)
|
||||
|
||||
table.raw_append(key_next, table_next)
|
||||
|
||||
@@ -1189,12 +1021,12 @@ class Parser:
|
||||
if isinstance(result, Null):
|
||||
result = table
|
||||
|
||||
if is_aot and (not self._aot_stack or name != self._aot_stack[-1]):
|
||||
result = self._parse_aot(result, name)
|
||||
if is_aot and (not self._aot_stack or full_key != self._aot_stack[-1]):
|
||||
result = self._parse_aot(result, full_key)
|
||||
|
||||
return key, result
|
||||
|
||||
def _peek_table(self): # type: () -> Tuple[bool, str]
|
||||
def _peek_table(self) -> Tuple[bool, Key]:
|
||||
"""
|
||||
Peeks ahead non-intrusively by cloning then restoring the
|
||||
initial state of the parser.
|
||||
@@ -1203,7 +1035,6 @@ class Parser:
|
||||
as well as whether it is part of an AoT.
|
||||
"""
|
||||
# we always want to restore after exiting this scope
|
||||
table_name = ""
|
||||
with self._state(save_marker=True, restore=True):
|
||||
if self._current != "[":
|
||||
raise self.parse_error(
|
||||
@@ -1217,15 +1048,12 @@ class Parser:
|
||||
if self._current == "[":
|
||||
self.inc()
|
||||
is_aot = True
|
||||
try:
|
||||
return is_aot, self._parse_key()
|
||||
except EmptyKeyError:
|
||||
raise self.parse_error(EmptyTableNameError) from None
|
||||
|
||||
self.mark()
|
||||
|
||||
while self._current != "]" and self.inc():
|
||||
table_name = self.extract()
|
||||
|
||||
return is_aot, table_name
|
||||
|
||||
def _parse_aot(self, first, name_first): # type: (Table, str) -> AoT
|
||||
def _parse_aot(self, first: Table, name_first: Key) -> AoT:
|
||||
"""
|
||||
Parses all siblings of the provided table first and bundles them into
|
||||
an AoT.
|
||||
@@ -1244,7 +1072,7 @@ class Parser:
|
||||
|
||||
return AoT(payload, parsed=True)
|
||||
|
||||
def _peek(self, n): # type: (int) -> str
|
||||
def _peek(self, n: int) -> str:
|
||||
"""
|
||||
Peeks ahead n characters.
|
||||
|
||||
@@ -1254,7 +1082,7 @@ class Parser:
|
||||
with self._state(restore=True):
|
||||
buf = ""
|
||||
for _ in range(n):
|
||||
if self._current not in " \t\n\r#,]}":
|
||||
if self._current not in " \t\n\r#,]}" + self._src.EOF:
|
||||
buf += self._current
|
||||
self.inc()
|
||||
continue
|
||||
@@ -1262,9 +1090,7 @@ class Parser:
|
||||
break
|
||||
return buf
|
||||
|
||||
def _peek_unicode(
|
||||
self, is_long
|
||||
): # type: (bool) -> Tuple[Optional[str], Optional[str]]
|
||||
def _peek_unicode(self, is_long: bool) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""
|
||||
Peeks ahead non-intrusively by cloning then restoring the
|
||||
initial state of the parser.
|
||||
|
||||
Vendored
+29
-37
@@ -1,39 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import itertools
|
||||
|
||||
from copy import copy
|
||||
from typing import Any
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
from typing import Type
|
||||
|
||||
from ._compat import PY2
|
||||
from ._compat import unicode
|
||||
from .exceptions import ParseError
|
||||
from .exceptions import UnexpectedCharError
|
||||
from .exceptions import UnexpectedEofError
|
||||
from .toml_char import TOMLChar
|
||||
|
||||
|
||||
class _State:
|
||||
def __init__(
|
||||
self, source, save_marker=False, restore=False
|
||||
): # type: (_Source, Optional[bool], Optional[bool]) -> None
|
||||
self,
|
||||
source: "Source",
|
||||
save_marker: Optional[bool] = False,
|
||||
restore: Optional[bool] = False,
|
||||
) -> None:
|
||||
self._source = source
|
||||
self._save_marker = save_marker
|
||||
self.restore = restore
|
||||
|
||||
def __enter__(self): # type: () -> None
|
||||
def __enter__(self) -> "_State":
|
||||
# Entering this context manager - save the state
|
||||
if PY2:
|
||||
# Python 2.7 does not allow to directly copy
|
||||
# an iterator, so we have to make tees of the original
|
||||
# chars iterator.
|
||||
self._source._chars, self._chars = itertools.tee(self._source._chars)
|
||||
else:
|
||||
self._chars = copy(self._source._chars)
|
||||
self._chars = copy(self._source._chars)
|
||||
self._idx = self._source._idx
|
||||
self._current = self._source._current
|
||||
self._marker = self._source._marker
|
||||
@@ -55,14 +44,14 @@ class _StateHandler:
|
||||
State preserver for the Parser.
|
||||
"""
|
||||
|
||||
def __init__(self, source): # type: (Source) -> None
|
||||
def __init__(self, source: "Source") -> None:
|
||||
self._source = source
|
||||
self._states = []
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return _State(self._source, *args, **kwargs)
|
||||
|
||||
def __enter__(self): # type: () -> None
|
||||
def __enter__(self) -> None:
|
||||
state = self()
|
||||
self._states.append(state)
|
||||
return state.__enter__()
|
||||
@@ -72,11 +61,11 @@ class _StateHandler:
|
||||
return state.__exit__(exception_type, exception_val, trace)
|
||||
|
||||
|
||||
class Source(unicode):
|
||||
class Source(str):
|
||||
EOF = TOMLChar("\0")
|
||||
|
||||
def __init__(self, _): # type: (unicode) -> None
|
||||
super(Source, self).__init__()
|
||||
def __init__(self, _: str) -> None:
|
||||
super().__init__()
|
||||
|
||||
# Collection of TOMLChars
|
||||
self._chars = iter([(i, TOMLChar(c)) for i, c in enumerate(self)])
|
||||
@@ -97,28 +86,28 @@ class Source(unicode):
|
||||
self.mark()
|
||||
|
||||
@property
|
||||
def state(self): # type: () -> _StateHandler
|
||||
def state(self) -> _StateHandler:
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def idx(self): # type: () -> int
|
||||
def idx(self) -> int:
|
||||
return self._idx
|
||||
|
||||
@property
|
||||
def current(self): # type: () -> TOMLChar
|
||||
def current(self) -> TOMLChar:
|
||||
return self._current
|
||||
|
||||
@property
|
||||
def marker(self): # type: () -> int
|
||||
def marker(self) -> int:
|
||||
return self._marker
|
||||
|
||||
def extract(self): # type: () -> unicode
|
||||
def extract(self) -> str:
|
||||
"""
|
||||
Extracts the value between marker and index
|
||||
"""
|
||||
return self[self._marker : self._idx]
|
||||
|
||||
def inc(self, exception=None): # type: (Optional[Type[ParseError]]) -> bool
|
||||
def inc(self, exception: Optional[Type[ParseError]] = None) -> bool:
|
||||
"""
|
||||
Increments the parser if the end of the input has not been reached.
|
||||
Returns whether or not it was able to advance.
|
||||
@@ -135,7 +124,7 @@ class Source(unicode):
|
||||
|
||||
return False
|
||||
|
||||
def inc_n(self, n, exception=None): # type: (int, Exception) -> bool
|
||||
def inc_n(self, n: int, exception: Optional[Type[ParseError]] = None) -> bool:
|
||||
"""
|
||||
Increments the parser by n characters
|
||||
if the end of the input has not been reached.
|
||||
@@ -158,31 +147,34 @@ class Source(unicode):
|
||||
|
||||
# failed to consume minimum number of characters
|
||||
if min > 0:
|
||||
self.parse_error(UnexpectedCharError)
|
||||
raise self.parse_error(UnexpectedCharError, self.current)
|
||||
|
||||
def end(self): # type: () -> bool
|
||||
def end(self) -> bool:
|
||||
"""
|
||||
Returns True if the parser has reached the end of the input.
|
||||
"""
|
||||
return self._current is self.EOF
|
||||
|
||||
def mark(self): # type: () -> None
|
||||
def mark(self) -> None:
|
||||
"""
|
||||
Sets the marker to the index's current position
|
||||
"""
|
||||
self._marker = self._idx
|
||||
|
||||
def parse_error(
|
||||
self, exception=ParseError, *args
|
||||
): # type: (Type[ParseError], Any) -> ParseError
|
||||
self,
|
||||
exception: Type[ParseError] = ParseError,
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> ParseError:
|
||||
"""
|
||||
Creates a generic "parse error" at the current position.
|
||||
"""
|
||||
line, col = self._to_linecol()
|
||||
|
||||
return exception(line, col, *args)
|
||||
return exception(line, col, *args, **kwargs)
|
||||
|
||||
def _to_linecol(self): # type: () -> Tuple[int, int]
|
||||
def _to_linecol(self) -> Tuple[int, int]:
|
||||
cur = 0
|
||||
for i, line in enumerate(self.splitlines()):
|
||||
if cur + len(line) + 1 > self.idx:
|
||||
|
||||
Vendored
+9
-16
@@ -1,18 +1,11 @@
|
||||
import string
|
||||
|
||||
from ._compat import PY2
|
||||
from ._compat import unicode
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
if PY2:
|
||||
from pipenv.vendor.backports.functools_lru_cache import lru_cache
|
||||
else:
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
class TOMLChar(unicode):
|
||||
class TOMLChar(str):
|
||||
def __init__(self, c):
|
||||
super(TOMLChar, self).__init__()
|
||||
super().__init__()
|
||||
|
||||
if len(self) > 1:
|
||||
raise ValueError("A TOML character must be of length 1")
|
||||
@@ -25,42 +18,42 @@ class TOMLChar(unicode):
|
||||
WS = SPACES + NL
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def is_bare_key_char(self): # type: () -> bool
|
||||
def is_bare_key_char(self) -> bool:
|
||||
"""
|
||||
Whether the character is a valid bare key name or not.
|
||||
"""
|
||||
return self in self.BARE
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def is_kv_sep(self): # type: () -> bool
|
||||
def is_kv_sep(self) -> bool:
|
||||
"""
|
||||
Whether the character is a valid key/value separator ot not.
|
||||
"""
|
||||
return self in self.KV
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def is_int_float_char(self): # type: () -> bool
|
||||
def is_int_float_char(self) -> bool:
|
||||
"""
|
||||
Whether the character if a valid integer or float value character or not.
|
||||
"""
|
||||
return self in self.NUMBER
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def is_ws(self): # type: () -> bool
|
||||
def is_ws(self) -> bool:
|
||||
"""
|
||||
Whether the character is a whitespace character or not.
|
||||
"""
|
||||
return self in self.WS
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def is_nl(self): # type: () -> bool
|
||||
def is_nl(self) -> bool:
|
||||
"""
|
||||
Whether the character is a new line character or not.
|
||||
"""
|
||||
return self in self.NL
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def is_spaces(self): # type: () -> bool
|
||||
def is_spaces(self) -> bool:
|
||||
"""
|
||||
Whether the character is a space or not
|
||||
"""
|
||||
|
||||
Vendored
+10
-11
@@ -1,24 +1,23 @@
|
||||
import io
|
||||
|
||||
from typing import Any
|
||||
from typing import Dict
|
||||
|
||||
from .api import loads
|
||||
from .toml_document import TOMLDocument
|
||||
|
||||
|
||||
class TOMLFile(object):
|
||||
class TOMLFile:
|
||||
"""
|
||||
Represents a TOML file.
|
||||
|
||||
:param path: path to the TOML file
|
||||
"""
|
||||
|
||||
def __init__(self, path): # type: (str) -> None
|
||||
def __init__(self, path: str) -> None:
|
||||
self._path = path
|
||||
|
||||
def read(self): # type: () -> TOMLDocument
|
||||
with io.open(self._path, encoding="utf-8") as f:
|
||||
def read(self) -> TOMLDocument:
|
||||
"""Read the file content as a :class:`tomlkit.toml_document.TOMLDocument`."""
|
||||
with open(self._path, encoding="utf-8", newline="") as f:
|
||||
return loads(f.read())
|
||||
|
||||
def write(self, data): # type: (TOMLDocument) -> None
|
||||
with io.open(self._path, "w", encoding="utf-8") as f:
|
||||
def write(self, data: TOMLDocument) -> None:
|
||||
"""Write the TOMLDocument to the file."""
|
||||
with open(self._path, "w", encoding="utf-8", newline="") as f:
|
||||
f.write(data.as_string())
|
||||
|
||||
Reference in New Issue
Block a user