pax_global_header00006660000000000000000000000064137474124200014516gustar00rootroot0000000000000052 comment=3f637dba5f68db63d4b30967fedda51c82459471 toml-0.10.2/000077500000000000000000000000001374741242000125515ustar00rootroot00000000000000toml-0.10.2/.coveragearc000066400000000000000000000000661374741242000150350ustar00rootroot00000000000000[run] branch = True source = toml omit = tests/* toml-0.10.2/.flake8000066400000000000000000000005271374741242000137300ustar00rootroot00000000000000[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 = W504 # Output config: show-source = True statistics = True tee = True output-file = .flake8.log toml-0.10.2/.gitignore000066400000000000000000000024111374741242000145370ustar00rootroot00000000000000# 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.2/.travis.yml000066400000000000000000000010201374741242000146530ustar00rootroot00000000000000language: python cache: pip python: - "2.7" - "3.4" - "3.5" - "3.6" - "3.7" - "3.8" - "3.9-dev" #- "pypy" 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.2/CONTRIBUTING000066400000000000000000000026451374741242000144120ustar00rootroot00000000000000************ 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.2/LICENSE000066400000000000000000000023441374741242000135610ustar00rootroot00000000000000The MIT License Copyright 2013-2019 William Pearson Copyright 2015-2016 Julien Enselme Copyright 2016 Google Inc. Copyright 2017 Samuel Vasko Copyright 2017 Nate Prewitt Copyright 2017 Jack Evans Copyright 2019 Filippo Broggini 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.2/MANIFEST.in000066400000000000000000000001711374741242000143060ustar00rootroot00000000000000include LICENSE include README.rst include toml/*pyi include tox.ini include test.toml recursive-include tests *.py *.sh toml-0.10.2/README.rst000066400000000000000000000134531374741242000142460ustar00rootroot00000000000000**** TOML **** .. image:: https://img.shields.io/pypi/v/toml :target: https://pypi.org/project/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" *toml.dump* takes a dictionary and a file descriptor and returns a string containing the corresponding TOML-formatted data. .. code:: pycon >>> with open('new_toml_file.toml', 'w') as f: ... new_toml_string = toml.dump(parsed_toml, f) >>> 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. Note ---- For Numpy users, by default the data types ``np.floatX`` will not be translated to floats by toml, but will instead be encoded as strings. To get around this, specify the ``TomlNumpyEncoder`` when saving your data. .. code:: pycon >>> import toml >>> import numpy as np >>> a = np.arange(0, 10, dtype=np.double) >>> output = {'a': a} >>> toml.dumps(output) 'a = [ "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0", "9.0",]\n' >>> toml.dumps(output, encoder=toml.TomlNumpyEncoder()) 'a = [ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0,]\n' 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, encoder=None)`` 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 * ``encoder``: An instance of ``TomlEncoder`` (or subclass) for encoding the object. If ``None``, will default to ``TomlEncoder`` :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, encoder=None)`` Create a TOML-formatted string from an input object :Args: * ``o``: An object to be converted into TOML * ``encoder``: An instance of ``TomlEncoder`` (or subclass) for encoding the object. If ``None``, will default to ``TomlEncoder`` :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.2/RELEASE.rst000066400000000000000000000010531374741242000143620ustar00rootroot00000000000000********* 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.2/examples/000077500000000000000000000000001374741242000143675ustar00rootroot00000000000000toml-0.10.2/examples/example-v0.4.0.out000066400000000000000000000041111374741242000173730ustar00rootroot00000000000000{'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.2/examples/example-v0.4.0.toml000066400000000000000000000124531374741242000175470ustar00rootroot00000000000000################################################################################ ## 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.2/setup.cfg000066400000000000000000000001021374741242000143630ustar00rootroot00000000000000[bdist_wheel] universal = True [metadata] license_file = LICENSE toml-0.10.2/setup.py000066400000000000000000000027151374741242000142700ustar00rootroot00000000000000try: 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, python_requires=">=2.6, !=3.0.*, !=3.1.*, !=3.2.*", 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 :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ] ) toml-0.10.2/test.toml000066400000000000000000000023361374741242000144310ustar00rootroot00000000000000# 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" ] [meeting] [meeting.inspace] time = 10:00:00 [meeting.nospace] time=10:00:00 [[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.2/tests/000077500000000000000000000000001374741242000137135ustar00rootroot00000000000000toml-0.10.2/tests/__init__.py000066400000000000000000000000001374741242000160120ustar00rootroot00000000000000toml-0.10.2/tests/decoding_test.py000077500000000000000000000033121374741242000171020ustar00rootroot00000000000000"""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.2/tests/decoding_test.sh000077500000000000000000000001011374741242000170550ustar00rootroot00000000000000#!/bin/sh export PYTHONPATH=`pwd` python tests/decoding_test.py toml-0.10.2/tests/decoding_test2.sh000077500000000000000000000001021374741242000171400ustar00rootroot00000000000000#!/bin/sh export PYTHONPATH=`pwd` python2 tests/decoding_test.py toml-0.10.2/tests/decoding_test3.sh000077500000000000000000000001021374741242000171410ustar00rootroot00000000000000#!/bin/sh export PYTHONPATH=`pwd` python3 tests/decoding_test.py toml-0.10.2/tests/test_api.py000066400000000000000000000156241374741242000161050ustar00rootroot00000000000000import toml import copy import pytest import os import sys from decimal import Decimal 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 with open(os.path.join(valid_dir, f)) as fh: toml.dumps(toml.load(fh)) def test_circular_ref(): a = {} b = {} b['c'] = 4 b['self'] = b a['b'] = b with pytest.raises(ValueError): toml.dumps(a) with pytest.raises(ValueError): toml.dumps(b) 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_numpy_floats(): np = pytest.importorskip('numpy') encoder = toml.TomlNumpyEncoder() d = {'a': np.array([1, .3], dtype=np.float64)} o = toml.loads(toml.dumps(d, encoder=encoder)) assert o == toml.loads(toml.dumps(o, encoder=encoder)) d = {'a': np.array([1, .3], dtype=np.float32)} o = toml.loads(toml.dumps(d, encoder=encoder)) assert o == toml.loads(toml.dumps(o, encoder=encoder)) d = {'a': np.array([1, .3], dtype=np.float16)} o = toml.loads(toml.dumps(d, encoder=encoder)) assert o == toml.loads(toml.dumps(o, encoder=encoder)) def test_numpy_ints(): np = pytest.importorskip('numpy') encoder = toml.TomlNumpyEncoder() d = {'a': np.array([1, 3], dtype=np.int64)} o = toml.loads(toml.dumps(d, encoder=encoder)) assert o == toml.loads(toml.dumps(o, encoder=encoder)) d = {'a': np.array([1, 3], dtype=np.int32)} o = toml.loads(toml.dumps(d, encoder=encoder)) assert o == toml.loads(toml.dumps(o, encoder=encoder)) d = {'a': np.array([1, 3], dtype=np.int16)} 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_decimal(): PLACES = Decimal(10) ** -4 d = {"a": Decimal("0.1")} o = toml.loads(toml.dumps(d)) assert o == toml.loads(toml.dumps(o)) assert Decimal(o["a"]).quantize(PLACES) == d["a"].quantize(PLACES) 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): with open(os.path.join(invalid_dir, f)) as fh: toml.load(fh) def test_exceptions(): with pytest.raises(TypeError): toml.loads(2) with pytest.raises(TypeError): toml.load(2) try: FNFError = FileNotFoundError except NameError: # py2 FNFError = IOError with pytest.raises(FNFError): 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(): from collections import OrderedDict f = FakeFile() g = FakeFile() h = FakeFile() toml.dump(TEST_DICT, f) toml.dump(toml.load(f, _dict=OrderedDict), g) toml.dump(toml.load(g, _dict=OrderedDict), h) assert g.written == h.written def test_paths(): toml.load("test.toml") toml.load(b"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)) def test_pathlib(): if (3, 4) <= sys.version_info: import pathlib o = {"root": {"path": pathlib.Path("/home/edgy")}} test_str = """[root] path = "/home/edgy" """ assert test_str == toml.dumps(o, encoder=toml.TomlPathlibEncoder()) def test_comment_preserve_decoder_encoder(): test_str = """[[products]] name = "Nail" sku = 284758393 # This is a comment color = "gray" # Hello World # name = { first = 'Tom', last = 'Preston-Werner' } # arr7 = [ # 1, 2, 3 # ] # lines = ''' # The first newline is # trimmed in raw strings. # All other whitespace # is preserved. # ''' [animals] color = "gray" # col fruits = "apple" # a = [1,2,3] a = 3 b-comment = "a is 3" """ s = toml.dumps(toml.loads(test_str, decoder=toml.TomlPreserveCommentDecoder()), encoder=toml.TomlPreserveCommentEncoder()) assert len(s) == len(test_str) and sorted(test_str) == sorted(s) def test_deepcopy_timezone(): import copy o = toml.loads("dob = 1979-05-24T07:32:00-08:00") o2 = copy.deepcopy(o) assert o2["dob"] == o["dob"] assert o2["dob"] is not o["dob"] toml-0.10.2/toml/000077500000000000000000000000001374741242000135245ustar00rootroot00000000000000toml-0.10.2/toml/__init__.py000066400000000000000000000013231374741242000156340ustar00rootroot00000000000000"""Python module which parses and emits TOML. Released under the MIT license. """ from toml import encoder from toml import decoder __version__ = "0.10.2" _spec_ = "0.5.0" load = decoder.load loads = decoder.loads TomlDecoder = decoder.TomlDecoder TomlDecodeError = decoder.TomlDecodeError TomlPreserveCommentDecoder = decoder.TomlPreserveCommentDecoder dump = encoder.dump dumps = encoder.dumps TomlEncoder = encoder.TomlEncoder TomlArraySeparatorEncoder = encoder.TomlArraySeparatorEncoder TomlPreserveInlineDictEncoder = encoder.TomlPreserveInlineDictEncoder TomlNumpyEncoder = encoder.TomlNumpyEncoder TomlPreserveCommentEncoder = encoder.TomlPreserveCommentEncoder TomlPathlibEncoder = encoder.TomlPathlibEncoder toml-0.10.2/toml/__init__.pyi000066400000000000000000000011331374741242000160040ustar00rootroot00000000000000from toml import decoder as decoder, encoder as encoder load = decoder.load loads = decoder.loads TomlDecoder = decoder.TomlDecoder TomlDecodeError = decoder.TomlDecodeError TomlPreserveCommentDecoder = decoder.TomlPreserveCommentDecoder dump = encoder.dump dumps = encoder.dumps TomlEncoder = encoder.TomlEncoder TomlArraySeparatorEncoder = encoder.TomlArraySeparatorEncoder TomlPreserveInlineDictEncoder = encoder.TomlPreserveInlineDictEncoder TomlNumpyEncoder = encoder.TomlNumpyEncoder TomlPreserveCommentEncoder = encoder.TomlPreserveCommentEncoder TomlPathlibEncoder = encoder.TomlPathlibEncoder toml-0.10.2/toml/decoder.py000066400000000000000000001140361374741242000155100ustar00rootroot00000000000000import 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, (bytes, 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(r"([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]))*') class CommentValue(object): def __init__(self, val, comment, beginline, _dict): self.val = val separator = "\n" if beginline else " " self.comment = separator + comment self._dict = _dict def __getitem__(self, key): return self.val[key] def __setitem__(self, key, value): self.val[key] = value def dump(self, dump_value_func): retstr = dump_value_func(self.val) if isinstance(self.val, self._dict): return self.comment + "\n" + unicode(retstr) else: return unicode(retstr) + self.comment 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 decoder: The decoder to use 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(_dict) d = decoder.get_empty_table() for l in f: # noqa: E741 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 key = '' prev_key = '' line_no = 1 for i, item in enumerate(sl): if item == '\r' and sl[i + 1] == '\n': sl[i] = ' ' continue if keyname: key += item if item == '\n': raise TomlDecodeError("Key name found without value." " Reached end of line.", original, i) if openstring: if item == openstrchar: oddbackslash = False k = 1 while i >= k and sl[i - k] == '\\': oddbackslash = not oddbackslash k += 1 if not oddbackslash: 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 prev_key = key[:-1].rstrip() key = '' 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 comment = "" try: while sl[j] != '\n': comment += s[j] sl[j] = ' ' j += 1 except IndexError: break if not openarr: decoder.preserve_comment(line_no, prev_key, comment, beginline) 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 line_no += 1 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 key += item if keyname: raise TomlDecodeError("Key name found without value." " Reached end of file.", original, len(s)) if openstring: # reached EOF and have an unterminated string raise TomlDecodeError("Unterminated string found." " Reached end of file.", original, len(s)) 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 decoder.embed_comments(idx, currentlevel) 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 closed = False if multilinestr[0] == '[': closed = line[-1] == ']' elif len(line) > 2: closed = (line[-1] == multilinestr[0] and line[-2] == multilinestr[0] and line[-3] == multilinestr[0]) if closed: 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]) or len(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].strip() != 'true' and pair[-1].strip() != 'false'): try: float(pair[-1]) break except ValueError: pass if _load_date(pair[-1]) is not None: break if TIME_RE.match(pair[-1]): 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] = _unescape(pair[0][1:-1]) k, koffset = self._load_line_multiline_str(pair[1]) if k > -1: while k > -1 and pair[1][k + koffset] == '\\': 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_line_multiline_str(self, p): poffset = 0 if len(p) < 3: return -1, poffset if p[0] == '[' and (p.strip()[-1] != ']' and self._load_array_isstrarray(p)): newp = p[1:].strip().split(',') while len(newp) > 1 and newp[-1][0] != '"' and newp[-1][0] != "'": newp = newp[:-2] + [newp[-2] + ',' + newp[-1]] newp = newp[-1] poffset = len(p) - len(newp) p = newp if p[0] != '"' and p[0] != "'": return -1, poffset if p[1] != p[0] or p[2] != p[0]: return -1, poffset if len(p) > 5 and p[-1] == p[0] and p[-2] == p[0] and p[-3] == p[0]: return -1, poffset return len(p) - 1, poffset 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.lower() == 'true': raise ValueError("Only all lowercase booleans allowed") elif v == 'false': return (False, "bool") elif v.lower() == 'false': raise ValueError("Only all lowercase booleans allowed") 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("Found tokens after a closed " + "string. Invalid TOML.") 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_isstrarray(self, a): a = a[1:-1].strip() if a != '' and (a[0] == '"' or a[0] == "'"): return True return False 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 = self._load_array_isstrarray(a) 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 open_bracket_count = 1 if a[start_group_index] == '{' else 0 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 not in_str and a[end_group_index] == '{': open_bracket_count += 1 if in_str or a[end_group_index] != '}': end_group_index += 1 continue elif a[end_group_index] == '}' and open_bracket_count > 1: open_bracket_count -= 1 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 def preserve_comment(self, line_no, key, comment, beginline): pass def embed_comments(self, idx, currentlevel): pass class TomlPreserveCommentDecoder(TomlDecoder): def __init__(self, _dict=dict): self.saved_comments = {} super(TomlPreserveCommentDecoder, self).__init__(_dict) def preserve_comment(self, line_no, key, comment, beginline): self.saved_comments[line_no] = (key, comment, beginline) def embed_comments(self, idx, currentlevel): if idx not in self.saved_comments: return key, comment, beginline = self.saved_comments[idx] currentlevel[key] = CommentValue(currentlevel[key], comment, beginline, self._dict) toml-0.10.2/toml/decoder.pyi000066400000000000000000000037231374741242000156610ustar00rootroot00000000000000from toml.tz import TomlTz as TomlTz from typing import Any, Optional unicode = str basestring = str unichr = chr FNFError = FileNotFoundError FNFError = IOError TIME_RE: Any class TomlDecodeError(ValueError): msg: Any = ... doc: Any = ... pos: Any = ... lineno: Any = ... colno: Any = ... def __init__(self, msg: Any, doc: Any, pos: Any) -> None: ... class CommentValue: val: Any = ... comment: Any = ... def __init__(self, val: Any, comment: Any, beginline: Any, _dict: Any) -> None: ... def __getitem__(self, key: Any): ... def __setitem__(self, key: Any, value: Any) -> None: ... def dump(self, dump_value_func: Any): ... def load(f: Union[str, list, IO[str]], _dict: Type[MutableMapping[str, Any]] = ..., decoder: TomlDecoder = ...) \ -> MutableMapping[str, Any]: ... def loads(s: str, _dict: Type[MutableMapping[str, Any]] = ..., decoder: TomlDecoder = ...) \ -> MutableMapping[str, Any]: ... class InlineTableDict: ... class TomlDecoder: def __init__(self, _dict: Any = ...) -> None: ... def get_empty_table(self): ... def get_empty_inline_table(self): ... def load_inline_object(self, line: Any, currentlevel: Any, multikey: bool = ..., multibackslash: bool = ...) -> None: ... def load_line(self, line: Any, currentlevel: Any, multikey: Any, multibackslash: Any): ... def load_value(self, v: Any, strictly_valid: bool = ...): ... def bounded_string(self, s: Any): ... def load_array(self, a: Any): ... def preserve_comment(self, line_no: Any, key: Any, comment: Any, beginline: Any) -> None: ... def embed_comments(self, idx: Any, currentlevel: Any) -> None: ... class TomlPreserveCommentDecoder(TomlDecoder): saved_comments: Any = ... def __init__(self, _dict: Any = ...) -> None: ... def preserve_comment(self, line_no: Any, key: Any, comment: Any, beginline: Any) -> None: ... def embed_comments(self, idx: Any, currentlevel: Any) -> None: ... toml-0.10.2/toml/encoder.py000066400000000000000000000233241374741242000155210ustar00rootroot00000000000000import datetime import re import sys from decimal import Decimal from toml.decoder import InlineTableDict if sys.version_info >= (3,): unicode = str def dump(o, f, encoder=None): """Writes out dict as toml to a file Args: o: Object to dump into toml f: File descriptor where the toml should be stored encoder: The ``TomlEncoder`` to use for constructing the output string 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, encoder=encoder) f.write(d) return d def dumps(o, encoder=None): """Stringifies input dict as toml Args: o: Object to dump into toml encoder: The ``TomlEncoder`` to use for constructing the output string Returns: String containing the toml corresponding to dict Examples: ```python >>> import toml >>> output = { ... 'a': "I'm a string", ... 'b': ["I'm", "a", "list"], ... 'c': 2400 ... } >>> toml.dumps(output) 'a = "I\'m a string"\nb = [ "I\'m", "a", "list",]\nc = 2400\n' ``` """ retval = "" if encoder is None: encoder = TomlEncoder(o.__class__) addtoretval, sections = encoder.dump_sections(o, "") retval += addtoretval outer_objs = [id(o)] while sections: section_ids = [id(section) for section in sections.values()] for outer_obj in outer_objs: if outer_obj in section_ids: raise ValueError("Circular reference detected") outer_objs += section_ids 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 "{}".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, Decimal: _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): qsection = _dump_str(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 class TomlNumpyEncoder(TomlEncoder): def __init__(self, _dict=dict, preserve=False): import numpy as np super(TomlNumpyEncoder, self).__init__(_dict, preserve) self.dump_funcs[np.float16] = _dump_float self.dump_funcs[np.float32] = _dump_float self.dump_funcs[np.float64] = _dump_float self.dump_funcs[np.int16] = self._dump_int self.dump_funcs[np.int32] = self._dump_int self.dump_funcs[np.int64] = self._dump_int def _dump_int(self, v): return "{}".format(int(v)) class TomlPreserveCommentEncoder(TomlEncoder): def __init__(self, _dict=dict, preserve=False): from toml.decoder import CommentValue super(TomlPreserveCommentEncoder, self).__init__(_dict, preserve) self.dump_funcs[CommentValue] = lambda v: v.dump(self.dump_value) class TomlPathlibEncoder(TomlEncoder): def _dump_pathlib_path(self, v): return _dump_str(str(v)) def dump_value(self, v): if (3, 4) <= sys.version_info: import pathlib if isinstance(v, pathlib.PurePath): v = str(v) return super(TomlPathlibEncoder, self).dump_value(v) toml-0.10.2/toml/encoder.pyi000066400000000000000000000023221374741242000156650ustar00rootroot00000000000000from toml.decoder import InlineTableDict as InlineTableDict from typing import Any, Optional unicode = str def dump(o: Mapping[str, Any], f: IO[str], encoder: TomlEncoder = ...) -> str: ... def dumps(o: Mapping[str, Any], encoder: TomlEncoder = ...) -> str: ... class TomlEncoder: preserve: Any = ... dump_funcs: Any = ... def __init__(self, _dict: Any = ..., preserve: bool = ...): ... def get_empty_table(self): ... def dump_list(self, v: Any): ... def dump_inline_table(self, section: Any): ... def dump_value(self, v: Any): ... def dump_sections(self, o: Any, sup: Any): ... class TomlPreserveInlineDictEncoder(TomlEncoder): def __init__(self, _dict: Any = ...) -> None: ... class TomlArraySeparatorEncoder(TomlEncoder): separator: Any = ... def __init__(self, _dict: Any = ..., preserve: bool = ..., separator: str = ...) -> None: ... def dump_list(self, v: Any): ... class TomlNumpyEncoder(TomlEncoder): def __init__(self, _dict: Any = ..., preserve: bool = ...) -> None: ... class TomlPreserveCommentEncoder(TomlEncoder): def __init__(self, _dict: Any = ..., preserve: bool = ...): ... class TomlPathlibEncoder(TomlEncoder): def dump_value(self, v: Any): ... toml-0.10.2/toml/ordered.py000066400000000000000000000005421374741242000155230ustar00rootroot00000000000000from 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.2/toml/ordered.pyi000066400000000000000000000003401374741242000156700ustar00rootroot00000000000000from toml import TomlDecoder as TomlDecoder, TomlEncoder as TomlEncoder class TomlOrderedDecoder(TomlDecoder): def __init__(self) -> None: ... class TomlOrderedEncoder(TomlEncoder): def __init__(self) -> None: ... toml-0.10.2/toml/tz.py000066400000000000000000000012751374741242000145400ustar00rootroot00000000000000from 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 __deepcopy__(self, memo): return self.__class__(self._raw_offset) 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.2/toml/tz.pyi000066400000000000000000000004241374741242000147040ustar00rootroot00000000000000from datetime import tzinfo from typing import Any class TomlTz(tzinfo): def __init__(self, toml_offset: Any) -> None: ... def __deepcopy__(self, memo: Any): ... def tzname(self, dt: Any): ... def utcoffset(self, dt: Any): ... def dst(self, dt: Any): ... toml-0.10.2/tox.ini000066400000000000000000000005621374741242000140670ustar00rootroot00000000000000[tox] envlist = py27, py33, py34, py35, py36, py37, py38, py39, pypy [testenv] deps = pytest pytest-cov numpy 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