pax_global_header 0000666 0000000 0000000 00000000064 13162074272 0014516 g ustar 00root root 0000000 0000000 52 comment=98954c4e682ada3cb73bbe12e90b7b2178885a2f
toml-0.9.3/ 0000775 0000000 0000000 00000000000 13162074272 0012502 5 ustar 00root root 0000000 0000000 toml-0.9.3/.flake8 0000664 0000000 0000000 00000000511 13162074272 0013652 0 ustar 00root root 0000000 0000000 [flake8]
exclude =
# Do not track .git dir
.git,
# Do not track virtualenv dir
venv,
# Ignore folders in gitignore
dist,
build,
# Do not track pycache dirs
__pycache__,
max-line-length = 80
ignore =
# Output config:
show-source = True
statistics = True
tee = True
output-file = .flake8.log
toml-0.9.3/.gitignore 0000664 0000000 0000000 00000000150 13162074272 0014466 0 ustar 00root root 0000000 0000000 # Filetypes
*~
*.html
*.pyc
# Distutils/Setuptools
build/
dist/
*.egg-info
# Flake8 Stuff
.flake8.log
toml-0.9.3/.travis.yml 0000664 0000000 0000000 00000000650 13162074272 0014614 0 ustar 00root root 0000000 0000000 language: python
python:
- "2.6"
- "2.7"
- "3.3"
- "3.4"
- "3.5"
- "3.6"
- "pypy"
notifications:
email: false
addons:
apt:
packages:
- golang
before_install:
- export GOPATH=~/go
- go get github.com/uiri/toml-test
- pip install flake8
install:
- python setup.py install
- chmod +x ./tests/*.sh
script:
- ~/go/bin/toml-test ./tests/decoding_test.sh
- python ./tests/api_test.py
- ./tests/flake8.sh
toml-0.9.3/CONTRIBUTING 0000664 0000000 0000000 00000001653 13162074272 0014341 0 ustar 00root root 0000000 0000000 ************
Contributing
************
Issues and Pull Requests are always welcome. Thank you in advance for
your contribution!
Reporting Issues
================
Before reporting an issue, please test the issue using the latest
development version to see if your issue has been fixed since the
latest release.
Testing
=======
There is a ``decoding_test.py`` script in the *tests/* directory
which acts as a harness in order to allow ``toml`` to be used with
the toml test suite, written (unfortunately) in Go.
Directions
----------
1. Install `Go `_ (AKA golang)
2. Get the toml-test suite from `here `_
and follow the instructions under the **Try it out** section of the README.
3. Test your changes for both versions of Python:
* For Python 2, use ``~/go/bin/toml-test ./tests/decoding_test2.sh``
* For Python 3, use ``~/go/bin/toml-test ./tests/decoding_test3.sh``
toml-0.9.3/LICENSE 0000664 0000000 0000000 00000002276 13162074272 0013516 0 ustar 00root root 0000000 0000000 The MIT License
Copyright 2013-2017 Uiri Noyb
Copyright 2015-2016 Julien Enselme
Copyright 2016 Google Inc.
Copyright 2017 Samuel Vasko
Copyright 2017 Nate Prewitt
Copyright 2017 Jack Evans
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. toml-0.9.3/MANIFEST.in 0000664 0000000 0000000 00000000104 13162074272 0014233 0 ustar 00root root 0000000 0000000 include toml.py
include setup.py
include README.rst
include LICENSE
toml-0.9.3/README.rst 0000664 0000000 0000000 00000010405 13162074272 0014171 0 ustar 00root root 0000000 0000000 ****
TOML
****
.. image:: https://badge.fury.io/py/toml.svg
:target: https://badge.fury.io/py/toml
.. image:: https://travis-ci.org/uiri/toml.svg?branch=master
:target: https://travis-ci.org/uiri/toml
A Python library for parsing and creating `TOML `_.
The module passes `the TOML test suite `_
which is a fork of `BurntSushi's TOML test suite `_.
See also:
* `The TOML Standard `_
* `The current TOML specification `_
Installation
============
To install the latest release on `PyPi `_,
simply run:
::
pip install toml
Or to install the latest development version, run:
::
git clone https://github.com/uiri/toml.git
cd toml
python setup.py install
Quick Tutorial
==============
*toml.loads* takes in a string containing standard TOML-formatted data and
returns a dictionary containing the parsed data.
.. code:: pycon
>>> import toml
>>> toml_string = """
... # This is a TOML document.
...
... title = "TOML Example"
...
... [owner]
... name = "Tom Preston-Werner"
... dob = 1979-05-27T07:32:00-08:00 # First class dates
...
... [database]
... server = "192.168.1.1"
... ports = [ 8001, 8001, 8002 ]
... connection_max = 5000
... enabled = true
...
... [servers]
...
... # Indentation (tabs and/or spaces) is allowed but not required
... [servers.alpha]
... ip = "10.0.0.1"
... dc = "eqdc10"
...
... [servers.beta]
... ip = "10.0.0.2"
... dc = "eqdc10"
...
... [clients]
... data = [ ["gamma", "delta"], [1, 2] ]
...
... # Line breaks are OK when inside arrays
... hosts = [
... "alpha",
... "omega"
... ]
... """
>>> parsed_toml = toml.loads(toml_string)
*toml.dumps* takes a dictionary and returns a string containing the
corresponding TOML-formatted data.
.. code:: pycon
>>> new_toml_string = toml.dumps(parsed_toml)
>>> print(new_toml_string)
title = "TOML Example"
[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00Z
[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002,]
connection_max = 5000
enabled = true
[clients]
data = [ [ "gamma", "delta",], [ 1, 2,],]
hosts = [ "alpha", "omega",]
[servers.alpha]
ip = "10.0.0.1"
dc = "eqdc10"
[servers.beta]
ip = "10.0.0.2"
dc = "eqdc10"
For more functions, view the API Reference below.
API Reference
=============
``toml.load(f, _dict=dict)``
Parse a file or a list of files as TOML and return a dictionary.
:Args:
* ``f``: A path to a file, list of filepaths (to be read into single
object) or a file descriptor
* ``_dict``: The class of the dictionary object to be returned
:Returns:
A dictionary (or object ``_dict``) containing parsed TOML data
:Raises:
* ``TypeError``: When ``f`` is an invalid type or is a list containing
invalid types
* ``TomlDecodeError``: When an error occurs while decoding the file(s)
``toml.loads(s, _dict=dict)``
Parse a TOML-formatted string to a dictionary.
:Args:
* ``s``: The TOML-formatted string to be parsed
* ``_dict``: Specifies the class of the returned toml dictionary
:Returns:
A dictionary (or object ``_dict``) containing parsed TOML data
:Raises:
* ``TypeError``: When a non-string object is passed
* ``TomlDecodeError``: When an error occurs while decoding the
TOML-formatted string
``toml.dump(o, f)``
Write a dictionary to a file containing TOML-formatted data
:Args:
* ``o``: An object to be converted into TOML
* ``f``: A File descriptor where the TOML-formatted output should be stored
:Returns:
A string containing the TOML-formatted data corresponding to object ``o``
:Raises:
* ``TypeError``: When anything other than file descriptor is passed
``toml.dumps(o)``
Create a TOML-formatted string from an input object
:Args:
* ``o``: An object to be converted into TOML
:Returns:
A string containing the TOML-formatted data corresponding to object ``o``
Licensing
=========
This project is released under the terms of the MIT Open Source License. View
*LICENSE.txt* for more information.
toml-0.9.3/RELEASE.rst 0000664 0000000 0000000 00000001031 13162074272 0014307 0 ustar 00root root 0000000 0000000 ****
Releasing
****
New versions of the TOML library are released on PyPI and installable via pip.
A new release is marked via a git tag.
Version numbering loosely follows semantic versioning.
1.0.0 will be the first version to support TOML 1.0.0. TOML still has yet to hit TOML 1.0.0 so its release is postponed indefinitely.
The version number is recorded in the source code (toml.py) itself as the __version__ variable.
The LICENSE Copyright notice should be up to date with a year-author pair for each significant contributor.
toml-0.9.3/examples/ 0000775 0000000 0000000 00000000000 13162074272 0014320 5 ustar 00root root 0000000 0000000 toml-0.9.3/examples/example-v0.4.0.toml 0000664 0000000 0000000 00000012172 13162074272 0017476 0 ustar 00root root 0000000 0000000 ################################################################################
## Comment
# Speak your mind with the hash symbol. They go from the symbol to the end of
# the line.
################################################################################
## Table
# Tables (also known as hash tables or dictionaries) are collections of
# key/value pairs. They appear in square brackets on a line by themselves.
[table]
key = "value" # Yeah, you can do this.
# Nested tables are denoted by table names with dots in them. Name your tables
# whatever crap you please, just don't use #, ., [ or ].
[table.subtable]
key = "another value"
# You don't need to specify all the super-tables if you don't want to. TOML
# knows how to do it for you.
# [x] you
# [x.y] don't
# [x.y.z] need these
[x.y.z.w] # for this to work
################################################################################
## Inline Table
# Inline tables provide a more compact syntax for expressing tables. They are
# especially useful for grouped data that can otherwise quickly become verbose.
# Inline tables are enclosed in curly braces `{` and `}`. No newlines are
# allowed between the curly braces unless they are valid within a value.
[table.inline]
name = { first = "Tom", last = "Preston-Werner" }
point = { x = 1, y = 2 }
################################################################################
## String
# There are four ways to express strings: basic, multi-line basic, literal, and
# multi-line literal. All strings must contain only valid UTF-8 characters.
[string.basic]
basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."
[string.multiline]
# The following strings are byte-for-byte equivalent:
key1 = "One\nTwo"
key2 = """One\nTwo"""
key3 = """
One
Two"""
[string.multiline.continued]
# The following strings are byte-for-byte equivalent:
key1 = "The quick brown fox jumps over the lazy dog."
key2 = """
The quick brown \
fox jumps over \
the lazy dog."""
key3 = """\
The quick brown \
fox jumps over \
the lazy dog.\
"""
[string.literal]
# What you see is what you get.
winpath = 'C:\Users\nodejs\templates'
winpath2 = '\\ServerX\admin$\system32\'
quoted = 'Tom "Dubs" Preston-Werner'
regex = '<\i\c*\s*>'
[string.literal.multiline]
regex2 = '''I [dw]on't need \d{2} apples'''
lines = '''
The first newline is
trimmed in raw strings.
All other whitespace
is preserved.
'''
################################################################################
## Integer
# Integers are whole numbers. Positive numbers may be prefixed with a plus sign.
# Negative numbers are prefixed with a minus sign.
[integer]
key1 = +99
key2 = 42
key3 = 0
key4 = -17
[integer.underscores]
# For large numbers, you may use underscores to enhance readability. Each
# underscore must be surrounded by at least one digit.
key1 = 1_000
key2 = 5_349_221
key3 = 1_2_3_4_5 # valid but inadvisable
################################################################################
## Float
# A float consists of an integer part (which may be prefixed with a plus or
# minus sign) followed by a fractional part and/or an exponent part.
[float.fractional]
key1 = +1.0
key2 = 3.1415
key3 = -0.01
[float.exponent]
key1 = 5e+22
key2 = 1e6
key3 = -2E-2
[float.both]
key = 6.626e-34
[float.underscores]
key1 = 9_224_617.445_991_228_313
key2 = 1e1_000
################################################################################
## Boolean
# Booleans are just the tokens you're used to. Always lowercase.
[boolean]
True = true
False = false
################################################################################
## Datetime
# Datetimes are RFC 3339 dates.
[datetime]
key1 = 1979-05-27T07:32:00Z
key2 = 1979-05-27T00:32:00-07:00
key3 = 1979-05-27T00:32:00.999999-07:00
################################################################################
## Array
# Arrays are square brackets with other primitives inside. Whitespace is
# ignored. Elements are separated by commas. Data types may not be mixed.
[array]
key1 = [ 1, 2, 3 ]
key2 = [ "red", "yellow", "green" ]
key3 = [ [ 1, 2 ], [3, 4, 5] ]
key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok
# Arrays can also be multiline. So in addition to ignoring whitespace, arrays
# also ignore newlines between the brackets. Terminating commas are ok before
# the closing bracket.
key5 = [
1, 2, 3
]
key6 = [
1,
2, # this is ok
]
################################################################################
## Array of Tables
# These can be expressed by using a table name in double brackets. Each table
# with the same double bracketed name will be an element in the array. The
# tables are inserted in the order encountered.
[[products]]
name = "Hammer"
sku = 738594937
[[products]]
[[products]]
name = "Nail"
sku = 284758393
color = "gray"
# You can create nested arrays of tables as well.
[[fruit]]
name = "apple"
[fruit.physical]
color = "red"
shape = "round"
[[fruit.variety]]
name = "red delicious"
[[fruit.variety]]
name = "granny smith"
[[fruit]]
name = "banana"
[[fruit.variety]]
name = "plantain"
toml-0.9.3/setup.py 0000664 0000000 0000000 00000001016 13162074272 0014212 0 ustar 00root root 0000000 0000000 try:
from setuptools import setup
except ImportError:
from distutils.core import setup
import toml
with open("README.rst") as readme_file:
readme_string = readme_file.read()
setup(
name="toml",
version=toml.__version__,
description="Python Library for Tom's Obvious, Minimal Language",
author="Uiri Noyb",
author_email="uiri@xqz.ca",
url="https://github.com/uiri/toml",
py_modules=['toml'],
license="License :: OSI Approved :: MIT License",
long_description=readme_string,
)
toml-0.9.3/test.toml 0000664 0000000 0000000 00000002221 13162074272 0014353 0 ustar 00root root 0000000 0000000 # This is a TOML document. Boom.
a = "C:\\Users\\n"
title = "TOML Example"
the-void = [[[[[]]]]]
mixed = [[1, +2], ["a", "b"], [1.0, 2.0]]
avogadro = 6.23e23
[owner]
name = "Tom \\ / Preston-Werner"
organization = "GitHub"
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002 ]
connection_max = 5000
enabled = true
test = [["a"], ["b"], ["c"]]
[servers]
# You can indent as you please. Tabs or spaces. TOML don't care.
[servers.alpha]
ip = "10.0.0.1"
dc = "eqdc10"
[servers.beta]
ip = "\u000a\u1000\u1000\u0002"
dc = "eqdc10"
[clients]
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
# Line breaks are OK when inside arrays
hosts = [
"alpha",
"omega"
]
[[fruit]]
name = "apple"
[fruit.physical]
color = "red"
shape = "round"
[[fruit.variety]]
name = "red delicious"
#[fruit.variety]
# name = "granny smith"
[[fruit]]
name = "banana"
[[fruit.variety]]
name = "plantain"
[[fruit.variety]]
name = "whatever"
thing=34
"l.mao" = 42
toml-0.9.3/tests/ 0000775 0000000 0000000 00000000000 13162074272 0013644 5 ustar 00root root 0000000 0000000 toml-0.9.3/tests/api_test.py 0000664 0000000 0000000 00000002462 13162074272 0016032 0 ustar 00root root 0000000 0000000 if __name__ == "__main__" and __package__ is None:
from os import sys, path
sys.path.insert(0, path.dirname(path.dirname(__file__)))
from toml import TomlDecodeError # noqa: E402
import toml # noqa: E402
import warnings # noqa: E402
class TestDict(dict):
pass
try:
FNFError = FileNotFoundError
except NameError:
FNFError = IOError
TEST_STR = """
[a]
b = 1
c = 2
"""
INVALID_TOML = """
strings-and-ints = ["hi", 42]
"""
TEST_DICT = {"a": {"b": 1, "c": 2}}
o = toml.loads(TEST_STR)
assert(o == toml.loads(toml.dumps(o)))
assert(isinstance(toml.loads(TEST_STR, _dict=TestDict), TestDict))
try:
toml.loads(2)
# Expected TypeError
assert(False)
except TypeError:
pass
try:
toml.loads(INVALID_TOML)
# Expected TomlDecodeError
assert(False)
except TomlDecodeError:
pass
try:
toml.load(2)
# Expected TypeError
assert(False)
except TypeError:
pass
try:
toml.load([])
# Expected FileNotFoundError or IOError (according to Python version)
assert(False)
except FNFError:
pass
toml.load(["test.toml"])
with warnings.catch_warnings(record=True) as w:
toml.load(["test.toml", "nonexist.toml"])
# Expect 1 warning for the non existent toml file
assert(len(w) == 1)
s = toml.dumps(TEST_DICT)
assert(s == toml.dumps(toml.loads(s)))
toml-0.9.3/tests/decoding_test.py 0000775 0000000 0000000 00000002761 13162074272 0017042 0 ustar 00root root 0000000 0000000 """Decodes toml and outputs it as tagged JSON"""
import datetime
import json
import sys
import toml
if sys.version_info < (3,):
_range = xrange # noqa: F821
iteritems = dict.iteritems
else:
unicode = str
_range = range
basestring = str
unichr = chr
iteritems = dict.items
long = int
def tag(value):
if isinstance(value, dict):
d = {}
for k, v in iteritems(value):
d[k] = tag(v)
return d
elif isinstance(value, list):
a = []
for v in value:
a.append(tag(v))
try:
a[0]["value"]
except KeyError:
return a
except IndexError:
pass
return {'type': 'array', 'value': a}
elif isinstance(value, basestring):
return {'type': 'string', 'value': value}
elif isinstance(value, bool):
return {'type': 'bool', 'value': str(value).lower()}
elif isinstance(value, int):
return {'type': 'integer', 'value': str(value)}
elif isinstance(value, long):
return {'type': 'integer', 'value': str(value)}
elif isinstance(value, float):
return {'type': 'float', 'value': repr(value)}
elif isinstance(value, datetime.datetime):
sdate = value.strftime('%Y-%m-%dT%H:%M:%SZ')
return {'type': 'datetime', 'value': sdate}
assert False, 'Unknown type: %s' % type(value)
if __name__ == '__main__':
tdata = toml.loads(sys.stdin.read())
tagged = tag(tdata)
print(json.dumps(tagged))
toml-0.9.3/tests/decoding_test.sh 0000775 0000000 0000000 00000000051 13162074272 0017012 0 ustar 00root root 0000000 0000000 #!/bin/sh
python tests/decoding_test.py
toml-0.9.3/tests/decoding_test2.sh 0000775 0000000 0000000 00000000102 13162074272 0017071 0 ustar 00root root 0000000 0000000 #!/bin/sh
export PYTHONPATH=`pwd`
python2 tests/decoding_test.py
toml-0.9.3/tests/decoding_test3.sh 0000775 0000000 0000000 00000000102 13162074272 0017072 0 ustar 00root root 0000000 0000000 #!/bin/sh
export PYTHONPATH=`pwd`
python3 tests/decoding_test.py
toml-0.9.3/tests/flake8.sh 0000775 0000000 0000000 00000000114 13162074272 0015351 0 ustar 00root root 0000000 0000000 #!/bin/sh
if [ -z `python --version 2>&1 | grep 2.6` ]; then
flake8
fi
toml-0.9.3/toml.py 0000664 0000000 0000000 00000074276 13162074272 0014047 0 ustar 00root root 0000000 0000000 """Python module which parses and emits TOML.
Released under the MIT license.
"""
import re
import io
import datetime
from os import linesep
__version__ = "0.9.3"
__spec__ = "0.4.0"
class TomlDecodeError(Exception):
"""Base toml Exception / Error."""
pass
class TomlTz(datetime.tzinfo):
def __init__(self, toml_offset):
if toml_offset == "Z":
self._raw_offset = "+00:00"
else:
self._raw_offset = toml_offset
self._sign = -1 if self._raw_offset[0] == '-' else 1
self._hours = int(self._raw_offset[1:3])
self._minutes = int(self._raw_offset[4:6])
def tzname(self, dt):
return "UTC" + self._raw_offset
def utcoffset(self, dt):
return self._sign * datetime.timedelta(hours=self._hours,
minutes=self._minutes)
def dst(self, dt):
return datetime.timedelta(0)
class InlineTableDict(object):
"""Sentinel subclass of dict for inline tables."""
def _get_empty_inline_table(_dict):
class DynamicInlineTableDict(_dict, InlineTableDict):
"""Concrete sentinel subclass for inline tables.
It is a subclass of _dict which is passed in dynamically at load time
It is also a subclass of InlineTableDict
"""
return DynamicInlineTableDict()
try:
_range = xrange
except NameError:
unicode = str
_range = range
basestring = str
unichr = chr
try:
FNFError = FileNotFoundError
except NameError:
FNFError = IOError
def load(f, _dict=dict):
"""Parses named file or files as toml and returns a dictionary
Args:
f: Path to the file to open, array of files to read into single dict
or a file descriptor
_dict: (optional) Specifies the class of the returned toml dictionary
Returns:
Parsed toml file represented as a dictionary
Raises:
TypeError -- When f is invalid type
TomlDecodeError: Error while decoding toml
IOError / FileNotFoundError -- When an array with no valid (existing)
(Python 2 / Python 3) file paths is passed
"""
if isinstance(f, basestring):
with io.open(f, encoding='utf-8') as ffile:
return loads(ffile.read(), _dict)
elif isinstance(f, list):
from os import path as op
from warnings import warn
if not [path for path in f if op.exists(path)]:
error_msg = "Load expects a list to contain filenames only."
error_msg += linesep
error_msg += ("The list needs to contain the path of at least one "
"existing file.")
raise FNFError(error_msg)
d = _dict()
for l in f:
if op.exists(l):
d.update(load(l))
else:
warn("Non-existent filename in list with at least one valid "
"filename")
return d
else:
try:
return loads(f.read(), _dict)
except AttributeError:
raise TypeError("You can only load a file descriptor, filename or "
"list")
_groupname_re = re.compile(r'^[A-Za-z0-9_-]+$')
def loads(s, _dict=dict):
"""Parses string as toml
Args:
s: String to be parsed
_dict: (optional) Specifies the class of the returned toml dictionary
Returns:
Parsed toml file represented as a dictionary
Raises:
TypeError: When a non-string is passed
TomlDecodeError: Error while decoding toml
"""
implicitgroups = []
retval = _dict()
currentlevel = retval
if not isinstance(s, basestring):
raise TypeError("Expecting something like a string")
if not isinstance(s, unicode):
s = s.decode('utf8')
sl = list(s)
openarr = 0
openstring = False
openstrchar = ""
multilinestr = False
arrayoftables = False
beginline = True
keygroup = False
keyname = 0
for i, item in enumerate(sl):
if item == '\r' and sl[i + 1] == '\n':
sl[i] = ' '
continue
if keyname:
if item == '\n':
raise TomlDecodeError("Key name found without value."
" Reached end of line.")
if openstring:
if item == openstrchar:
keyname = 2
openstring = False
openstrchar = ""
continue
elif keyname == 1:
if item.isspace():
keyname = 2
continue
elif item.isalnum() or item == '_' or item == '-':
continue
elif keyname == 2 and item.isspace():
continue
if item == '=':
keyname = 0
else:
raise TomlDecodeError("Found invalid character in key name: '" +
item + "'. Try quoting the key name.")
if item == "'" and openstrchar != '"':
k = 1
try:
while sl[i - k] == "'":
k += 1
if k == 3:
break
except IndexError:
pass
if k == 3:
multilinestr = not multilinestr
openstring = multilinestr
else:
openstring = not openstring
if openstring:
openstrchar = "'"
else:
openstrchar = ""
if item == '"' and openstrchar != "'":
oddbackslash = False
k = 1
tripquote = False
try:
while sl[i - k] == '"':
k += 1
if k == 3:
tripquote = True
break
if k == 1 or (k == 3 and tripquote):
while sl[i - k] == '\\':
oddbackslash = not oddbackslash
k += 1
except IndexError:
pass
if not oddbackslash:
if tripquote:
multilinestr = not multilinestr
openstring = multilinestr
else:
openstring = not openstring
if openstring:
openstrchar = '"'
else:
openstrchar = ""
if item == '#' and (not openstring and not keygroup and
not arrayoftables):
j = i
try:
while sl[j] != '\n':
sl[j] = ' '
j += 1
except IndexError:
break
if item == '[' and (not openstring and not keygroup and
not arrayoftables):
if beginline:
if sl[i + 1] == '[':
arrayoftables = True
else:
keygroup = True
else:
openarr += 1
if item == ']' and not openstring:
if keygroup:
keygroup = False
elif arrayoftables:
if sl[i - 1] == ']':
arrayoftables = False
else:
openarr -= 1
if item == '\n':
if openstring or multilinestr:
if not multilinestr:
raise TomlDecodeError("Unbalanced quotes")
if ((sl[i - 1] == "'" or sl[i - 1] == '"') and (
sl[i - 2] == sl[i - 1])):
sl[i] = sl[i - 1]
if sl[i - 3] == sl[i - 1]:
sl[i - 3] = ' '
elif openarr:
sl[i] = ' '
else:
beginline = True
elif beginline and sl[i] != ' ' and sl[i] != '\t':
beginline = False
if not keygroup and not arrayoftables:
if sl[i] == '=':
raise TomlDecodeError("Found empty keyname. ")
keyname = 1
s = ''.join(sl)
s = s.split('\n')
multikey = None
multilinestr = ""
multibackslash = False
for line in s:
if not multilinestr or multibackslash or '\n' not in multilinestr:
line = line.strip()
if line == "" and (not multikey or multibackslash):
continue
if multikey:
if multibackslash:
multilinestr += line
else:
multilinestr += line
multibackslash = False
if len(line) > 2 and (line[-1] == multilinestr[0] and
line[-2] == multilinestr[0] and
line[-3] == multilinestr[0]):
value, vtype = _load_value(multilinestr, _dict)
currentlevel[multikey] = value
multikey = None
multilinestr = ""
else:
k = len(multilinestr) - 1
while k > -1 and multilinestr[k] == '\\':
multibackslash = not multibackslash
k -= 1
if multibackslash:
multilinestr = multilinestr[:-1]
else:
multilinestr += "\n"
continue
if line[0] == '[':
arrayoftables = False
if line[1] == '[':
arrayoftables = True
line = line[2:].split(']]', 1)
else:
line = line[1:].split(']', 1)
if line[1].strip() != "":
raise TomlDecodeError("Key group not on a line by itself.")
groups = line[0].split('.')
i = 0
while i < len(groups):
groups[i] = groups[i].strip()
if groups[i][0] == '"' or groups[i][0] == "'":
groupstr = groups[i]
j = i + 1
while not groupstr[0] == groupstr[-1]:
j += 1
groupstr = '.'.join(groups[i:j])
groups[i] = groupstr[1:-1]
groups[i + 1:j] = []
else:
if not _groupname_re.match(groups[i]):
raise TomlDecodeError("Invalid group name '" +
groups[i] + "'. Try quoting it.")
i += 1
currentlevel = retval
for i in _range(len(groups)):
group = groups[i]
if group == "":
raise TomlDecodeError("Can't have a keygroup with an empty "
"name")
try:
currentlevel[group]
if i == len(groups) - 1:
if group in implicitgroups:
implicitgroups.remove(group)
if arrayoftables:
raise TomlDecodeError("An implicitly defined "
"table can't be an array")
elif arrayoftables:
currentlevel[group].append(_dict())
else:
raise TomlDecodeError("What? " + group +
" already exists?" +
str(currentlevel))
except TypeError:
currentlevel = currentlevel[-1]
try:
currentlevel[group]
except KeyError:
currentlevel[group] = _dict()
if i == len(groups) - 1 and arrayoftables:
currentlevel[group] = [_dict()]
except KeyError:
if i != len(groups) - 1:
implicitgroups.append(group)
currentlevel[group] = _dict()
if i == len(groups) - 1 and arrayoftables:
currentlevel[group] = [_dict()]
currentlevel = currentlevel[group]
if arrayoftables:
try:
currentlevel = currentlevel[-1]
except KeyError:
pass
elif line[0] == "{":
if line[-1] != "}":
raise TomlDecodeError("Line breaks are not allowed in inline"
"objects")
_load_inline_object(line, currentlevel, _dict, multikey,
multibackslash)
elif "=" in line:
ret = _load_line(line, currentlevel, _dict, multikey,
multibackslash)
if ret is not None:
multikey, multilinestr, multibackslash = ret
return retval
def _load_inline_object(line, currentlevel, _dict, multikey=False,
multibackslash=False):
candidate_groups = line[1:-1].split(",")
groups = []
if len(candidate_groups) == 1 and not candidate_groups[0].strip():
candidate_groups.pop()
while len(candidate_groups) > 0:
candidate_group = candidate_groups.pop(0)
try:
_, value = candidate_group.split('=', 1)
except ValueError:
raise TomlDecodeError("Invalid inline table encountered")
value = value.strip()
if ((value[0] == value[-1] and value[0] in ('"', "'")) or (
value[0] in '-0123456789' or
value in ('true', 'false') or
(value[0] == "[" and value[-1] == "]"))):
groups.append(candidate_group)
else:
candidate_groups[0] = candidate_group + "," + candidate_groups[0]
for group in groups:
status = _load_line(group, currentlevel, _dict, multikey,
multibackslash)
if status is not None:
break
# Matches a TOML number, which allows underscores for readability
_number_with_underscores = re.compile('([0-9])(_([0-9]))*')
def _strictly_valid_num(n):
n = n.strip()
if not n:
return False
if n[0] == '_':
return False
if n[-1] == '_':
return False
if "_." in n or "._" in n:
return False
if len(n) == 1:
return True
if n[0] == '0' and n[1] != '.':
return False
if n[0] == '+' or n[0] == '-':
n = n[1:]
if n[0] == '0' and n[1] != '.':
return False
if '__' in n:
return False
return True
def _load_line(line, currentlevel, _dict, multikey, multibackslash):
i = 1
pair = line.split('=', i)
strictly_valid = _strictly_valid_num(pair[-1])
if _number_with_underscores.match(pair[-1]):
pair[-1] = pair[-1].replace('_', '')
while len(pair[-1]) and (pair[-1][0] != ' ' and pair[-1][0] != '\t' and
pair[-1][0] != "'" and pair[-1][0] != '"' and
pair[-1][0] != '[' and pair[-1][0] != '{' and
pair[-1] != 'true' and pair[-1] != 'false'):
try:
float(pair[-1])
break
except ValueError:
pass
if _load_date(pair[-1]) is not None:
break
i += 1
prev_val = pair[-1]
pair = line.split('=', i)
if prev_val == pair[-1]:
raise TomlDecodeError("Invalid date or number")
if strictly_valid:
strictly_valid = _strictly_valid_num(pair[-1])
pair = ['='.join(pair[:-1]).strip(), pair[-1].strip()]
if (pair[0][0] == '"' or pair[0][0] == "'") and \
(pair[0][-1] == '"' or pair[0][-1] == "'"):
pair[0] = pair[0][1:-1]
if len(pair[1]) > 2 and ((pair[1][0] == '"' or pair[1][0] == "'") and
pair[1][1] == pair[1][0] and
pair[1][2] == pair[1][0] and
not (len(pair[1]) > 5 and
pair[1][-1] == pair[1][0] and
pair[1][-2] == pair[1][0] and
pair[1][-3] == pair[1][0])):
k = len(pair[1]) - 1
while k > -1 and pair[1][k] == '\\':
multibackslash = not multibackslash
k -= 1
if multibackslash:
multilinestr = pair[1][:-1]
else:
multilinestr = pair[1] + "\n"
multikey = pair[0]
else:
value, vtype = _load_value(pair[1], _dict, strictly_valid)
try:
currentlevel[pair[0]]
raise TomlDecodeError("Duplicate keys!")
except KeyError:
if multikey:
return multikey, multilinestr, multibackslash
else:
currentlevel[pair[0]] = value
except:
raise TomlDecodeError("Duplicate keys!")
def _load_date(val):
microsecond = 0
tz = None
try:
if len(val) > 19:
if val[19] == '.':
microsecond = int(val[20:26])
if len(val) > 26:
tz = TomlTz(val[26:32])
else:
tz = TomlTz(val[19:25])
except ValueError:
tz = None
try:
d = datetime.datetime(
int(val[:4]), int(val[5:7]),
int(val[8:10]), int(val[11:13]),
int(val[14:16]), int(val[17:19]), microsecond, tz)
except ValueError:
return None
return d
def _load_unicode_escapes(v, hexbytes, prefix):
hexchars = ['0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
skip = False
i = len(v) - 1
while i > -1 and v[i] == '\\':
skip = not skip
i -= 1
for hx in hexbytes:
if skip:
skip = False
i = len(hx) - 1
while i > -1 and hx[i] == '\\':
skip = not skip
i -= 1
v += prefix
v += hx
continue
hxb = ""
i = 0
hxblen = 4
if prefix == "\\U":
hxblen = 8
while i < hxblen:
try:
if not hx[i].lower() in hexchars:
raise IndexError("This is a hack")
except IndexError:
raise TomlDecodeError("Invalid escape sequence")
hxb += hx[i].lower()
i += 1
v += unichr(int(hxb, 16))
v += unicode(hx[len(hxb):])
return v
# Unescape TOML string values.
# content after the \
_escapes = ['0', 'b', 'f', 'n', 'r', 't', '"']
# What it should be replaced by
_escapedchars = ['\0', '\b', '\f', '\n', '\r', '\t', '\"']
# Used for substitution
_escape_to_escapedchars = dict(zip(_escapes, _escapedchars))
def _unescape(v):
"""Unescape characters in a TOML string."""
i = 0
backslash = False
while i < len(v):
if backslash:
backslash = False
if v[i] in _escapes:
v = v[:i - 1] + _escape_to_escapedchars[v[i]] + v[i + 1:]
elif v[i] == '\\':
v = v[:i - 1] + v[i:]
elif v[i] == 'u' or v[i] == 'U':
i += 1
else:
raise TomlDecodeError("Reserved escape sequence used")
continue
elif v[i] == '\\':
backslash = True
i += 1
return v
def _load_value(v, _dict, strictly_valid=True):
if not v:
raise TomlDecodeError("Empty value is invalid")
if v == 'true':
return (True, "bool")
elif v == 'false':
return (False, "bool")
elif v[0] == '"':
testv = v[1:].split('"')
if testv[0] == '' and testv[1] == '':
testv = testv[2:-2]
closed = False
for tv in testv:
if tv == '':
closed = True
else:
oddbackslash = False
try:
i = -1
j = tv[i]
while j == '\\':
oddbackslash = not oddbackslash
i -= 1
j = tv[i]
except IndexError:
pass
if not oddbackslash:
if closed:
raise TomlDecodeError("Stuff after closed string. WTF?")
else:
closed = True
escapeseqs = v.split('\\')[1:]
backslash = False
for i in escapeseqs:
if i == '':
backslash = not backslash
else:
if i[0] not in _escapes and (i[0] != 'u' and i[0] != 'U' and
not backslash):
raise TomlDecodeError("Reserved escape sequence used")
if backslash:
backslash = False
for prefix in ["\\u", "\\U"]:
if prefix in v:
hexbytes = v.split(prefix)
v = _load_unicode_escapes(hexbytes[0], hexbytes[1:], prefix)
v = _unescape(v)
if v[1] == '"' and (len(v) < 3 or v[1] == v[2]):
v = v[2:-2]
return (v[1:-1], "str")
elif v[0] == "'":
if v[1] == "'" and (len(v) < 3 or v[1] == v[2]):
v = v[2:-2]
return (v[1:-1], "str")
elif v[0] == '[':
return (_load_array(v, _dict), "array")
elif v[0] == '{':
inline_object = _get_empty_inline_table(_dict)
_load_inline_object(v, inline_object, _dict)
return (inline_object, "inline_object")
else:
parsed_date = _load_date(v)
if parsed_date is not None:
return (parsed_date, "date")
if not strictly_valid:
raise TomlDecodeError("Weirdness with leading zeroes or underscores"
" in your number.")
itype = "int"
neg = False
if v[0] == '-':
neg = True
v = v[1:]
elif v[0] == '+':
v = v[1:]
v = v.replace('_', '')
if '.' in v or 'e' in v or 'E' in v:
if '.' in v and v.split('.', 1)[1] == '':
raise TomlDecodeError("This float is missing digits after "
"the point")
if v[0] not in '0123456789':
raise TomlDecodeError("This float doesn't have a leading digit")
v = float(v)
itype = "float"
else:
v = int(v)
if neg:
return (0 - v, itype)
return (v, itype)
def _load_array(a, _dict):
atype = None
retval = []
a = a.strip()
if '[' not in a[1:-1] or "" != a[1:-1].split('[')[0].strip():
strarray = False
tmpa = a[1:-1].strip()
if tmpa != '' and (tmpa[0] == '"' or tmpa[0] == "'"):
strarray = True
if not a[1:-1].strip().startswith('{'):
a = a[1:-1].split(',')
else:
# a is an inline object, we must find the matching parenthesis
# to define groups
new_a = []
start_group_index = 1
end_group_index = 2
in_str = False
while end_group_index < len(a[1:]):
if a[end_group_index] == '"' or a[end_group_index] == "'":
in_str = not in_str
if in_str or a[end_group_index] != '}':
end_group_index += 1
continue
# Increase end_group_index by 1 to get the closing bracket
end_group_index += 1
new_a.append(a[start_group_index:end_group_index])
# The next start index is at least after the closing bracket, a
# closing bracket can be followed by a comma since we are in
# an array.
start_group_index = end_group_index + 1
while (start_group_index < len(a[1:]) and
a[start_group_index] != '{'):
start_group_index += 1
end_group_index = start_group_index + 1
a = new_a
b = 0
if strarray:
while b < len(a) - 1:
ab = a[b].strip()
while ab[-1] != ab[0] or (ab[0] == ab[1] == ab[2] and
ab[-2] != ab[0] and ab[-3] != ab[0]):
a[b] = a[b] + ',' + a[b + 1]
ab = a[b].strip()
if b < len(a) - 2:
a = a[:b + 1] + a[b + 2:]
else:
a = a[:b + 1]
b += 1
else:
al = list(a[1:-1])
a = []
openarr = 0
j = 0
for i in _range(len(al)):
if al[i] == '[':
openarr += 1
elif al[i] == ']':
openarr -= 1
elif al[i] == ',' and not openarr:
a.append(''.join(al[j:i]))
j = i + 1
a.append(''.join(al[j:]))
for i in _range(len(a)):
a[i] = a[i].strip()
if a[i] != '':
nval, ntype = _load_value(a[i], _dict)
if atype:
if ntype != atype:
raise TomlDecodeError("Not a homogeneous array")
else:
atype = ntype
retval.append(nval)
return retval
def dump(o, f):
"""Writes out dict as toml to a file
Args:
o: Object to dump into toml
f: File descriptor where the toml should be stored
Returns:
String containing the toml corresponding to dictionary
Raises:
TypeError: When anything other than file descriptor is passed
"""
if not f.write:
raise TypeError("You can only dump an object to a file descriptor")
d = dumps(o)
f.write(d)
return d
def dumps(o, preserve=False):
"""Stringifies input dict as toml
Args:
o: Object to dump into toml
preserve: Boolean parameter. If true, preserve inline tables.
Returns:
String containing the toml corresponding to dict
"""
retval = ""
addtoretval, sections = _dump_sections(o, "")
retval += addtoretval
while sections != {}:
newsections = {}
for section in sections:
addtoretval, addtosections = _dump_sections(sections[section],
section, preserve)
if addtoretval or (not addtoretval and not addtosections):
if retval and retval[-2:] != "\n\n":
retval += "\n"
retval += "[" + section + "]\n"
if addtoretval:
retval += addtoretval
for s in addtosections:
newsections[section + "." + s] = addtosections[s]
sections = newsections
return retval
def _dump_sections(o, sup, preserve=False):
retstr = ""
if sup != "" and sup[-1] != ".":
sup += '.'
retdict = o.__class__()
arraystr = ""
for section in o:
section = str(section)
qsection = section
if not re.match(r'^[A-Za-z0-9_-]+$', section):
if '"' in section:
qsection = "'" + section + "'"
else:
qsection = '"' + section + '"'
if not isinstance(o[section], dict):
arrayoftables = False
if isinstance(o[section], list):
for a in o[section]:
if isinstance(a, dict):
arrayoftables = True
if arrayoftables:
for a in o[section]:
arraytabstr = "\n"
arraystr += "[[" + sup + qsection + "]]\n"
s, d = _dump_sections(a, sup + qsection)
if s:
if s[0] == "[":
arraytabstr += s
else:
arraystr += s
while d != {}:
newd = {}
for dsec in d:
s1, d1 = _dump_sections(d[dsec], sup + qsection +
"." + dsec)
if s1:
arraytabstr += ("[" + sup + qsection + "." +
dsec + "]\n")
arraytabstr += s1
for s1 in d1:
newd[dsec + "." + s1] = d1[s1]
d = newd
arraystr += arraytabstr
else:
if o[section] is not None:
retstr += (qsection + " = " +
str(_dump_value(o[section])) + '\n')
elif preserve and isinstance(o[section], InlineTableDict):
retstr += (section + " = " + _dump_inline_table(o[section]))
else:
retdict[qsection] = o[section]
retstr += arraystr
return (retstr, retdict)
def _dump_inline_table(section):
"""Preserve inline table in its compact syntax instead of expanding
into subsection.
https://github.com/toml-lang/toml#user-content-inline-table
"""
retval = ""
if isinstance(section, dict):
val_list = []
for k, v in section.items():
val = _dump_inline_table(v)
val_list.append(k + " = " + val)
retval += "{ " + ", ".join(val_list) + " }\n"
return retval
else:
return str(_dump_value(section))
def _dump_value(v):
dump_funcs = {
str: lambda: _dump_str(v),
unicode: lambda: _dump_str(v),
list: lambda: _dump_list(v),
bool: lambda: str(v).lower(),
float: lambda: _dump_float(v),
datetime.datetime: lambda: v.isoformat(),
}
# Lookup function corresponding to v's type
dump_fn = dump_funcs.get(type(v))
# Evaluate function (if it exists) else return v
return dump_fn() if dump_fn is not None else v
def _dump_str(v):
v = "%r" % v
if v[0] == 'u':
v = v[1:]
singlequote = v.startswith("'")
v = v[1:-1]
if singlequote:
v = v.replace("\\'", "'")
v = v.replace('"', '\\"')
v = v.replace("\\x", "\\u00")
return str('"' + v + '"')
def _dump_list(v):
t = []
retval = "["
for u in v:
t.append(_dump_value(u))
while t != []:
s = []
for u in t:
if isinstance(u, list):
for r in u:
s.append(r)
else:
retval += " " + str(u) + ","
t = s
retval += "]"
return retval
def _dump_float(v):
return "{0:.16g}".format(v).replace("e+0", "e+").replace("e-0", "e-")
toml-0.9.3/toml.pyi 0000664 0000000 0000000 00000001063 13162074272 0014200 0 ustar 00root root 0000000 0000000 from typing import Any, IO, Mapping, MutableMapping, Optional, Type, Union
import datetime
class TomlDecodeError(Exception): ...
class TomlTz(datetime.tzinfo):
def __init__(self, toml_offset: str) -> None: ...
def load(f: Union[str, list, IO[str]],
_dict: Type[MutableMapping[str, Any]] = ...) \
-> MutableMapping[str, Any]: ...
def loads(s: str, _dict: Type[MutableMapping[str, Any]] = ...) \
-> MutableMapping[str, Any]: ...
def dump(o: Mapping[str, Any], f: IO[str]) -> str: ...
def dumps(o: Mapping[str, Any]) -> str: ...