pax_global_header 0000666 0000000 0000000 00000000064 13355272442 0014521 g ustar 00root root 0000000 0000000 52 comment=4935f616ef78c35a968b2473e806d7049eba9af1
toml-0.10.0/ 0000775 0000000 0000000 00000000000 13355272442 0012552 5 ustar 00root root 0000000 0000000 toml-0.10.0/.coveragearc 0000664 0000000 0000000 00000000066 13355272442 0015036 0 ustar 00root root 0000000 0000000 [run]
branch = True
source = toml
omit =
tests/*
toml-0.10.0/.flake8 0000664 0000000 0000000 00000000522 13355272442 0013724 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__,
.tox,
max-line-length = 80
ignore =
# Output config:
show-source = True
statistics = True
tee = True
output-file = .flake8.log
toml-0.10.0/.gitignore 0000664 0000000 0000000 00000002411 13355272442 0014540 0 ustar 00root root 0000000 0000000 # Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
.static_storage/
.media/
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
# Emacs
*~
\#*\#
/.emacs.desktop
/.emacs.desktop.lock
*.elc
auto-save-list
tramp
.\#*
toml-0.10.0/.travis.yml 0000664 0000000 0000000 00000001074 13355272442 0014665 0 ustar 00root root 0000000 0000000 language: python
cache: pip
python:
- "2.7"
- "3.4"
- "3.5"
- "3.6"
- "pypy"
matrix:
include:
- python: "3.7"
dist: xenial
sudo: true
notifications:
email: false
addons:
apt:
packages:
- git
- golang
before_install:
- export GOPATH=~/go
- go get github.com/BurntSushi/toml-test
- git clone https://github.com/BurntSushi/toml-test
install:
- pip install tox-travis
- python setup.py install
- chmod +x ./tests/*.sh
script:
- ~/go/bin/toml-test ./tests/decoding_test.sh
- tox
- tox -e check
after_success:
- tox -e codecov
toml-0.10.0/CONTRIBUTING 0000664 0000000 0000000 00000002645 13355272442 0014413 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
=======
### Unit tests
Unit tests can be run using [tox](https://tox.readthedocs.io/en/latest/).
Simply `pip install tox` and you are ready to go. Tox creates required
virual eniroments and installs necessary packages.
tox
This is not very practical for day to day use. To easily run tests
in the current Python simply run.
tox -e py
We are using [pytest](https://docs.pytest.org/en/latest/) testing framework.
You can pass parameters to it like this:
tox -e py -- -vsx
### Decoding tests
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.10.0/LICENSE 0000664 0000000 0000000 00000002304 13355272442 0013556 0 ustar 00root root 0000000 0000000 The MIT License
Copyright 2013-2018 William Pearson
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.10.0/MANIFEST.in 0000664 0000000 0000000 00000000146 13355272442 0014311 0 ustar 00root root 0000000 0000000 include LICENSE
include README.rst
include toml.pyi
include tox.ini
recursive-include tests *.py *.sh
toml-0.10.0/README.rst 0000664 0000000 0000000 00000010431 13355272442 0014240 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
.. image:: https://img.shields.io/pypi/pyversions/toml.svg
:target: https://pypi.org/project/toml/
A Python library for parsing and creating `TOML `_.
The module passes `the TOML test suite `_.
See also:
* `The TOML Standard `_
* `The currently supported 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.10.0/RELEASE.rst 0000664 0000000 0000000 00000001053 13355272442 0014363 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.10.0/examples/ 0000775 0000000 0000000 00000000000 13355272442 0014370 5 ustar 00root root 0000000 0000000 toml-0.10.0/examples/example-v0.4.0.out 0000664 0000000 0000000 00000004111 13355272442 0017374 0 ustar 00root root 0000000 0000000 {'table': {'key': 'value',
'subtable': {'key': 'another value'},
'inline': {'name': {'first': 'Tom', 'last': 'Preston-Werner'},
'point': {'x': 1, 'y': 2}}},
'x': {'y': {'z': {'w': {}}}},
'string': {'basic': {'basic': 'I\'m a string. "You can quote me". Name\tJosé\nLocation\tSF.'},
'multiline': {'key1': 'One\nTwo',
'key2': 'One\nTwo',
'key3': 'One\nTwo',
'continued': {'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.'}},
'literal': {'winpath': 'C:\\Users\\nodejs\\templates',
'winpath2': '\\\\ServerX\\admin$\\system32\\',
'quoted': 'Tom "Dubs" Preston-Werner',
'regex': '<\\i\\c*\\s*>',
'multiline': {'regex2': "I [dw]on't need \\d{2} apples",
'lines': 'The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'}}},
'integer': {'key1': 99,
'key2': 42,
'key3': 0,
'key4': -17,
'underscores': {'key1': 1000, 'key2': 5349221, 'key3': 12345}},
'float': {'fractional': {'key1': 1.0, 'key2': 3.1415, 'key3': -0.01},
'exponent': {'key1': 5e+22, 'key2': 1000000.0, 'key3': -0.02},
'both': {'key': 6.626e-34},
'underscores': {'key1': 9224617.445991227, 'key2': inf}},
'boolean': {'True': True, 'False': False},
'datetime': {'key1': datetime.datetime(1979, 5, 27, 7, 32, tzinfo=),
'key2': datetime.datetime(1979, 5, 27, 0, 32, tzinfo=),
'key3': datetime.datetime(1979, 5, 27, 0, 32, 0, 999999, tzinfo=)},
'array': {'key1': [1, 2, 3],
'key2': ['red', 'yellow', 'green'],
'key3': [[1, 2], [3, 4, 5]],
'key4': [[1, 2], ['a', 'b', 'c']],
'key5': [1, 2, 3],
'key6': [1, 2]},
'products': [{'name': 'Hammer', 'sku': 738594937},
{},
{'name': 'Nail', 'sku': 284758393, 'color': 'gray'}],
'fruit': [{'name': 'apple',
'physical': {'color': 'red', 'shape': 'round'},
'variety': [{'name': 'red delicious'}, {'name': 'granny smith'}]},
{'name': 'banana', 'variety': [{'name': 'plantain'}]}]}
toml-0.10.0/examples/example-v0.4.0.toml 0000664 0000000 0000000 00000012453 13355272442 0017550 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.If you include only the date portion of an
# RFC 3339 formatted date-time, it will represent that entire day without
# any relation to an offset or timezone.
[datetime]
key1 = 1979-05-27T07:32:00Z
key2 = 1979-05-27T00:32:00-07:00
key3 = 1979-05-27T00:32:00.999999-07:00
key4 = 1979-05-27
################################################################################
## 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.10.0/setup.cfg 0000664 0000000 0000000 00000000102 13355272442 0014364 0 ustar 00root root 0000000 0000000 [bdist_wheel]
universal = True
[metadata]
license_file = LICENSE
toml-0.10.0/setup.py 0000664 0000000 0000000 00000002463 13355272442 0014271 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="William Pearson",
author_email="uiri@xqz.ca",
url="https://github.com/uiri/toml",
packages=['toml'],
license="MIT",
long_description=readme_string,
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
]
)
toml-0.10.0/test.toml 0000664 0000000 0000000 00000002221 13355272442 0014423 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.10.0/tests/ 0000775 0000000 0000000 00000000000 13355272442 0013714 5 ustar 00root root 0000000 0000000 toml-0.10.0/tests/__init__.py 0000664 0000000 0000000 00000000000 13355272442 0016013 0 ustar 00root root 0000000 0000000 toml-0.10.0/tests/decoding_test.py 0000775 0000000 0000000 00000003312 13355272442 0017103 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):
return {'type': 'datetime', 'value': value.isoformat()
.replace('+00:00', 'Z')}
elif isinstance(value, datetime.date):
return {'type': 'date', 'value': value.isoformat()}
elif isinstance(value, datetime.time):
return {'type': 'time', 'value': value.strftime('%H:%M:%S.%f')}
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.10.0/tests/decoding_test.sh 0000775 0000000 0000000 00000000051 13355272442 0017062 0 ustar 00root root 0000000 0000000 #!/bin/sh
python tests/decoding_test.py
toml-0.10.0/tests/decoding_test2.sh 0000775 0000000 0000000 00000000102 13355272442 0017141 0 ustar 00root root 0000000 0000000 #!/bin/sh
export PYTHONPATH=`pwd`
python2 tests/decoding_test.py
toml-0.10.0/tests/decoding_test3.sh 0000775 0000000 0000000 00000000102 13355272442 0017142 0 ustar 00root root 0000000 0000000 #!/bin/sh
export PYTHONPATH=`pwd`
python3 tests/decoding_test.py
toml-0.10.0/tests/test_api.py 0000664 0000000 0000000 00000010013 13355272442 0016071 0 ustar 00root root 0000000 0000000 import toml
import copy
import pytest
import os
import sys
from toml.decoder import InlineTableDict
TEST_STR = """
[a]\r
b = 1\r
c = 2
"""
TEST_DICT = {"a": {"b": 1, "c": 2}}
def test_bug_148():
assert 'a = "\\u0064"\n' == toml.dumps({'a': '\\x64'})
assert 'a = "\\\\x64"\n' == toml.dumps({'a': '\\\\x64'})
assert 'a = "\\\\\\u0064"\n' == toml.dumps({'a': '\\\\\\x64'})
def test_bug_144():
if sys.version_info >= (3,):
return
bug_dict = {'username': '\xd7\xa9\xd7\x9c\xd7\x95\xd7\x9d'}
round_trip_bug_dict = toml.loads(toml.dumps(bug_dict))
unicoded_bug_dict = {'username': bug_dict['username'].decode('utf-8')}
assert round_trip_bug_dict == unicoded_bug_dict
assert bug_dict['username'] == (round_trip_bug_dict['username']
.encode('utf-8'))
def test_bug_196():
import datetime
d = datetime.datetime.now()
bug_dict = {'x': d}
round_trip_bug_dict = toml.loads(toml.dumps(bug_dict))
assert round_trip_bug_dict == bug_dict
assert round_trip_bug_dict['x'] == bug_dict['x']
def test_valid_tests():
valid_dir = "toml-test/tests/valid/"
for f in os.listdir(valid_dir):
if not f.endswith("toml"):
continue
toml.dumps(toml.load(open(os.path.join(valid_dir, f))))
def test__dict():
class TestDict(dict):
pass
assert isinstance(toml.loads(
TEST_STR, _dict=TestDict), TestDict)
def test_dict_decoder():
class TestDict(dict):
pass
test_dict_decoder = toml.TomlDecoder(TestDict)
assert isinstance(toml.loads(
TEST_STR, decoder=test_dict_decoder), TestDict)
def test_inline_dict():
class TestDict(dict, InlineTableDict):
pass
encoder = toml.TomlPreserveInlineDictEncoder()
t = copy.deepcopy(TEST_DICT)
t['d'] = TestDict()
t['d']['x'] = "abc"
o = toml.loads(toml.dumps(t, encoder=encoder))
assert o == toml.loads(toml.dumps(o, encoder=encoder))
def test_array_sep():
encoder = toml.TomlArraySeparatorEncoder(separator=",\t")
d = {"a": [1, 2, 3]}
o = toml.loads(toml.dumps(d, encoder=encoder))
assert o == toml.loads(toml.dumps(o, encoder=encoder))
def test_ordered():
from toml import ordered as toml_ordered
encoder = toml_ordered.TomlOrderedEncoder()
decoder = toml_ordered.TomlOrderedDecoder()
o = toml.loads(toml.dumps(TEST_DICT, encoder=encoder), decoder=decoder)
assert o == toml.loads(toml.dumps(TEST_DICT, encoder=encoder),
decoder=decoder)
def test_tuple():
d = {"a": (3, 4)}
o = toml.loads(toml.dumps(d))
assert o == toml.loads(toml.dumps(o))
def test_invalid_tests():
invalid_dir = "toml-test/tests/invalid/"
for f in os.listdir(invalid_dir):
if not f.endswith("toml"):
continue
with pytest.raises(toml.TomlDecodeError):
toml.load(open(os.path.join(invalid_dir, f)))
def test_exceptions():
with pytest.raises(TypeError):
toml.loads(2)
with pytest.raises(TypeError):
toml.load(2)
try:
FileNotFoundError
except NameError:
# py2
FileNotFoundError = IOError
with pytest.raises(FileNotFoundError):
toml.load([])
class FakeFile(object):
def __init__(self):
self.written = ""
def write(self, s):
self.written += s
return None
def read(self):
return self.written
def test_dump():
f = FakeFile()
g = FakeFile()
h = FakeFile()
toml.dump(TEST_DICT, f)
toml.dump(toml.load(f), g)
toml.dump(toml.load(g), h)
assert g.written == h.written
def test_paths():
toml.load("test.toml")
import sys
if (3, 4) <= sys.version_info:
import pathlib
p = pathlib.Path("test.toml")
toml.load(p)
def test_warnings():
# Expect 1 warning for the non existent toml file
with pytest.warns(UserWarning):
toml.load(["test.toml", "nonexist.toml"])
def test_commutativity():
o = toml.loads(toml.dumps(TEST_DICT))
assert o == toml.loads(toml.dumps(o))
toml-0.10.0/toml.pyi 0000664 0000000 0000000 00000001063 13355272442 0014250 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: ...
toml-0.10.0/toml/ 0000775 0000000 0000000 00000000000 13355272442 0013525 5 ustar 00root root 0000000 0000000 toml-0.10.0/toml/__init__.py 0000664 0000000 0000000 00000000767 13355272442 0015650 0 ustar 00root root 0000000 0000000 """Python module which parses and emits TOML.
Released under the MIT license.
"""
from toml import encoder
from toml import decoder
__version__ = "0.10.0"
_spec_ = "0.5.0"
load = decoder.load
loads = decoder.loads
TomlDecoder = decoder.TomlDecoder
TomlDecodeError = decoder.TomlDecodeError
dump = encoder.dump
dumps = encoder.dumps
TomlEncoder = encoder.TomlEncoder
TomlArraySeparatorEncoder = encoder.TomlArraySeparatorEncoder
TomlPreserveInlineDictEncoder = encoder.TomlPreserveInlineDictEncoder
toml-0.10.0/toml/decoder.py 0000664 0000000 0000000 00000104357 13355272442 0015516 0 ustar 00root root 0000000 0000000 import datetime
import io
from os import linesep
import re
import sys
from toml.tz import TomlTz
if sys.version_info < (3,):
_range = xrange # noqa: F821
else:
unicode = str
_range = range
basestring = str
unichr = chr
def _detect_pathlib_path(p):
if (3, 4) <= sys.version_info:
import pathlib
if isinstance(p, pathlib.PurePath):
return True
return False
def _ispath(p):
if isinstance(p, basestring):
return True
return _detect_pathlib_path(p)
def _getpath(p):
if (3, 6) <= sys.version_info:
import os
return os.fspath(p)
if _detect_pathlib_path(p):
return str(p)
return p
try:
FNFError = FileNotFoundError
except NameError:
FNFError = IOError
TIME_RE = re.compile("([0-9]{2}):([0-9]{2}):([0-9]{2})(\.([0-9]{3,6}))?")
class TomlDecodeError(ValueError):
"""Base toml Exception / Error."""
def __init__(self, msg, doc, pos):
lineno = doc.count('\n', 0, pos) + 1
colno = pos - doc.rfind('\n', 0, pos)
emsg = '{} (line {} column {} char {})'.format(msg, lineno, colno, pos)
ValueError.__init__(self, emsg)
self.msg = msg
self.doc = doc
self.pos = pos
self.lineno = lineno
self.colno = colno
# 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] not in ['.', 'o', 'b', 'x']:
return False
if n[0] == '+' or n[0] == '-':
n = n[1:]
if len(n) > 1 and n[0] == '0' and n[1] != '.':
return False
if '__' in n:
return False
return True
def load(f, _dict=dict, decoder=None):
"""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 _ispath(f):
with io.open(_getpath(f), encoding='utf-8') as ffile:
return loads(ffile.read(), _dict, decoder)
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)
if decoder is None:
decoder = TomlDecoder()
d = decoder.get_empty_table()
for l in f:
if op.exists(l):
d.update(load(l, _dict, decoder))
else:
warn("Non-existent filename in list with at least one valid "
"filename")
return d
else:
try:
return loads(f.read(), _dict, decoder)
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, decoder=None):
"""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 = []
if decoder is None:
decoder = TomlDecoder(_dict)
retval = decoder.get_empty_table()
currentlevel = retval
if not isinstance(s, basestring):
raise TypeError("Expecting something like a string")
if not isinstance(s, unicode):
s = s.decode('utf8')
original = s
sl = list(s)
openarr = 0
openstring = False
openstrchar = ""
multilinestr = False
arrayoftables = False
beginline = True
keygroup = False
dottedkey = 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.", original, i)
if openstring:
if item == openstrchar:
keyname = 2
openstring = False
openstrchar = ""
continue
elif keyname == 1:
if item.isspace():
keyname = 2
continue
elif item == '.':
dottedkey = True
continue
elif item.isalnum() or item == '_' or item == '-':
continue
elif (dottedkey and sl[i - 1] == '.' and
(item == '"' or item == "'")):
openstring = True
openstrchar = item
continue
elif keyname == 2:
if item.isspace():
if dottedkey:
nextitem = sl[i + 1]
if not nextitem.isspace() and nextitem != '.':
keyname = 1
continue
if item == '.':
dottedkey = True
nextitem = sl[i + 1]
if not nextitem.isspace() and nextitem != '.':
keyname = 1
continue
if item == '=':
keyname = 0
dottedkey = False
else:
raise TomlDecodeError("Found invalid character in key name: '" +
item + "'. Try quoting the key name.",
original, i)
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 len(sl) > i + 1 and 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", original, i)
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. ", original, i)
keyname = 1
s = ''.join(sl)
s = s.split('\n')
multikey = None
multilinestr = ""
multibackslash = False
pos = 0
for idx, line in enumerate(s):
if idx > 0:
pos += len(s[idx - 1]) + 1
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]):
try:
value, vtype = decoder.load_value(multilinestr)
except ValueError as err:
raise TomlDecodeError(str(err), original, pos)
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 len(line) == 1:
raise TomlDecodeError("Opening key group bracket on line by "
"itself.", original, pos)
if line[1] == '[':
arrayoftables = True
line = line[2:]
splitstr = ']]'
else:
line = line[1:]
splitstr = ']'
i = 1
quotesplits = decoder._get_split_on_quotes(line)
quoted = False
for quotesplit in quotesplits:
if not quoted and splitstr in quotesplit:
break
i += quotesplit.count(splitstr)
quoted = not quoted
line = line.split(splitstr, i)
if len(line) < i + 1 or line[-1].strip() != "":
raise TomlDecodeError("Key group not on a line by itself.",
original, pos)
groups = splitstr.join(line[:-1]).split('.')
i = 0
while i < len(groups):
groups[i] = groups[i].strip()
if len(groups[i]) > 0 and (groups[i][0] == '"' or
groups[i][0] == "'"):
groupstr = groups[i]
j = i + 1
while not groupstr[0] == groupstr[-1]:
j += 1
if j > len(groups) + 2:
raise TomlDecodeError("Invalid group name '" +
groupstr + "' Something " +
"went wrong.", original, pos)
groupstr = '.'.join(groups[i:j]).strip()
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.",
original, pos)
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", original, pos)
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",
original, pos)
elif arrayoftables:
currentlevel[group].append(decoder.get_empty_table()
)
else:
raise TomlDecodeError("What? " + group +
" already exists?" +
str(currentlevel),
original, pos)
except TypeError:
currentlevel = currentlevel[-1]
if group not in currentlevel:
currentlevel[group] = decoder.get_empty_table()
if i == len(groups) - 1 and arrayoftables:
currentlevel[group] = [decoder.get_empty_table()]
except KeyError:
if i != len(groups) - 1:
implicitgroups.append(group)
currentlevel[group] = decoder.get_empty_table()
if i == len(groups) - 1 and arrayoftables:
currentlevel[group] = [decoder.get_empty_table()]
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", original, pos)
try:
decoder.load_inline_object(line, currentlevel, multikey,
multibackslash)
except ValueError as err:
raise TomlDecodeError(str(err), original, pos)
elif "=" in line:
try:
ret = decoder.load_line(line, currentlevel, multikey,
multibackslash)
except ValueError as err:
raise TomlDecodeError(str(err), original, pos)
if ret is not None:
multikey, multilinestr, multibackslash = ret
return retval
def _load_date(val):
microsecond = 0
tz = None
try:
if len(val) > 19:
if val[19] == '.':
if val[-1].upper() == 'Z':
subsecondval = val[20:-1]
tzval = "Z"
else:
subsecondvalandtz = val[20:]
if '+' in subsecondvalandtz:
splitpoint = subsecondvalandtz.index('+')
subsecondval = subsecondvalandtz[:splitpoint]
tzval = subsecondvalandtz[splitpoint:]
elif '-' in subsecondvalandtz:
splitpoint = subsecondvalandtz.index('-')
subsecondval = subsecondvalandtz[:splitpoint]
tzval = subsecondvalandtz[splitpoint:]
else:
tzval = None
subsecondval = subsecondvalandtz
if tzval is not None:
tz = TomlTz(tzval)
microsecond = int(int(subsecondval) *
(10 ** (6 - len(subsecondval))))
else:
tz = TomlTz(val[19:])
except ValueError:
tz = None
if "-" not in val[1:]:
return None
try:
if len(val) == 10:
d = datetime.date(
int(val[:4]), int(val[5:7]),
int(val[8:10]))
else:
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):
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
hxb = ''.join(hx[i:i + hxblen]).lower()
if hxb.strip('0123456789abcdef'):
raise ValueError("Invalid escape sequence: " + hxb)
if hxb[0] == "d" and hxb[1].strip('01234567'):
raise ValueError("Invalid escape sequence: " + hxb +
". Only scalar unicode points are allowed.")
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 ValueError("Reserved escape sequence used")
continue
elif v[i] == '\\':
backslash = True
i += 1
return v
class InlineTableDict(object):
"""Sentinel subclass of dict for inline tables."""
class TomlDecoder(object):
def __init__(self, _dict=dict):
self._dict = _dict
def get_empty_table(self):
return self._dict()
def get_empty_inline_table(self):
class DynamicInlineTableDict(self._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()
def load_inline_object(self, line, currentlevel, 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 ValueError("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] == "]") or
(value[0] == '{' and value[-1] == '}'))):
groups.append(candidate_group)
elif len(candidate_groups) > 0:
candidate_groups[0] = (candidate_group + "," +
candidate_groups[0])
else:
raise ValueError("Invalid inline table value encountered")
for group in groups:
status = self.load_line(group, currentlevel, multikey,
multibackslash)
if status is not None:
break
def _get_split_on_quotes(self, line):
doublequotesplits = line.split('"')
quoted = False
quotesplits = []
if len(doublequotesplits) > 1 and "'" in doublequotesplits[0]:
singlequotesplits = doublequotesplits[0].split("'")
doublequotesplits = doublequotesplits[1:]
while len(singlequotesplits) % 2 == 0 and len(doublequotesplits):
singlequotesplits[-1] += '"' + doublequotesplits[0]
doublequotesplits = doublequotesplits[1:]
if "'" in singlequotesplits[-1]:
singlequotesplits = (singlequotesplits[:-1] +
singlequotesplits[-1].split("'"))
quotesplits += singlequotesplits
for doublequotesplit in doublequotesplits:
if quoted:
quotesplits.append(doublequotesplit)
else:
quotesplits += doublequotesplit.split("'")
quoted = not quoted
return quotesplits
def load_line(self, line, currentlevel, multikey, multibackslash):
i = 1
quotesplits = self._get_split_on_quotes(line)
quoted = False
for quotesplit in quotesplits:
if not quoted and '=' in quotesplit:
break
i += quotesplit.count('=')
quoted = not quoted
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 ValueError("Invalid date or number")
if strictly_valid:
strictly_valid = _strictly_valid_num(pair[-1])
pair = ['='.join(pair[:-1]).strip(), pair[-1].strip()]
if '.' in pair[0]:
if '"' in pair[0] or "'" in pair[0]:
quotesplits = self._get_split_on_quotes(pair[0])
quoted = False
levels = []
for quotesplit in quotesplits:
if quoted:
levels.append(quotesplit)
else:
levels += [level.strip() for level in
quotesplit.split('.')]
quoted = not quoted
else:
levels = pair[0].split('.')
while levels[-1] == "":
levels = levels[:-1]
for level in levels[:-1]:
if level == "":
continue
if level not in currentlevel:
currentlevel[level] = self.get_empty_table()
currentlevel = currentlevel[level]
pair[0] = levels[-1].strip()
elif (pair[0][0] == '"' or pair[0][0] == "'") and \
(pair[0][-1] == pair[0][0]):
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 = self.load_value(pair[1], strictly_valid)
try:
currentlevel[pair[0]]
raise ValueError("Duplicate keys!")
except TypeError:
raise ValueError("Duplicate keys!")
except KeyError:
if multikey:
return multikey, multilinestr, multibackslash
else:
currentlevel[pair[0]] = value
def load_value(self, v, strictly_valid=True):
if not v:
raise ValueError("Empty value is invalid")
if v == 'true':
return (True, "bool")
elif v == 'false':
return (False, "bool")
elif v[0] == '"' or v[0] == "'":
quotechar = v[0]
testv = v[1:].split(quotechar)
triplequote = False
triplequotecount = 0
if len(testv) > 1 and testv[0] == '' and testv[1] == '':
testv = testv[2:]
triplequote = True
closed = False
for tv in testv:
if tv == '':
if triplequote:
triplequotecount += 1
else:
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 ValueError("Stuff after closed string. WTF?")
else:
if not triplequote or triplequotecount > 1:
closed = True
else:
triplequotecount = 0
if quotechar == '"':
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 ValueError("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 len(v) > 1 and v[1] == quotechar and (len(v) < 3 or
v[1] == v[2]):
v = v[2:-2]
return (v[1:-1], "str")
elif v[0] == '[':
return (self.load_array(v), "array")
elif v[0] == '{':
inline_object = self.get_empty_inline_table()
self.load_inline_object(v, inline_object)
return (inline_object, "inline_object")
elif TIME_RE.match(v):
h, m, s, _, ms = TIME_RE.match(v).groups()
time = datetime.time(int(h), int(m), int(s), int(ms) if ms else 0)
return (time, "time")
else:
parsed_date = _load_date(v)
if parsed_date is not None:
return (parsed_date, "date")
if not strictly_valid:
raise ValueError("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('_', '')
lowerv = v.lower()
if '.' in v or ('x' not in v and ('e' in v or 'E' in v)):
if '.' in v and v.split('.', 1)[1] == '':
raise ValueError("This float is missing digits after "
"the point")
if v[0] not in '0123456789':
raise ValueError("This float doesn't have a leading "
"digit")
v = float(v)
itype = "float"
elif len(lowerv) == 3 and (lowerv == 'inf' or lowerv == 'nan'):
v = float(v)
itype = "float"
if itype == "int":
v = int(v, 0)
if neg:
return (0 - v, itype)
return (v, itype)
def bounded_string(self, s):
if len(s) == 0:
return True
if s[-1] != s[0]:
return False
i = -2
backslash = False
while len(s) + i > 0:
if s[i] == "\\":
backslash = not backslash
i -= 1
else:
break
return not backslash
def load_array(self, a):
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] == "'":
if in_str:
backslash_index = end_group_index - 1
while (backslash_index > -1 and
a[backslash_index] == '\\'):
in_str = not in_str
backslash_index -= 1
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 (not self.bounded_string(ab) or
(len(ab) > 2 and
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 = self.load_value(a[i])
if atype:
if ntype != atype:
raise ValueError("Not a homogeneous array")
else:
atype = ntype
retval.append(nval)
return retval
toml-0.10.0/toml/encoder.py 0000664 0000000 0000000 00000017664 13355272442 0015534 0 ustar 00root root 0000000 0000000 import datetime
import re
import sys
from toml.decoder import InlineTableDict
if sys.version_info >= (3,):
unicode = str
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, encoder=None):
"""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 = ""
if encoder is None:
encoder = TomlEncoder(o.__class__)
addtoretval, sections = encoder.dump_sections(o, "")
retval += addtoretval
while sections:
newsections = encoder.get_empty_table()
for section in sections:
addtoretval, addtosections = encoder.dump_sections(
sections[section], section)
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_str(v):
if sys.version_info < (3,) and hasattr(v, 'decode') and isinstance(v, str):
v = v.decode('utf-8')
v = "%r" % v
if v[0] == 'u':
v = v[1:]
singlequote = v.startswith("'")
if singlequote or v.startswith('"'):
v = v[1:-1]
if singlequote:
v = v.replace("\\'", "'")
v = v.replace('"', '\\"')
v = v.split("\\x")
while len(v) > 1:
i = -1
if not v[0]:
v = v[1:]
v[0] = v[0].replace("\\\\", "\\")
# No, I don't know why != works and == breaks
joinx = v[0][i] != "\\"
while v[0][:i] and v[0][i] == "\\":
joinx = not joinx
i -= 1
if joinx:
joiner = "x"
else:
joiner = "u00"
v = [v[0] + joiner + v[1]] + v[2:]
return unicode('"' + v[0] + '"')
def _dump_float(v):
return "{0:.16}".format(v).replace("e+0", "e+").replace("e-0", "e-")
def _dump_time(v):
utcoffset = v.utcoffset()
if utcoffset is None:
return v.isoformat()
# The TOML norm specifies that it's local time thus we drop the offset
return v.isoformat()[:-6]
class TomlEncoder(object):
def __init__(self, _dict=dict, preserve=False):
self._dict = _dict
self.preserve = preserve
self.dump_funcs = {
str: _dump_str,
unicode: _dump_str,
list: self.dump_list,
bool: lambda v: unicode(v).lower(),
int: lambda v: v,
float: _dump_float,
datetime.datetime: lambda v: v.isoformat().replace('+00:00', 'Z'),
datetime.time: _dump_time,
datetime.date: lambda v: v.isoformat()
}
def get_empty_table(self):
return self._dict()
def dump_list(self, v):
retval = "["
for u in v:
retval += " " + unicode(self.dump_value(u)) + ","
retval += "]"
return retval
def dump_inline_table(self, 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 = self.dump_inline_table(v)
val_list.append(k + " = " + val)
retval += "{ " + ", ".join(val_list) + " }\n"
return retval
else:
return unicode(self.dump_value(section))
def dump_value(self, v):
# Lookup function corresponding to v's type
dump_fn = self.dump_funcs.get(type(v))
if dump_fn is None and hasattr(v, '__iter__'):
dump_fn = self.dump_funcs[list]
# Evaluate function (if it exists) else return v
return dump_fn(v) if dump_fn is not None else self.dump_funcs[str](v)
def dump_sections(self, o, sup):
retstr = ""
if sup != "" and sup[-1] != ".":
sup += '.'
retdict = self._dict()
arraystr = ""
for section in o:
section = unicode(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 = self.dump_sections(a, sup + qsection)
if s:
if s[0] == "[":
arraytabstr += s
else:
arraystr += s
while d:
newd = self._dict()
for dsec in d:
s1, d1 = self.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 + " = " +
unicode(self.dump_value(o[section])) + '\n')
elif self.preserve and isinstance(o[section], InlineTableDict):
retstr += (qsection + " = " +
self.dump_inline_table(o[section]))
else:
retdict[qsection] = o[section]
retstr += arraystr
return (retstr, retdict)
class TomlPreserveInlineDictEncoder(TomlEncoder):
def __init__(self, _dict=dict):
super(TomlPreserveInlineDictEncoder, self).__init__(_dict, True)
class TomlArraySeparatorEncoder(TomlEncoder):
def __init__(self, _dict=dict, preserve=False, separator=","):
super(TomlArraySeparatorEncoder, self).__init__(_dict, preserve)
if separator.strip() == "":
separator = "," + separator
elif separator.strip(' \t\n\r,'):
raise ValueError("Invalid separator for arrays")
self.separator = separator
def dump_list(self, v):
t = []
retval = "["
for u in v:
t.append(self.dump_value(u))
while t != []:
s = []
for u in t:
if isinstance(u, list):
for r in u:
s.append(r)
else:
retval += " " + unicode(u) + self.separator
t = s
retval += "]"
return retval
toml-0.10.0/toml/ordered.py 0000664 0000000 0000000 00000000542 13355272442 0015524 0 ustar 00root root 0000000 0000000 from collections import OrderedDict
from toml import TomlEncoder
from toml import TomlDecoder
class TomlOrderedDecoder(TomlDecoder):
def __init__(self):
super(self.__class__, self).__init__(_dict=OrderedDict)
class TomlOrderedEncoder(TomlEncoder):
def __init__(self):
super(self.__class__, self).__init__(_dict=OrderedDict)
toml-0.10.0/toml/tz.py 0000664 0000000 0000000 00000001152 13355272442 0014533 0 ustar 00root root 0000000 0000000 from datetime import tzinfo, timedelta
class TomlTz(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 * timedelta(hours=self._hours, minutes=self._minutes)
def dst(self, dt):
return timedelta(0)
toml-0.10.0/tox.ini 0000664 0000000 0000000 00000000526 13355272442 0014070 0 ustar 00root root 0000000 0000000 [tox]
envlist = py27, py33, py34, py35, py36, pypy
[testenv]
deps =
pytest
pytest-cov
commands=pytest tests {posargs}
[testenv:check]
skip_install = true
deps = flake8
commands = flake8
[testenv:codecov]
passenv = CI TRAVIS TRAVIS_*
deps =
codecov
pytest
pytest-cov
commands =
pytest tests --cov=./toml
codecov