├── .codesandbox ├── Dockerfile └── tasks.json ├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── README.raw.md ├── poetry.lock ├── pyproject.toml ├── reports ├── with_toml-test_latest.md ├── with_toml-test_v1.2.0.md ├── with_toml-test_v1.3.0.md └── with_toml-test_v1.4.0.md ├── run-tests.py ├── setup.py └── toml_bench ├── __init__.py ├── __main__.py ├── api.py ├── cases ├── __init__.py ├── compliance.py ├── hetero_array.py ├── keys_order.py ├── speed.py ├── unicode.py ├── value_none.py └── version.py ├── sets.py ├── suite.py └── utils.py /.codesandbox/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10.12 2 | 3 | RUN apt-get update && apt-get install -y fish && \ 4 | pip install -U pip && \ 5 | pip install poetry && \ 6 | poetry config virtualenvs.create false && \ 7 | chsh -s /usr/bin/fish 8 | -------------------------------------------------------------------------------- /.codesandbox/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // These tasks will run in order when initializing your CodeSandbox project. 3 | "setupTasks": [], 4 | 5 | // These tasks can be run from CodeSandbox. Running one will open a log in the app. 6 | "tasks": { 7 | "poetry update && poetry install": { 8 | "name": "poetry update && poetry install", 9 | "command": "poetry update && poetry install", 10 | "runAtStart": true 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | 7 | 8 | deploy: 9 | runs-on: ubuntu-latest 10 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') 11 | strategy: 12 | matrix: 13 | python-version: [3.9] 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Setup Python # Set Python version 17 | uses: actions/setup-python@v2 18 | - name: Install dependencies 19 | run: | 20 | python -m pip install --upgrade pip 21 | python -m pip install poetry 22 | - name: Publish to PyPI 23 | run: poetry publish --build -u ${{ secrets.PYPI_USER }} -p ${{ secrets.PYPI_PASSWORD }} 24 | if: success() 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | 162 | test-data/ 163 | _t.py 164 | _t.md 165 | .vscode/ 166 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 pwwang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.raw.md: -------------------------------------------------------------------------------- 1 | # toml-bench 2 | 3 | [![deps][1]][2] 4 | 5 | Which toml package to use in python? 6 | 7 | See also: [toml-lang](https://toml.io/en/) and [PEP 680](https://www.python.org/dev/peps/pep-0680/) 8 | 9 | {{latest-report}} 10 | 11 | ## Other reports 12 | 13 | - [Tests with `toml-test` v1.4.0](./reports/with_toml-test_v1.4.0.md) 14 | - [Tests with `toml-test` v1.3.0](./reports/with_toml-test_v1.3.0.md) 15 | - [Tests with `toml-test` v1.2.0](./reports/with_toml-test_v1.2.0.md) 16 | 17 | ## Run your own report 18 | 19 | ### Install 20 | 21 | ```shell 22 | pip install -U toml-bench 23 | ``` 24 | 25 | ### Generate your own report 26 | 27 | ```shell 28 | toml-bench 29 | ``` 30 | 31 | #### Use a different data directory than the default one 32 | 33 | ```shell 34 | toml-bench --datadir /tmp/toml-bench 35 | ``` 36 | 37 | #### Write the report to a markdown file 38 | 39 | ```shell 40 | toml-bench --report ./README.md 41 | ``` 42 | 43 | #### Test with a different version of compliance set (`BurntSushi/toml-test`) 44 | 45 | ```shell 46 | toml-bench --comver 1.0.0 47 | ``` 48 | 49 | #### Use a different number of iterations in speed tests 50 | 51 | ```shell 52 | toml-bench --iter 5000 53 | ``` 54 | 55 | #### Test with different versions of packages 56 | 57 | ```shell 58 | git clone https://github.com/pwwang/toml-bench.git 59 | cd toml-bench 60 | # See https://python-poetry.org/docs/cli/#add 61 | # for how to specify a version constraint 62 | poetry add "tomli=2.0.0" 63 | poetry update 64 | poetry install 65 | poetry run toml-bench 66 | ``` 67 | 68 | [1]: https://img.shields.io/librariesio/release/pypi/toml-bench?style=flat-square 69 | [2]: https://libraries.io/github/pwwang/toml-bench#repository_dependencies 70 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "argx" 5 | version = "0.2.10" 6 | description = "Super-charged argparse for python" 7 | optional = false 8 | python-versions = ">=3.7,<4.0" 9 | files = [ 10 | {file = "argx-0.2.10-py3-none-any.whl", hash = "sha256:85c6b150c5ec86dd5aefaca260dcf8bd8445a3c6d174eaaacb5835638d207083"}, 11 | {file = "argx-0.2.10.tar.gz", hash = "sha256:72ab5511163870eeb589c007fb11cb2c5cc91b9faa553ed45e7404f6c1e7a238"}, 12 | ] 13 | 14 | [package.dependencies] 15 | python-simpleconf = ">=0.6,<0.7" 16 | 17 | [[package]] 18 | name = "attrs" 19 | version = "21.4.0" 20 | description = "Classes Without Boilerplate" 21 | optional = false 22 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 23 | files = [ 24 | {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, 25 | {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, 26 | ] 27 | 28 | [package.extras] 29 | dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "sphinx", "sphinx-notfound-page", "zope.interface"] 30 | docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] 31 | tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"] 32 | tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"] 33 | 34 | [[package]] 35 | name = "benchwork" 36 | version = "0.0.2" 37 | description = "A framework for benchmarking in python" 38 | optional = false 39 | python-versions = ">=3.7,<4.0" 40 | files = [ 41 | {file = "benchwork-0.0.2-py3-none-any.whl", hash = "sha256:108ca9bd7aef958ff0b5af1e41d654d95296629ffcffb2b49a9ba1a9347f1fb2"}, 42 | {file = "benchwork-0.0.2.tar.gz", hash = "sha256:5e6c3ba005c535485534bb67fe48b16a153739103c2d4e319eca57730701fdbe"}, 43 | ] 44 | 45 | [[package]] 46 | name = "click" 47 | version = "8.1.7" 48 | description = "Composable command line interface toolkit" 49 | optional = false 50 | python-versions = ">=3.7" 51 | files = [ 52 | {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, 53 | {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, 54 | ] 55 | 56 | [package.dependencies] 57 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 58 | 59 | [[package]] 60 | name = "colorama" 61 | version = "0.4.6" 62 | description = "Cross-platform colored terminal text." 63 | optional = false 64 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 65 | files = [ 66 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 67 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 68 | ] 69 | 70 | [[package]] 71 | name = "diot" 72 | version = "0.2.3" 73 | description = "Python dictionary with dot notation." 74 | optional = false 75 | python-versions = ">=3.7,<4.0" 76 | files = [ 77 | {file = "diot-0.2.3-py3-none-any.whl", hash = "sha256:defd4556bd961327cbfd497f63fcb9d0d79a649bbc8932f26d94dd25a7832e1c"}, 78 | {file = "diot-0.2.3.tar.gz", hash = "sha256:0b8ccbe3813acda67665ea9025d4eea7f73b2df4e2ab16c5c3f1da8b0dbba4cf"}, 79 | ] 80 | 81 | [package.dependencies] 82 | inflection = ">=0.5,<0.6" 83 | 84 | [[package]] 85 | name = "importlib-metadata" 86 | version = "7.2.1" 87 | description = "Read metadata from Python packages" 88 | optional = false 89 | python-versions = ">=3.8" 90 | files = [ 91 | {file = "importlib_metadata-7.2.1-py3-none-any.whl", hash = "sha256:ffef94b0b66046dd8ea2d619b701fe978d9264d38f3998bc4c27ec3b146a87c8"}, 92 | {file = "importlib_metadata-7.2.1.tar.gz", hash = "sha256:509ecb2ab77071db5137c655e24ceb3eee66e7bbc6574165d0d114d9fc4bbe68"}, 93 | ] 94 | 95 | [package.dependencies] 96 | zipp = ">=0.5" 97 | 98 | [package.extras] 99 | doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 100 | perf = ["ipython"] 101 | test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] 102 | 103 | [[package]] 104 | name = "inflection" 105 | version = "0.5.1" 106 | description = "A port of Ruby on Rails inflector to Python" 107 | optional = false 108 | python-versions = ">=3.5" 109 | files = [ 110 | {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, 111 | {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, 112 | ] 113 | 114 | [[package]] 115 | name = "python-dateutil" 116 | version = "2.9.0.post0" 117 | description = "Extensions to the standard Python datetime module" 118 | optional = false 119 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 120 | files = [ 121 | {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, 122 | {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, 123 | ] 124 | 125 | [package.dependencies] 126 | six = ">=1.5" 127 | 128 | [[package]] 129 | name = "python-simpleconf" 130 | version = "0.6.0" 131 | description = "Simple configuration management with python." 132 | optional = false 133 | python-versions = ">=3.7,<4.0" 134 | files = [ 135 | {file = "python_simpleconf-0.6.0-py3-none-any.whl", hash = "sha256:40e73fbd60d84ff45d22fcd8f7e2ade5fcca65559f8d9865e7750d8bcfadd209"}, 136 | {file = "python_simpleconf-0.6.0.tar.gz", hash = "sha256:06c29e0c08e22a41024028c46cd845bdfe3cbba402730fa65fb007fa89315801"}, 137 | ] 138 | 139 | [package.dependencies] 140 | diot = ">=0.2.1,<0.3.0" 141 | 142 | [package.extras] 143 | all = ["iniconfig (>=2.0,<3.0)", "python-dotenv (>=0.21,<0.22)", "pyyaml (>=6,<7)", "rtoml (>=0.8,<0.9)", "tomli (>=2.0,<3.0)"] 144 | env = ["python-dotenv (>=0.21,<0.22)"] 145 | ini = ["iniconfig (>=2.0,<3.0)"] 146 | toml = ["rtoml (>=0.8,<0.9)", "tomli (>=2.0,<3.0)"] 147 | yaml = ["pyyaml (>=6,<7)"] 148 | 149 | [[package]] 150 | name = "pytomlpp" 151 | version = "1.0.13" 152 | description = "A python wrapper for toml++" 153 | optional = false 154 | python-versions = "*" 155 | files = [ 156 | {file = "pytomlpp-1.0.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:131644b5c32a0b32667875883251fa01a6a852e0386e5db8d0f70ddf44bebe3b"}, 157 | {file = "pytomlpp-1.0.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:23d7ac98b01965e17c604bc86c77f751e4f91055c68397a724e7a05cd91c04fd"}, 158 | {file = "pytomlpp-1.0.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:98ae1451dcabb3449df8a097014b2d5cdaeb8961f16cced83462dfb704b61d12"}, 159 | {file = "pytomlpp-1.0.13-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc31231a57b45499c68b7c6c7c7d176874c1b4d3c236e3e4ecfc005642496815"}, 160 | {file = "pytomlpp-1.0.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68d1743e03806e67a0b5daefb8f548609ffeec4ab94d91092440325c9e22bc77"}, 161 | {file = "pytomlpp-1.0.13-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5b9a8b2179271abc54c0879cccc880b50918409765074c9ee25d45545f60b2a8"}, 162 | {file = "pytomlpp-1.0.13-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d325590d276104e4030c7907dd559148566347e840cb1c3874671ea09491789"}, 163 | {file = "pytomlpp-1.0.13-cp310-cp310-win_amd64.whl", hash = "sha256:fab6e69a754d3be5d8580753feac56fa70d2f4f9388f1d7a30c719ceca2d3ff2"}, 164 | {file = "pytomlpp-1.0.13-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47684714f9ed1a880e66b2e183789519a9ad7b55f2da9e30b65090a42342ef83"}, 165 | {file = "pytomlpp-1.0.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d7c0e8a1e21249df4e60f07a728129f33899b7548ff391343b244e1222296b1"}, 166 | {file = "pytomlpp-1.0.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aa016e89a76c3ed58ea7a1eb2cb795ee1b1aa2831bb47c724ae787cb03dcf790"}, 167 | {file = "pytomlpp-1.0.13-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4549130097ca8b7aada5f6e8440db3dbc36f0f3df24231b3521297a5e3cecf1"}, 168 | {file = "pytomlpp-1.0.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d2e7be5ddf349fbcfdd1cfcee630c4ad33031411a9dded93a96d186f2086038"}, 169 | {file = "pytomlpp-1.0.13-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3398853a6ab98ccb5e722b9d2f1ac127ea2a82d267dcff8ff7dc98a472f70ad0"}, 170 | {file = "pytomlpp-1.0.13-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b606dea8b05d2f5915a4d6f88e7772eb4772ed8a65c113b15bff5754982f48df"}, 171 | {file = "pytomlpp-1.0.13-cp311-cp311-win_amd64.whl", hash = "sha256:1843cd3816e25453bfcac5468106f9f39e766bda198bd69d41e09184a6062497"}, 172 | {file = "pytomlpp-1.0.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:adfbfb981bcfe31cec4443b2410ae65cea6ec37b13396f7a0a66ffffd418a075"}, 173 | {file = "pytomlpp-1.0.13-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87256e701a6237a178739323394e5abfe3bf5fa5eb0188d8710839412556b56e"}, 174 | {file = "pytomlpp-1.0.13-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2752caeb649c04dcdfe78dd43d63b9c03db01d41f4e97af133f214cf6ee5f09"}, 175 | {file = "pytomlpp-1.0.13-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:988288644025dc20a997a5caa8d6d283ad94bdbc30415a965a12b82bc77026c1"}, 176 | {file = "pytomlpp-1.0.13-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:807f8cfff2195b68fbefe8122561be6ced51009f5c95c422f781bae5d7d90fc4"}, 177 | {file = "pytomlpp-1.0.13-cp36-cp36m-win_amd64.whl", hash = "sha256:7443a2dce8f48c0801c1f5b3ea78acbae30fb263480570f8b68b198620041afa"}, 178 | {file = "pytomlpp-1.0.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c155e2b72844dd11ca0fdfc3bfb44bc41770ba34803b67621e41496f67b70453"}, 179 | {file = "pytomlpp-1.0.13-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffae01816f10fa29e0cd30bd8709573de5ba1ec57dd5901ab6e2f8c7c199ed7a"}, 180 | {file = "pytomlpp-1.0.13-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e030b4440cc65937ae9701448615ba445ea2c1ff20fa630c149e368e06f9b082"}, 181 | {file = "pytomlpp-1.0.13-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2e2a94463207608d1cba0c36ea4f882e0b409e28590e2dbc48961dea59f170c0"}, 182 | {file = "pytomlpp-1.0.13-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70b43d955fb53ca9186dafc95fa24ea783870a226bf875b833c25dd5865ed526"}, 183 | {file = "pytomlpp-1.0.13-cp37-cp37m-win_amd64.whl", hash = "sha256:7832307f4bc0518f78f0afc615cb8135400522be8676eff47ece5dfbca240115"}, 184 | {file = "pytomlpp-1.0.13-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d50395394963529940703638b422b19d84c5a003a7eb0106ad7c7347ad6f20c0"}, 185 | {file = "pytomlpp-1.0.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:73062c46d315e1f3cdf631fb8761e0981dda0df346723ca50355c4311167fbfa"}, 186 | {file = "pytomlpp-1.0.13-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2fe54bcd5911b33410a7a7e9cad66d016b056b331cfdf9e5e9d8b404339b1003"}, 187 | {file = "pytomlpp-1.0.13-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88facf9533603b8e2a502f4485641dcb3121d20ccff78f1167b38efc1c7eb9a4"}, 188 | {file = "pytomlpp-1.0.13-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce78fab1987ff48d42e4f4759d765dbf183824a56862f35f500ce0cfc974b5ef"}, 189 | {file = "pytomlpp-1.0.13-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0cf58c70c6cf97d8f98f1931c168b5a2b4c894e5bfd46bd574f0ea0668e8d2b2"}, 190 | {file = "pytomlpp-1.0.13-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b95933988d11d6b54beb1dbf5d13b7afb4f60c2d014dfaaa5c7df44393e23537"}, 191 | {file = "pytomlpp-1.0.13-cp38-cp38-win_amd64.whl", hash = "sha256:a43b2be6182d56914e9cf8aea25cd756b6c7415395ed223b7fc270582a0a4fd2"}, 192 | {file = "pytomlpp-1.0.13-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2fea0318339407d0a9a097d63e9c020cc1bdb0b2de90b5ba6cfb3eabfdbbdfd1"}, 193 | {file = "pytomlpp-1.0.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98510ef5b92f8f7c5e42280948301f542f0086c39bf879f140e111a987f521aa"}, 194 | {file = "pytomlpp-1.0.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8287ded4f27d9f54d017e996480e95ebcf9b2fd8381d4bc755f39fc0b2f70629"}, 195 | {file = "pytomlpp-1.0.13-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f931803e4f48df82ac86ddc075a16635d57018bbac779e0258c896544f5e8ec6"}, 196 | {file = "pytomlpp-1.0.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20a1f6584814f7c9db63f1f590868788aff86201e2b49e89f76772346b79606a"}, 197 | {file = "pytomlpp-1.0.13-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:abbca560310f4bc1f0dea15e77042da03d2a9437ffc239fd949fdcb4302bd85b"}, 198 | {file = "pytomlpp-1.0.13-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:62150ce6f7a47a135ba4375a8075eb321d1064ea2295f59fd92d5d148b7093f0"}, 199 | {file = "pytomlpp-1.0.13-cp39-cp39-win_amd64.whl", hash = "sha256:b5e89ea80cd25732a2789d67075b72d64133fdf13490aa058abfe9e4964880e4"}, 200 | {file = "pytomlpp-1.0.13-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4710c72456c10a90e58084174312abef8f9652b0f91c240c008903c1bd99814d"}, 201 | {file = "pytomlpp-1.0.13-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b59acc12339a992404289ab7294f28ba06c7df3c2562e81d316a0e744ab4103b"}, 202 | {file = "pytomlpp-1.0.13-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:252e31a5e013a74b898784f4ffb8aa8068e136b910ad11f2af1ee8a5700e6e1e"}, 203 | {file = "pytomlpp-1.0.13-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:09e716c0f462d15f2334cecc736957777dd30f8a5bfa5cf8150679da7577d2fd"}, 204 | {file = "pytomlpp-1.0.13-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:19dbded2995370e802105fa6dce54ed60f79e58b4eb35fee7ef33f1fb5958f6c"}, 205 | {file = "pytomlpp-1.0.13-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f87f6c958309e4c2358b778902c80bd33611d1c392f1abe2c226e3a62909ca4"}, 206 | {file = "pytomlpp-1.0.13-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e285aca948b419301fdda1927723287ef28482752782c44c9ee8c57eae7a1dc8"}, 207 | {file = "pytomlpp-1.0.13-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:aad6ae19c056ea62a43fec82427ad4675b5c773dc255c4bdcf6da659cd7edff6"}, 208 | {file = "pytomlpp-1.0.13-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0e0b34b7a132856567714342e9a622f7be0b4c9bac561a6252f0f85626c1aa4b"}, 209 | {file = "pytomlpp-1.0.13-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac06ca7683f5a2737b3888ea1e38d6968abb24fab703bc7ceccbe589d5420e0c"}, 210 | {file = "pytomlpp-1.0.13-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35225c1d9d674df87b4682f04af97856049351c38822455b78258248d9309363"}, 211 | {file = "pytomlpp-1.0.13-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:dbc9208ac58ea2a9d5ebb77e69d54d146744007f4a704a3f4e56d9881d41ee1c"}, 212 | {file = "pytomlpp-1.0.13.tar.gz", hash = "sha256:a0bd639a8f624d1bdf5b3ea94363ca23dbfef38ab7b5b9348881a84afab434ad"}, 213 | ] 214 | 215 | [[package]] 216 | name = "qtoml" 217 | version = "0.3.1" 218 | description = "New TOML encoder/decoder" 219 | optional = false 220 | python-versions = ">=3.6,<4.0" 221 | files = [ 222 | {file = "qtoml-0.3.1-py3-none-any.whl", hash = "sha256:de4ec4f759a77931cda324a06e2c2fbbb2209372c42f4741c2ab6748f15640b4"}, 223 | {file = "qtoml-0.3.1.tar.gz", hash = "sha256:7f2d0c2c39659d2a408ae93d02a068e0d22eb67e16e474239f7735ff1094b1ba"}, 224 | ] 225 | 226 | [package.dependencies] 227 | attrs = ">=19.3.0,<22.0" 228 | click = ">=7.0,<9.0" 229 | 230 | [[package]] 231 | name = "regex" 232 | version = "2024.5.15" 233 | description = "Alternative regular expression module, to replace re." 234 | optional = false 235 | python-versions = ">=3.8" 236 | files = [ 237 | {file = "regex-2024.5.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a81e3cfbae20378d75185171587cbf756015ccb14840702944f014e0d93ea09f"}, 238 | {file = "regex-2024.5.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b59138b219ffa8979013be7bc85bb60c6f7b7575df3d56dc1e403a438c7a3f6"}, 239 | {file = "regex-2024.5.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0bd000c6e266927cb7a1bc39d55be95c4b4f65c5be53e659537537e019232b1"}, 240 | {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eaa7ddaf517aa095fa8da0b5015c44d03da83f5bd49c87961e3c997daed0de7"}, 241 | {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba68168daedb2c0bab7fd7e00ced5ba90aebf91024dea3c88ad5063c2a562cca"}, 242 | {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e8d717bca3a6e2064fc3a08df5cbe366369f4b052dcd21b7416e6d71620dca1"}, 243 | {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1337b7dbef9b2f71121cdbf1e97e40de33ff114801263b275aafd75303bd62b5"}, 244 | {file = "regex-2024.5.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9ebd0a36102fcad2f03696e8af4ae682793a5d30b46c647eaf280d6cfb32796"}, 245 | {file = "regex-2024.5.15-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9efa1a32ad3a3ea112224897cdaeb6aa00381627f567179c0314f7b65d354c62"}, 246 | {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1595f2d10dff3d805e054ebdc41c124753631b6a471b976963c7b28543cf13b0"}, 247 | {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b802512f3e1f480f41ab5f2cfc0e2f761f08a1f41092d6718868082fc0d27143"}, 248 | {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a0981022dccabca811e8171f913de05720590c915b033b7e601f35ce4ea7019f"}, 249 | {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:19068a6a79cf99a19ccefa44610491e9ca02c2be3305c7760d3831d38a467a6f"}, 250 | {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1b5269484f6126eee5e687785e83c6b60aad7663dafe842b34691157e5083e53"}, 251 | {file = "regex-2024.5.15-cp310-cp310-win32.whl", hash = "sha256:ada150c5adfa8fbcbf321c30c751dc67d2f12f15bd183ffe4ec7cde351d945b3"}, 252 | {file = "regex-2024.5.15-cp310-cp310-win_amd64.whl", hash = "sha256:ac394ff680fc46b97487941f5e6ae49a9f30ea41c6c6804832063f14b2a5a145"}, 253 | {file = "regex-2024.5.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f5b1dff3ad008dccf18e652283f5e5339d70bf8ba7c98bf848ac33db10f7bc7a"}, 254 | {file = "regex-2024.5.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c6a2b494a76983df8e3d3feea9b9ffdd558b247e60b92f877f93a1ff43d26656"}, 255 | {file = "regex-2024.5.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a32b96f15c8ab2e7d27655969a23895eb799de3665fa94349f3b2fbfd547236f"}, 256 | {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10002e86e6068d9e1c91eae8295ef690f02f913c57db120b58fdd35a6bb1af35"}, 257 | {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec54d5afa89c19c6dd8541a133be51ee1017a38b412b1321ccb8d6ddbeb4cf7d"}, 258 | {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10e4ce0dca9ae7a66e6089bb29355d4432caed736acae36fef0fdd7879f0b0cb"}, 259 | {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e507ff1e74373c4d3038195fdd2af30d297b4f0950eeda6f515ae3d84a1770f"}, 260 | {file = "regex-2024.5.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1f059a4d795e646e1c37665b9d06062c62d0e8cc3c511fe01315973a6542e40"}, 261 | {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0721931ad5fe0dda45d07f9820b90b2148ccdd8e45bb9e9b42a146cb4f695649"}, 262 | {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:833616ddc75ad595dee848ad984d067f2f31be645d603e4d158bba656bbf516c"}, 263 | {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:287eb7f54fc81546346207c533ad3c2c51a8d61075127d7f6d79aaf96cdee890"}, 264 | {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:19dfb1c504781a136a80ecd1fff9f16dddf5bb43cec6871778c8a907a085bb3d"}, 265 | {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:119af6e56dce35e8dfb5222573b50c89e5508d94d55713c75126b753f834de68"}, 266 | {file = "regex-2024.5.15-cp311-cp311-win32.whl", hash = "sha256:1c1c174d6ec38d6c8a7504087358ce9213d4332f6293a94fbf5249992ba54efa"}, 267 | {file = "regex-2024.5.15-cp311-cp311-win_amd64.whl", hash = "sha256:9e717956dcfd656f5055cc70996ee2cc82ac5149517fc8e1b60261b907740201"}, 268 | {file = "regex-2024.5.15-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:632b01153e5248c134007209b5c6348a544ce96c46005d8456de1d552455b014"}, 269 | {file = "regex-2024.5.15-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e64198f6b856d48192bf921421fdd8ad8eb35e179086e99e99f711957ffedd6e"}, 270 | {file = "regex-2024.5.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68811ab14087b2f6e0fc0c2bae9ad689ea3584cad6917fc57be6a48bbd012c49"}, 271 | {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ec0c2fea1e886a19c3bee0cd19d862b3aa75dcdfb42ebe8ed30708df64687a"}, 272 | {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0c0c0003c10f54a591d220997dd27d953cd9ccc1a7294b40a4be5312be8797b"}, 273 | {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2431b9e263af1953c55abbd3e2efca67ca80a3de8a0437cb58e2421f8184717a"}, 274 | {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a605586358893b483976cffc1723fb0f83e526e8f14c6e6614e75919d9862cf"}, 275 | {file = "regex-2024.5.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391d7f7f1e409d192dba8bcd42d3e4cf9e598f3979cdaed6ab11288da88cb9f2"}, 276 | {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9ff11639a8d98969c863d4617595eb5425fd12f7c5ef6621a4b74b71ed8726d5"}, 277 | {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4eee78a04e6c67e8391edd4dad3279828dd66ac4b79570ec998e2155d2e59fd5"}, 278 | {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8fe45aa3f4aa57faabbc9cb46a93363edd6197cbc43523daea044e9ff2fea83e"}, 279 | {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d0a3d8d6acf0c78a1fff0e210d224b821081330b8524e3e2bc5a68ef6ab5803d"}, 280 | {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c486b4106066d502495b3025a0a7251bf37ea9540433940a23419461ab9f2a80"}, 281 | {file = "regex-2024.5.15-cp312-cp312-win32.whl", hash = "sha256:c49e15eac7c149f3670b3e27f1f28a2c1ddeccd3a2812cba953e01be2ab9b5fe"}, 282 | {file = "regex-2024.5.15-cp312-cp312-win_amd64.whl", hash = "sha256:673b5a6da4557b975c6c90198588181029c60793835ce02f497ea817ff647cb2"}, 283 | {file = "regex-2024.5.15-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:87e2a9c29e672fc65523fb47a90d429b70ef72b901b4e4b1bd42387caf0d6835"}, 284 | {file = "regex-2024.5.15-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c3bea0ba8b73b71b37ac833a7f3fd53825924165da6a924aec78c13032f20850"}, 285 | {file = "regex-2024.5.15-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bfc4f82cabe54f1e7f206fd3d30fda143f84a63fe7d64a81558d6e5f2e5aaba9"}, 286 | {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5bb9425fe881d578aeca0b2b4b3d314ec88738706f66f219c194d67179337cb"}, 287 | {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64c65783e96e563103d641760664125e91bd85d8e49566ee560ded4da0d3e704"}, 288 | {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf2430df4148b08fb4324b848672514b1385ae3807651f3567871f130a728cc3"}, 289 | {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5397de3219a8b08ae9540c48f602996aa6b0b65d5a61683e233af8605c42b0f2"}, 290 | {file = "regex-2024.5.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:455705d34b4154a80ead722f4f185b04c4237e8e8e33f265cd0798d0e44825fa"}, 291 | {file = "regex-2024.5.15-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2b6f1b3bb6f640c1a92be3bbfbcb18657b125b99ecf141fb3310b5282c7d4ed"}, 292 | {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3ad070b823ca5890cab606c940522d05d3d22395d432f4aaaf9d5b1653e47ced"}, 293 | {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5b5467acbfc153847d5adb21e21e29847bcb5870e65c94c9206d20eb4e99a384"}, 294 | {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e6662686aeb633ad65be2a42b4cb00178b3fbf7b91878f9446075c404ada552f"}, 295 | {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:2b4c884767504c0e2401babe8b5b7aea9148680d2e157fa28f01529d1f7fcf67"}, 296 | {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3cd7874d57f13bf70078f1ff02b8b0aa48d5b9ed25fc48547516c6aba36f5741"}, 297 | {file = "regex-2024.5.15-cp38-cp38-win32.whl", hash = "sha256:e4682f5ba31f475d58884045c1a97a860a007d44938c4c0895f41d64481edbc9"}, 298 | {file = "regex-2024.5.15-cp38-cp38-win_amd64.whl", hash = "sha256:d99ceffa25ac45d150e30bd9ed14ec6039f2aad0ffa6bb87a5936f5782fc1569"}, 299 | {file = "regex-2024.5.15-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13cdaf31bed30a1e1c2453ef6015aa0983e1366fad2667657dbcac7b02f67133"}, 300 | {file = "regex-2024.5.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cac27dcaa821ca271855a32188aa61d12decb6fe45ffe3e722401fe61e323cd1"}, 301 | {file = "regex-2024.5.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7dbe2467273b875ea2de38ded4eba86cbcbc9a1a6d0aa11dcf7bd2e67859c435"}, 302 | {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64f18a9a3513a99c4bef0e3efd4c4a5b11228b48aa80743be822b71e132ae4f5"}, 303 | {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d347a741ea871c2e278fde6c48f85136c96b8659b632fb57a7d1ce1872547600"}, 304 | {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1878b8301ed011704aea4c806a3cadbd76f84dece1ec09cc9e4dc934cfa5d4da"}, 305 | {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4babf07ad476aaf7830d77000874d7611704a7fcf68c9c2ad151f5d94ae4bfc4"}, 306 | {file = "regex-2024.5.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35cb514e137cb3488bce23352af3e12fb0dbedd1ee6e60da053c69fb1b29cc6c"}, 307 | {file = "regex-2024.5.15-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cdd09d47c0b2efee9378679f8510ee6955d329424c659ab3c5e3a6edea696294"}, 308 | {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:72d7a99cd6b8f958e85fc6ca5b37c4303294954eac1376535b03c2a43eb72629"}, 309 | {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a094801d379ab20c2135529948cb84d417a2169b9bdceda2a36f5f10977ebc16"}, 310 | {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c0c18345010870e58238790a6779a1219b4d97bd2e77e1140e8ee5d14df071aa"}, 311 | {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:16093f563098448ff6b1fa68170e4acbef94e6b6a4e25e10eae8598bb1694b5d"}, 312 | {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e38a7d4e8f633a33b4c7350fbd8bad3b70bf81439ac67ac38916c4a86b465456"}, 313 | {file = "regex-2024.5.15-cp39-cp39-win32.whl", hash = "sha256:71a455a3c584a88f654b64feccc1e25876066c4f5ef26cd6dd711308aa538694"}, 314 | {file = "regex-2024.5.15-cp39-cp39-win_amd64.whl", hash = "sha256:cab12877a9bdafde5500206d1020a584355a97884dfd388af3699e9137bf7388"}, 315 | {file = "regex-2024.5.15.tar.gz", hash = "sha256:d3ee02d9e5f482cc8309134a91eeaacbdd2261ba111b0fef3748eeb4913e6a2c"}, 316 | ] 317 | 318 | [[package]] 319 | name = "rtoml" 320 | version = "0.11.0" 321 | description = "" 322 | optional = false 323 | python-versions = ">=3.8" 324 | files = [ 325 | {file = "rtoml-0.11.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:bbea5538d8e7a495878717743225bf261b097b42b17558d7f6876ec8ff700649"}, 326 | {file = "rtoml-0.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec72c71ce1eec6a9a75a59c914644a92afed55625f88086418c8f610646f6c31"}, 327 | {file = "rtoml-0.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4161c01ee5cbf173382d4a55b26fc0d4a00ce68b9252c51cf6ee1a7b15d7922b"}, 328 | {file = "rtoml-0.11.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ca19e750d2becefe482bd4ddcc3395816a0d1e69fbc562a18a0fe2f4b688ec70"}, 329 | {file = "rtoml-0.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:398a17548248d06844e125fd748f5fd507819652866e833db4114bd32fda30f6"}, 330 | {file = "rtoml-0.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb4c11a82a5ae89a4f5793dceed93e186032f1588e8f7ab3ebc48b8b54665a92"}, 331 | {file = "rtoml-0.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:384a580433edf011d8cdc683996866d0809721b7b9ab055c279a4bd51b5c7300"}, 332 | {file = "rtoml-0.11.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c55295fac0e18af5a7b91dc8a308dd50fe0dbf26e439a86ff674290823ed010d"}, 333 | {file = "rtoml-0.11.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b00ea0a3f14af57f1bdbb89b71f59c912d4682f9aa6a1a3a6729d210a599c64f"}, 334 | {file = "rtoml-0.11.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2bde7832c792a95d3c2f1a55833d5f5ae745c701ff0c09ad3e935146670403ed"}, 335 | {file = "rtoml-0.11.0-cp310-none-win32.whl", hash = "sha256:dc350452dcdd69b5c247d6a6951f4253a5bec93b9da621da96ced4e1edc1d42d"}, 336 | {file = "rtoml-0.11.0-cp310-none-win_amd64.whl", hash = "sha256:1836518858936ab435fff23805120760b759c7fa441d7150cdc6744d8d28e0ad"}, 337 | {file = "rtoml-0.11.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:abf9a0f729040f99c7b1734c433919b19314843d0f1d597f2d82e448e210c60f"}, 338 | {file = "rtoml-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:48a3504d58d9a0178947cf988841f2a771b63bb90155ad20ba4da93318ca2195"}, 339 | {file = "rtoml-0.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56421e722d61ff09df0ae1d447ed1f8f109877281a091da1062165093bfe6291"}, 340 | {file = "rtoml-0.11.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1be1b24c51a0a79beaf13e69603a556a165f27c92c93d284408e44686f82fbc"}, 341 | {file = "rtoml-0.11.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec5837a2e0e0e3084d243914c11aff602f6840e5c07a7b2e1ca0c89464ed7079"}, 342 | {file = "rtoml-0.11.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:765bb5f226f7694d66af39f23d79b65f6b30744ee1c1396acff303b772f4ba1d"}, 343 | {file = "rtoml-0.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd6dfbc1830055290e8ee9f7ffb7465106536f54c1465e08c980c38147e3bcf4"}, 344 | {file = "rtoml-0.11.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:068131643b18ee839f1d5798ef5b347d04e101b8b9398b774fc839e6231c63c7"}, 345 | {file = "rtoml-0.11.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:042fc83c225b1075b6e717d156c4c7ecdab255127f875f6188fb062d72e59c5f"}, 346 | {file = "rtoml-0.11.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bcab62bbf210434dcab736c658911d0182afe33b3920b051978c04ca442504a9"}, 347 | {file = "rtoml-0.11.0-cp311-none-win32.whl", hash = "sha256:8edbf3f0498287557a0a170c9a9331dfb69058729968578c37aad19977ba5722"}, 348 | {file = "rtoml-0.11.0-cp311-none-win_amd64.whl", hash = "sha256:d5e3d8d73a220d06bf2359b13572bdbd198e66f45aeac1e84058e448dc34f85e"}, 349 | {file = "rtoml-0.11.0-cp311-none-win_arm64.whl", hash = "sha256:806e3893555657012fe89774e62999e8cac50df53a7bd27f7c838f606397c3f7"}, 350 | {file = "rtoml-0.11.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:34c4fd6e6465e6de84ac499e19ae3e4b0d4cd3f9763d1a72a75bd2ef4d0e466a"}, 351 | {file = "rtoml-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0095bc482788d39c6c3e2f9f3a6f7a8149ee1cefe624129e2afc90512d8f16b7"}, 352 | {file = "rtoml-0.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d90b06a36325c9ae6e273669d811aa0e6b8ae41fa9ffe3949f02ef1870dc0ad5"}, 353 | {file = "rtoml-0.11.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:902148f2bd13b4c64d2c5ed576910c5a0a2c86dc052f4e31ca1855131ee157c8"}, 354 | {file = "rtoml-0.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d84bbdad28e24f66b8ad632fb7cbec64b921fdff48bf57f4f7179648726f7e54"}, 355 | {file = "rtoml-0.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b1d4d4b8d765cf5228e5def9c39837d5b70c6e51422e05b975898c8a765fd1e"}, 356 | {file = "rtoml-0.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b900dcdf2068105b81b1ded3342dad952e3ea7d4a949473a690c686116a1730"}, 357 | {file = "rtoml-0.11.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4a491ad282b918292812518a61f432d6becf1b4a965d9c6595b7f00b1b722e61"}, 358 | {file = "rtoml-0.11.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d5ccbe43af82f17efed273b517e29febbd8225dab9a314f5728465b61a34781a"}, 359 | {file = "rtoml-0.11.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4125260584290461825abac23dafb34048dcbedaf9fafea913b5f0b1691c1ad0"}, 360 | {file = "rtoml-0.11.0-cp312-none-win32.whl", hash = "sha256:6e42385f510458f68587051a533cd0161653e1f2b381400a4873eab6f706940b"}, 361 | {file = "rtoml-0.11.0-cp312-none-win_amd64.whl", hash = "sha256:fdce3405e50ba73a45acdf36a793e3563da04fc913c53d54852c5d6cf1eb6d25"}, 362 | {file = "rtoml-0.11.0-cp312-none-win_arm64.whl", hash = "sha256:affc854919b46555f37d1408cb67f06f7453744bf1c5c1c53b69ed8b5227a28d"}, 363 | {file = "rtoml-0.11.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:377d24e0c9aac0aa92ff7f4f48d90298c005e4b01a97b532d4a9cc18c180a29c"}, 364 | {file = "rtoml-0.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a557bd7c40afbdad121f952dd4b87a432c8fc50fea795b44889b84d5f8adea7e"}, 365 | {file = "rtoml-0.11.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91c9d5b3a8f7efcee02cd250cedebe0b02ca65abd7dfc20b5f69a16b68808d53"}, 366 | {file = "rtoml-0.11.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb53b953925ea0f59d1892307503ceb21b66724b7929186d9dc7a53599075b51"}, 367 | {file = "rtoml-0.11.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4aab5871b86e557087d2042136cde2528ad5d2fd34e4261b43a4e66dcaaacd1d"}, 368 | {file = "rtoml-0.11.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:992646793a06fc6925171301440f56d7c44cd8302a0f1f573935e92e30f50a8e"}, 369 | {file = "rtoml-0.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8dd9e35799ad86aba60a742c73bfe036210f2d504ab40e623d8360e43cc34f4"}, 370 | {file = "rtoml-0.11.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0658ed9376fddb96803e2d042d1b86056f9e8a81e31ee24cca638553ed3aee25"}, 371 | {file = "rtoml-0.11.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d15df762e4d513e979158c7dd670bf6df5856b768ac0107bfef0330865229f1"}, 372 | {file = "rtoml-0.11.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d931274a54fc770601735f611f03bc44032226b549536a16c2479a455a86e1d2"}, 373 | {file = "rtoml-0.11.0-cp38-none-win32.whl", hash = "sha256:e8dfb1460d3fc0501720dbee32acf1100d8ca4d02fbf634e956587f8d969632f"}, 374 | {file = "rtoml-0.11.0-cp38-none-win_amd64.whl", hash = "sha256:38f805d8030b276b3ba19561cde1ac0c74b680b550e284a9e85f4d026be7d9ca"}, 375 | {file = "rtoml-0.11.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:15511219ff53406b42da8fb14ecf1ab580ff1bdbdb5d76405df3d97c155fb997"}, 376 | {file = "rtoml-0.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:faeb038772540ad3344fd73329eae3a9e84614c6b25c848dad3258647b3f4abf"}, 377 | {file = "rtoml-0.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52b410561af66ca5689a5c21b6bd31036d5839bb97b06cec71ac1147109f8fb6"}, 378 | {file = "rtoml-0.11.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fe9938d4793b873e53bb6fc12e4a5b88820b8596a3eaabee67cc3563f080eb85"}, 379 | {file = "rtoml-0.11.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5178ebb7442d9e24448e76d4d32f07aa75acbd765563a2accf50cfb262163b18"}, 380 | {file = "rtoml-0.11.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d0e6f5c936a5d5e2c26d2443a8c861448885cc099f351982c3f7f3a0d1ce114"}, 381 | {file = "rtoml-0.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b750450e1ac63eb43dfbba3612dbb1830b6d90cc22c6ea78d7f2322ec698727d"}, 382 | {file = "rtoml-0.11.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3d41b865acf22bd547e771dfac3394e7dbf14586f236f20b70d3058a89352027"}, 383 | {file = "rtoml-0.11.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8362905a25f3f3b0a6ca015877811d1bd4131ac3dd16f8acd4229e3f40ebd2e5"}, 384 | {file = "rtoml-0.11.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e42a8536bce3ce91ee8ea867caf5bbc1caedbf477820b14498e85e6d539592aa"}, 385 | {file = "rtoml-0.11.0-cp39-none-win32.whl", hash = "sha256:84b83f2c57a8c4f65c0a6f0c3dbbe1ecb9082757b1fde02b2eb4781040483725"}, 386 | {file = "rtoml-0.11.0-cp39-none-win_amd64.whl", hash = "sha256:fe6594d1a2f1948abf74ecfa24425b4ed7817b93c3daeb1ec779b5fbf935f388"}, 387 | {file = "rtoml-0.11.0.tar.gz", hash = "sha256:a1d1ec36261c47169934a6c0f713a6cdf917604b3f72a72a809c3a68384255d4"}, 388 | ] 389 | 390 | [[package]] 391 | name = "six" 392 | version = "1.16.0" 393 | description = "Python 2 and 3 compatibility utilities" 394 | optional = false 395 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 396 | files = [ 397 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 398 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 399 | ] 400 | 401 | [[package]] 402 | name = "toml" 403 | version = "0.10.2" 404 | description = "Python Library for Tom's Obvious, Minimal Language" 405 | optional = false 406 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 407 | files = [ 408 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 409 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 410 | ] 411 | 412 | [[package]] 413 | name = "tomli" 414 | version = "2.0.1" 415 | description = "A lil' TOML parser" 416 | optional = false 417 | python-versions = ">=3.7" 418 | files = [ 419 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 420 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 421 | ] 422 | 423 | [[package]] 424 | name = "tomli-w" 425 | version = "1.0.0" 426 | description = "A lil' TOML writer" 427 | optional = false 428 | python-versions = ">=3.7" 429 | files = [ 430 | {file = "tomli_w-1.0.0-py3-none-any.whl", hash = "sha256:9f2a07e8be30a0729e533ec968016807069991ae2fd921a78d42f429ae5f4463"}, 431 | {file = "tomli_w-1.0.0.tar.gz", hash = "sha256:f463434305e0336248cac9c2dc8076b707d8a12d019dd349f5c1e382dd1ae1b9"}, 432 | ] 433 | 434 | [[package]] 435 | name = "tomlkit" 436 | version = "0.12.5" 437 | description = "Style preserving TOML library" 438 | optional = false 439 | python-versions = ">=3.7" 440 | files = [ 441 | {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, 442 | {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, 443 | ] 444 | 445 | [[package]] 446 | name = "zipp" 447 | version = "3.19.2" 448 | description = "Backport of pathlib-compatible object wrapper for zip files" 449 | optional = false 450 | python-versions = ">=3.8" 451 | files = [ 452 | {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, 453 | {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, 454 | ] 455 | 456 | [package.extras] 457 | doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 458 | test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] 459 | 460 | [metadata] 461 | lock-version = "2.0" 462 | python-versions = "^3.9" 463 | content-hash = "19f12b253b74488979ac892d93fa637e710d96fe3681fe51329447ec2eae4940" 464 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "toml-bench" 3 | version = "0.4.0" 4 | description = "Benchmarking for python toml libraries" 5 | authors = ["pwwang "] 6 | license = "MIT" 7 | 8 | [tool.poetry.dependencies] 9 | python = "^3.9" 10 | benchwork = "^0.0.2" 11 | toml = "^0.10.2" 12 | rtoml = "^0.11.0" 13 | # pytomlpp does not support python 3.11 yet 14 | pytomlpp = {version = "^1.0.13", python = "<3.11"} 15 | tomli = "^2.0.1" 16 | tomli-w = "^1.0.0" 17 | qtoml = "^0.3.1" 18 | tomlkit = "^0.12.5" 19 | python-dateutil = "^2.8.2" 20 | importlib-metadata = "^7.2.1" 21 | # required by tomlkit, but cannot be inferred by poetry 22 | regex = "^2024.5.15" 23 | argx = "^0.2.10" 24 | 25 | [tool.poetry.dev-dependencies] 26 | 27 | [tool.poetry.scripts] 28 | toml-bench = "toml_bench.__main__:main" 29 | 30 | [build-system] 31 | requires = ["poetry-core>=1.0.0"] 32 | build-backend = "poetry.core.masonry.api" 33 | 34 | [tool.black] 35 | line-length = 80 36 | target-version = ['py37', 'py38', 'py39'] 37 | include = '\.pyi?$' 38 | -------------------------------------------------------------------------------- /reports/with_toml-test_v1.2.0.md: -------------------------------------------------------------------------------- 1 | # Report 2 | 3 | ## Version 4 | 5 | The verions of the packages tested in this report. 6 | 7 | | |Version| 8 | |-|-----------------------| 9 | |toml|0.10.2| 10 | |tomli/tomli_w|2.0.1; **tomli_w**: 1.0.0| 11 | |tomlkit|0.12.5| 12 | |rtoml|0.11.0| 13 | |qtoml|0.3.1| 14 | |tomllib|(Python 3.12.2)| 15 | 16 | ## Dumping `None` value 17 | 18 | How the package dumps `None` value in python 19 | 20 | Literally `.dumps(None)` 21 | 22 | 23 | | |Dumped value or error| 24 | |-|-----------------------| 25 | |toml|'NoneType' object is not iterable| 26 | |tomli/tomli_w|'NoneType' object has no attribute 'items'| 27 | |tomlkit|Expecting Mapping or TOML Container, given| 28 | |rtoml|"null"
---
rtoml v0.11+ supports dumping None to a desired string:
`rtoml.dumps(data, none_value='@None')`:
"@None"| 29 | |qtoml|'NoneType' object has no attribute 'items'| 30 | |tomllib|module 'tomllib' has no attribute 'dumps'| 31 | 32 | ## Dumping key-`None` pair 33 | 34 | How the package dumps key-value pair with value `None` 35 | 36 | Literally `.dumps({"key": None})` 37 | 38 | 39 | | |Dumped value or error| 40 | |-|-----------------------| 41 | |toml|| 42 | |tomli/tomli_w|Object of type is not TOML serializable| 43 | |tomlkit|Invalid type | 44 | |rtoml|key = "null"

---
rtoml v0.11+ supports dumping None to a desired string:
`rtoml.dumps(data, none_value='@None')`:
key = "@None"
| 45 | |qtoml|TOML cannot encode None| 46 | |tomllib|module 'tomllib' has no attribute 'dumps'| 47 | 48 | ## Dumping list with `None` value 49 | 50 | How the package dumps a list with `None` value in it. 51 | 52 | Literally `.dumps({"key": [1, 2, 3, None, 5]})` 53 | 54 | 55 | | |Dumped value or error| 56 | |-|-----------------------| 57 | |toml|key = [ 1, 2, 3, "None", 5,]
| 58 | |tomli/tomli_w|Object of type is not TOML serializable| 59 | |tomlkit|Invalid type | 60 | |rtoml|key = [1, 2, 3, "null", 5]

---
rtoml v0.11+ supports dumping None to a desired string:
`rtoml.dumps(data, none_value='@None')`:
key = [1, 2, 3, "@None", 5]
| 61 | |qtoml|bad type '' for dump_value| 62 | |tomllib|module 'tomllib' has no attribute 'dumps'| 63 | 64 | ## Loading `None`-like values 65 | 66 | How the package loads `None`-like value in string 67 | 68 | Literally `.loads('v1 = "null" v2 = "None"')` 69 | 70 | 71 | | |Loaded as| 72 | |-|-----------------------| 73 | |toml|{'v1': 'null', 'v2': 'None'}| 74 | |tomli/tomli_w|module 'tomli_w' has no attribute 'loads'| 75 | |tomlkit|{'v1': 'null', 'v2': 'None'}| 76 | |rtoml|{'v1': 'null', 'v2': 'None'}
---
rtoml v0.11+ supports loading custom None values:
`rtoml.loads(data, none_value='None')`:
{'v1': 'null', 'v2': None}
`rtoml.loads(data, none_value='null')`:
{'v1': None, 'v2': 'None'}| 77 | |qtoml|{'v1': 'null', 'v2': 'None'}| 78 | |tomllib|{'v1': 'null', 'v2': 'None'}| 79 | 80 | ## Dumping a heterogenous array 81 | 82 | How the package dumps a python dictionary with a heterogenous array. 83 | 84 | Literally `.dumps({"v": [1, 1.2, True, "string"]})` 85 | 86 | 87 | | |Dumped value or error| 88 | |-|-----------------------| 89 | |toml|v = \[ 1, 1.2, true, "string",\]
| 90 | |tomli/tomli_w|v = \[
    1,
    1.2,
    true,
    "string",
\]
| 91 | |tomlkit|v = \[1, 1.2, true, "string"\]
| 92 | |rtoml|v = \[1, 1.2, true, "string"\]
| 93 | |qtoml|v = \[1, 1.2, true, 'string'\]
| 94 | |tomllib|Dumping not supported| 95 | 96 | ## Loading a heterogenous array 97 | 98 | How the package loads a toml string with a heterogenous array. 99 | 100 | Literally `.loads('v = [1, 1.2, True, "string"]')` 101 | 102 | 103 | | |Loaded as| 104 | |-|-----------------------| 105 | |toml|Not a homogeneous array (line 2 column 1 char 1)| 106 | |tomli/tomli_w|`{'v': [1, 1.2, True, 'string']}`| 107 | |tomlkit|`{'v': [1, 1.2, True, 'string']}`| 108 | |rtoml|`{'v': [1, 1.2, True, 'string']}`| 109 | |qtoml|`{'v': [1, 1.2, True, 'string']}`| 110 | |tomllib|`{'v': [1, 1.2, True, 'string']}`| 111 | 112 | ## Dumping a nested array 113 | 114 | How the package dumps a python dictionary with a nested array. 115 | 116 | Literally `.dumps({"v": [[1], [1, 2]]})` 117 | 118 | 119 | | |Dumped value or error| 120 | |-|-----------------------| 121 | |toml|v = \[ \[ 1,\], \[ 1, 2,\],\]
| 122 | |tomli/tomli_w|v = \[
    \[
        1,
    \],
    \[
        1,
        2,
    \],
\]
| 123 | |tomlkit|v = \[\[1\], \[1, 2\]\]
| 124 | |rtoml|v = \[\[1\], \[1, 2\]\]
| 125 | |qtoml|v = \[\[1\], \[1, 2\]\]
| 126 | |tomllib|Dumping not supported| 127 | 128 | ## Loading a nested array 129 | 130 | How the package loads a toml string with a nested array. 131 | 132 | Literally `.loads('v = [[1], [1, 2]]')` 133 | 134 | 135 | | |Loaded as| 136 | |-|-----------------------| 137 | |toml|`{'v': [[1], [1, 2]]}`| 138 | |tomli/tomli_w|`{'v': [[1], [1, 2]]}`| 139 | |tomlkit|`{'v': [[1], [1, 2]]}`| 140 | |rtoml|`{'v': [[1], [1, 2]]}`| 141 | |qtoml|`{'v': [[1], [1, 2]]}`| 142 | |tomllib|`{'v': [[1], [1, 2]]}`| 143 | 144 | ## Dumping keeps order of keys? 145 | 146 | Whether the package preserves the order of the keys while dumps 147 | a python dictionary. 148 | 149 | Thus, whether `.dumps({"c": 1, "a": 2, "b": 3})` yields a string 150 | like `c = 1\na = 2\nb = 3\n`. 151 | 152 | 153 | | |Order kept?| 154 | |-|-----------------------| 155 | |toml|Kept| 156 | |tomli/tomli_w|Kept| 157 | |tomlkit|Kept| 158 | |rtoml|Kept| 159 | |qtoml|Kept| 160 | |tomllib|Dumping not supported| 161 | 162 | ## Loading keeps order of keys? 163 | 164 | Whether the package preserves the order of the keys 165 | while loads a TOML string. 166 | 167 | Thus, whether `.loads('c = 1\na = 2\nb = 3\n')` yields 168 | a dictionary with keys in the order of `['c', 'a', 'b']`. 169 | 170 | 171 | | |Order kept?| 172 | |-|-----------------------| 173 | |toml|Kept| 174 | |tomli/tomli_w|Kept| 175 | |tomlkit|Kept| 176 | |rtoml|Kept| 177 | |qtoml|Kept| 178 | |tomllib|Kept| 179 | 180 | ## Dumping unicode 181 | 182 | How the package dumps Unicode in python 183 | 184 | Literally, `.dumps({"你好": "世界"})` 185 | 186 | 187 | | |Dumped value| 188 | |-|-----------------------| 189 | |toml|"你好" = "世界"
| 190 | |tomli/tomli_w|"你好" = "世界"
| 191 | |tomlkit|"你好" = "世界"
| 192 | |rtoml|"你好" = "世界"
| 193 | |qtoml|'你好' = '世界'
| 194 | |tomllib|Dumping not supported| 195 | 196 | ## Loaded unicode 197 | 198 | How the package loads a file with unicode. 199 | 200 | The file was created by: 201 | 202 | ```python 203 | # Create a file with unicode content 204 | with open(self.datafile, "w", encoding="utf-8") as f: 205 | f.write('"你好" = "世界"\n') 206 | 207 | # Use `.load()` to load the file 208 | with open(self.datafile, "r", encoding="utf-8") as f: 209 | loaded = .load(f) 210 | ``` 211 | 212 | 213 | | |Loaded as| 214 | |-|-----------------------| 215 | |toml|{'你好': '世界'}| 216 | |tomli/tomli_w|File must be opened in binary mode, e.g. use `open('foo.toml', 'rb')`
When loaded with `rb`:
{'你好': '世界'}| 217 | |tomlkit|{'你好': '世界'}| 218 | |rtoml|{'你好': '世界'}| 219 | |qtoml|{'你好': '世界'}| 220 | |tomllib|File must be opened in binary mode, e.g. use `open('foo.toml', 'rb')`
When loaded with `rb`:
{'你好': '世界'}| 221 | 222 | ## Compliance with valid tests in toml-test 223 | 224 | Test the compliance with the standard test suites for valid toml files 225 | here: 226 | 227 | > https://github.com/BurntSushi/toml-test/ 228 | 229 | The tests come up with a JSON counterpart that can be used to valid whether 230 | loading the toml file yields the same result as the JSON counterpart. 231 | 232 | 233 | | |Result (toml-test v1.2.0)| 234 | |-|-----------------------| 235 | |toml|[datetime/local-time.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/datetime/local-time.toml) Parsed as unexpected data.
[datetime/datetime.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/datetime/datetime.toml) Parsed as unexpected data.
[comment/tricky.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/comment/tricky.toml) Parsed as unexpected data.
[key/dotted.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/key/dotted.toml) Found invalid character in key name: '"'. Try quoting the key name. (line 12 column 11 char 245)
[key/escapes.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/key/escapes.toml) Parsed as unexpected data.
[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/string/escape-esc.toml) Reserved escape sequence used (line 1 column 1 char 0)
[float/zero.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/float/zero.toml) Weirdness with leading zeroes or underscores in your number. (line 4 column 1 char 47)
[array/mixed-int-string.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/array/mixed-int-string.toml) Not a homogeneous array (line 1 column 1 char 0)
[array/nested-double.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/array/nested-double.toml) Not a homogeneous array (line 1 column 1 char 0)
[array/mixed-int-float.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/array/mixed-int-float.toml) Not a homogeneous array (line 1 column 1 char 0)
[array/mixed-string-table.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/array/mixed-string-table.toml) list index out of range
[array/mixed-int-array.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/array/mixed-int-array.toml) Not a homogeneous array (line 1 column 1 char 0)
[inline-table/multiline.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/inline-table/multiline.toml) Invalid inline table value encountered (line 1 column 1 char 0)
[inline-table/key-dotted.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/inline-table/key-dotted.toml) Parsed as unexpected data.
*86/100 (86.00%) passed*| 236 | |tomli/tomli_w|[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/string/escape-esc.toml) Unescaped '\' in a string (at line 1, column 10)
*99/100 (99.00%) passed*| 237 | |tomlkit|[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/string/escape-esc.toml) Invalid character 'e' in string at line 1 col 8
*99/100 (99.00%) passed*| 238 | |rtoml|[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/string/escape-esc.toml) invalid escape character in string: `e` at line 1 column 9
*99/100 (99.00%) passed*| 239 | |qtoml|[datetime/milliseconds.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/datetime/milliseconds.toml) Didn't find expected newline (line 2, column 27)
[datetime/datetime.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/datetime/datetime.toml) Didn't find expected newline (line 2, column 18)
[comment/tricky.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/comment/tricky.toml) can't parse type (line 11, column 7)
[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/string/escape-esc.toml) \e not a valid escape (line 1, column 33)
[string/multiline-quotes.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/string/multiline-quotes.toml) Didn't find expected newline (line 4, column 26)
*95/100 (95.00%) passed*| 240 | |tomllib|[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//valid/string/escape-esc.toml) Unescaped '\' in a string (at line 1, column 10)
*99/100 (99.00%) passed*| 241 | 242 | ## Compliance with invalid tests in toml-test 243 | 244 | Test the compliance with the standard test suites for invalid toml files 245 | here: 246 | 247 | > https://github.com/BurntSushi/toml-test/ 248 | 249 | - `Not OK`: The toml file is parsed without error, but expected to fail. 250 | - `OK`: All files are failed to parse, as expected. Showing the last 251 | parsing error. 252 | 253 | 254 | | |Result (toml-test v1.2.0)| 255 | |-|-----------------------| 256 | |toml|Not OK: [integer/double-sign-plus.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/integer/double-sign-plus.toml) incorrectly parsed.
Not OK: [integer/us-after-bin.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/integer/us-after-bin.toml) incorrectly parsed.
Not OK: [integer/double-sign-nex.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/integer/double-sign-nex.toml) incorrectly parsed.
Not OK: [integer/us-after-hex.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/integer/us-after-hex.toml) incorrectly parsed.
Not OK: [integer/us-after-oct.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/integer/us-after-oct.toml) incorrectly parsed.
Not OK: [control/comment-del.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/control/comment-del.toml) incorrectly parsed.
Not OK: [control/string-del.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/control/string-del.toml) incorrectly parsed.
Not OK: [control/multi-us.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/control/multi-us.toml) incorrectly parsed.
Not OK: [control/comment-lf.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/control/comment-lf.toml) incorrectly parsed.
Not OK: [control/comment-null.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/control/comment-null.toml) incorrectly parsed.
Not OK: *36 more items incorrectly parsed.*
*177/223 (79.37%) passed*| 257 | |tomli/tomli_w|OK: [inline-table/linebreak-1.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/inline-table/linebreak-1.toml) Unclosed inline table (at line 3, column 18)
*223/223 (100%) passed*| 258 | |tomlkit|Not OK: [control/comment-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/control/comment-cr.toml) incorrectly parsed.
Not OK: [control/bare-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/control/bare-cr.toml) incorrectly parsed.
Not OK: [table/append-with-dotted-keys-1.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/table/append-with-dotted-keys-1.toml) incorrectly parsed.
Not OK: [table/append-with-dotted-keys-2.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/table/append-with-dotted-keys-2.toml) incorrectly parsed.
*219/223 (98.21%) passed*| 259 | |rtoml|Not OK: [integer/positive-hex.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/integer/positive-hex.toml) incorrectly parsed.
Not OK: [integer/positive-bin.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/integer/positive-bin.toml) incorrectly parsed.
Not OK: [control/comment-del.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/control/comment-del.toml) incorrectly parsed.
Not OK: [control/comment-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/control/comment-cr.toml) incorrectly parsed.
Not OK: [control/bare-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/control/bare-cr.toml) incorrectly parsed.
*218/223 (97.76%) passed*| 260 | |qtoml|Not OK: [control/comment-del.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/control/comment-del.toml) incorrectly parsed.
Not OK: [control/comment-lf.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/control/comment-lf.toml) incorrectly parsed.
Not OK: [control/comment-null.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/control/comment-null.toml) incorrectly parsed.
Not OK: [control/comment-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/control/comment-cr.toml) incorrectly parsed.
Not OK: [control/comment-us.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/control/comment-us.toml) incorrectly parsed.
Not OK: [control/bare-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/control/bare-cr.toml) incorrectly parsed.
Not OK: [table/append-with-dotted-keys-1.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/table/append-with-dotted-keys-1.toml) incorrectly parsed.
Not OK: [table/duplicate-key-dotted-table.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/table/duplicate-key-dotted-table.toml) incorrectly parsed.
Not OK: [table/append-with-dotted-keys-2.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/table/append-with-dotted-keys-2.toml) incorrectly parsed.
Not OK: [table/duplicate-key-dotted-table2.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/table/duplicate-key-dotted-table2.toml) incorrectly parsed.
Not OK: *2 more items incorrectly parsed.*
*211/223 (94.62%) passed*| 261 | |tomllib|OK: [inline-table/linebreak-1.toml](https://github.com/BurntSushi/toml-test/blob/v1.2.0/tests//invalid/inline-table/linebreak-1.toml) Unclosed inline table (at line 3, column 18)
*223/223 (100%) passed*| 262 | 263 | ## Compliance with valid tests in python tomllib test data 264 | 265 | Test the compliance with python tomllib test data (since python 3.11) 266 | for valid toml files here: 267 | 268 | > https://github.com/python/cpython/tree/3.11/Lib/test/test_tomllib/data/valid 269 | 270 | The tests come up with a JSON counterpart that can be used to valid whether 271 | loading the toml file yields the same result as the JSON counterpart. 272 | 273 | 274 | | |Result (cpython tag 3.12.4)| 275 | |-|-----------------------| 276 | |toml|[apostrophes-in-literal-string.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/apostrophes-in-literal-string.toml) Unbalanced quotes (line 1 column 50 char 49)
[five-quotes.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/five-quotes.toml) Unterminated string found. Reached end of file. (line 7 column 1 char 97)
[dates-and-times/datetimes.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/dates-and-times/datetimes.toml) Parsed as unexpected data.
[multiline-basic-str/ends-in-whitespace-escape.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/multiline-basic-str/ends-in-whitespace-escape.toml) Reserved escape sequence used (line 6 column 1 char 28)
*8/12 (66.67%) passed*| 277 | |tomli/tomli_w|OK, *12/12 (100%) passed*| 278 | |tomlkit|OK, *12/12 (100%) passed*| 279 | |rtoml|OK, *12/12 (100%) passed*| 280 | |qtoml|[apostrophes-in-literal-string.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/apostrophes-in-literal-string.toml) Didn't find expected newline (line 3, column 3)
[five-quotes.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/five-quotes.toml) Didn't find expected newline (line 3, column 3)
[dates-and-times/datetimes.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/dates-and-times/datetimes.toml) Didn't find expected newline (line 1, column 19)
*9/12 (75.00%) passed*| 281 | |tomllib|OK, *12/12 (100%) passed*| 282 | 283 | ## Compliance with invalid tests in python tomllib test data 284 | 285 | Test the compliance with python tomllib test data (since python 3.11) 286 | for invalid toml files here: 287 | 288 | > https://github.com/python/cpython/tree/main/Lib/test/test_tomllib/data/invalid 289 | 290 | - `Not OK`: The toml file is parsed without error, but expected to fail. 291 | - `OK`: All files are failed to parse, as expected. Showing the last 292 | parsing error. 293 | 294 | 295 | | |Result (cpython tag 3.12.4)| 296 | |-|-----------------------| 297 | |toml|Not OK: [invalid-comment-char.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/invalid-comment-char.toml) incorrectly parsed.
Not OK: [multiline-basic-str/carriage-return.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/multiline-basic-str/carriage-return.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table-with-subtable.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table-with-subtable.toml) incorrectly parsed.
Not OK: [array/unclosed-empty.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/array/unclosed-empty.toml) incorrectly parsed.
Not OK: [array/file-end-after-val.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/array/file-end-after-val.toml) incorrectly parsed.
Not OK: [array/unclosed-after-item.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/array/unclosed-after-item.toml) incorrectly parsed.
Not OK: [inline-table/overwrite-value-in-inner-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/overwrite-value-in-inner-table.toml) incorrectly parsed.
Not OK: [inline-table/unclosed-empty.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/unclosed-empty.toml) incorrectly parsed.
*41/50 (82.00%) passed*| 298 | |tomli/tomli_w|OK: [inline-table/overwrite-implicitly.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/overwrite-implicitly.toml) Cannot overwrite a value (at line 1, column 21)
*50/50 (100%) passed*| 299 | |tomlkit|Not OK: [array-of-tables/overwrite-array-in-parent.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/array-of-tables/overwrite-array-in-parent.toml) incorrectly parsed.
Not OK: [multiline-basic-str/carriage-return.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/multiline-basic-str/carriage-return.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-aot.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-aot.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table-with-subtable.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table-with-subtable.toml) incorrectly parsed.
Not OK: [inline-table/override-val-in-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/override-val-in-table.toml) incorrectly parsed.
*44/50 (88.00%) passed*| 300 | |rtoml|Not OK: [multiline-basic-str/carriage-return.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/multiline-basic-str/carriage-return.toml) incorrectly parsed.
*49/50 (98.00%) passed*| 301 | |qtoml|Not OK: [non-scalar-escaped.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/non-scalar-escaped.toml) incorrectly parsed.
Not OK: [invalid-comment-char.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/invalid-comment-char.toml) incorrectly parsed.
Not OK: [table/redefine-2.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/table/redefine-2.toml) incorrectly parsed.
Not OK: [table/redefine-1.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/table/redefine-1.toml) incorrectly parsed.
Not OK: [multiline-basic-str/carriage-return.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/multiline-basic-str/carriage-return.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table-with-subtable.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table-with-subtable.toml) incorrectly parsed.
Not OK: [inline-table/overwrite-value-in-inner-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/overwrite-value-in-inner-table.toml) incorrectly parsed.
Not OK: [inline-table/override-val-with-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/override-val-with-table.toml) incorrectly parsed.
*41/50 (82.00%) passed*| 302 | |tomllib|OK: [inline-table/overwrite-implicitly.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/overwrite-implicitly.toml) Cannot overwrite a value (at line 1, column 21)
*50/50 (100%) passed*| 303 | 304 | ## Running speed with data provided by `rtoml` 305 | 306 | Test the speed of loading and dumping the loaded using data 307 | provided by `rtoml` 308 | 309 | > https://github.com/samuelcolvin/rtoml/raw/main/tests/data.toml 310 | 311 | 312 | | |Loading speed|Dumping speed| 313 | |-|-|-| 314 | |toml|Excluded (heterogeneous arrays not supported)|Excluded (heterogeneous arrays not supported)| 315 | |tomli/tomli_w|2.09s (5000 iterations)|0.74s (5000 iterations)| 316 | |tomlkit|39.62s (5000 iterations)|0.99s (5000 iterations)| 317 | |rtoml|0.38s (5000 iterations)|0.08s (5000 iterations)| 318 | |qtoml|5.08s (5000 iterations)|1.91s (5000 iterations)| 319 | |tomllib|2.02s (5000 iterations)|Dumping not supported| 320 | 321 | ## Running speed with data provided by `tomli` 322 | 323 | Test the speed of loading and dumping the loaded using data 324 | provided by `tomli` 325 | 326 | > https://github.com/hukkin/tomli/raw/master/benchmark/data.toml 327 | 328 | 329 | | |Loading speed|Dumping speed| 330 | |-|-|-| 331 | |toml|Excluded (heterogeneous arrays not supported)|Excluded (heterogeneous arrays not supported)| 332 | |tomli/tomli_w|1.41s (5000 iterations)|0.46s (5000 iterations)| 333 | |tomlkit|24.56s (5000 iterations)|0.50s (5000 iterations)| 334 | |rtoml|0.32s (5000 iterations)|0.16s (5000 iterations)| 335 | |qtoml|3.68s (5000 iterations)|1.26s (5000 iterations)| 336 | |tomllib|1.42s (5000 iterations)|Dumping not supported| 337 | 338 | -------------------------------------------------------------------------------- /reports/with_toml-test_v1.3.0.md: -------------------------------------------------------------------------------- 1 | # Report 2 | 3 | ## Version 4 | 5 | The verions of the packages tested in this report. 6 | 7 | | |Version| 8 | |-|-----------------------| 9 | |toml|0.10.2| 10 | |tomli/tomli_w|2.0.1; **tomli_w**: 1.0.0| 11 | |tomlkit|0.12.5| 12 | |rtoml|0.11.0| 13 | |qtoml|0.3.1| 14 | |tomllib|(Python 3.12.2)| 15 | 16 | ## Dumping `None` value 17 | 18 | How the package dumps `None` value in python 19 | 20 | Literally `.dumps(None)` 21 | 22 | 23 | | |Dumped value or error| 24 | |-|-----------------------| 25 | |toml|'NoneType' object is not iterable| 26 | |tomli/tomli_w|'NoneType' object has no attribute 'items'| 27 | |tomlkit|Expecting Mapping or TOML Container, given| 28 | |rtoml|"null"
---
rtoml v0.11+ supports dumping None to a desired string:
`rtoml.dumps(data, none_value='@None')`:
"@None"| 29 | |qtoml|'NoneType' object has no attribute 'items'| 30 | |tomllib|module 'tomllib' has no attribute 'dumps'| 31 | 32 | ## Dumping key-`None` pair 33 | 34 | How the package dumps key-value pair with value `None` 35 | 36 | Literally `.dumps({"key": None})` 37 | 38 | 39 | | |Dumped value or error| 40 | |-|-----------------------| 41 | |toml|| 42 | |tomli/tomli_w|Object of type is not TOML serializable| 43 | |tomlkit|Invalid type | 44 | |rtoml|key = "null"

---
rtoml v0.11+ supports dumping None to a desired string:
`rtoml.dumps(data, none_value='@None')`:
key = "@None"
| 45 | |qtoml|TOML cannot encode None| 46 | |tomllib|module 'tomllib' has no attribute 'dumps'| 47 | 48 | ## Dumping list with `None` value 49 | 50 | How the package dumps a list with `None` value in it. 51 | 52 | Literally `.dumps({"key": [1, 2, 3, None, 5]})` 53 | 54 | 55 | | |Dumped value or error| 56 | |-|-----------------------| 57 | |toml|key = [ 1, 2, 3, "None", 5,]
| 58 | |tomli/tomli_w|Object of type is not TOML serializable| 59 | |tomlkit|Invalid type | 60 | |rtoml|key = [1, 2, 3, "null", 5]

---
rtoml v0.11+ supports dumping None to a desired string:
`rtoml.dumps(data, none_value='@None')`:
key = [1, 2, 3, "@None", 5]
| 61 | |qtoml|bad type '' for dump_value| 62 | |tomllib|module 'tomllib' has no attribute 'dumps'| 63 | 64 | ## Loading `None`-like values 65 | 66 | How the package loads `None`-like value in string 67 | 68 | Literally `.loads('v1 = "null" v2 = "None"')` 69 | 70 | 71 | | |Loaded as| 72 | |-|-----------------------| 73 | |toml|{'v1': 'null', 'v2': 'None'}| 74 | |tomli/tomli_w|module 'tomli_w' has no attribute 'loads'| 75 | |tomlkit|{'v1': 'null', 'v2': 'None'}| 76 | |rtoml|{'v1': 'null', 'v2': 'None'}
---
rtoml v0.11+ supports loading custom None values:
`rtoml.loads(data, none_value='None')`:
{'v1': 'null', 'v2': None}
`rtoml.loads(data, none_value='null')`:
{'v1': None, 'v2': 'None'}| 77 | |qtoml|{'v1': 'null', 'v2': 'None'}| 78 | |tomllib|{'v1': 'null', 'v2': 'None'}| 79 | 80 | ## Dumping a heterogenous array 81 | 82 | How the package dumps a python dictionary with a heterogenous array. 83 | 84 | Literally `.dumps({"v": [1, 1.2, True, "string"]})` 85 | 86 | 87 | | |Dumped value or error| 88 | |-|-----------------------| 89 | |toml|v = \[ 1, 1.2, true, "string",\]
| 90 | |tomli/tomli_w|v = \[
    1,
    1.2,
    true,
    "string",
\]
| 91 | |tomlkit|v = \[1, 1.2, true, "string"\]
| 92 | |rtoml|v = \[1, 1.2, true, "string"\]
| 93 | |qtoml|v = \[1, 1.2, true, 'string'\]
| 94 | |tomllib|Dumping not supported| 95 | 96 | ## Loading a heterogenous array 97 | 98 | How the package loads a toml string with a heterogenous array. 99 | 100 | Literally `.loads('v = [1, 1.2, True, "string"]')` 101 | 102 | 103 | | |Loaded as| 104 | |-|-----------------------| 105 | |toml|Not a homogeneous array (line 2 column 1 char 1)| 106 | |tomli/tomli_w|`{'v': [1, 1.2, True, 'string']}`| 107 | |tomlkit|`{'v': [1, 1.2, True, 'string']}`| 108 | |rtoml|`{'v': [1, 1.2, True, 'string']}`| 109 | |qtoml|`{'v': [1, 1.2, True, 'string']}`| 110 | |tomllib|`{'v': [1, 1.2, True, 'string']}`| 111 | 112 | ## Dumping a nested array 113 | 114 | How the package dumps a python dictionary with a nested array. 115 | 116 | Literally `.dumps({"v": [[1], [1, 2]]})` 117 | 118 | 119 | | |Dumped value or error| 120 | |-|-----------------------| 121 | |toml|v = \[ \[ 1,\], \[ 1, 2,\],\]
| 122 | |tomli/tomli_w|v = \[
    \[
        1,
    \],
    \[
        1,
        2,
    \],
\]
| 123 | |tomlkit|v = \[\[1\], \[1, 2\]\]
| 124 | |rtoml|v = \[\[1\], \[1, 2\]\]
| 125 | |qtoml|v = \[\[1\], \[1, 2\]\]
| 126 | |tomllib|Dumping not supported| 127 | 128 | ## Loading a nested array 129 | 130 | How the package loads a toml string with a nested array. 131 | 132 | Literally `.loads('v = [[1], [1, 2]]')` 133 | 134 | 135 | | |Loaded as| 136 | |-|-----------------------| 137 | |toml|`{'v': [[1], [1, 2]]}`| 138 | |tomli/tomli_w|`{'v': [[1], [1, 2]]}`| 139 | |tomlkit|`{'v': [[1], [1, 2]]}`| 140 | |rtoml|`{'v': [[1], [1, 2]]}`| 141 | |qtoml|`{'v': [[1], [1, 2]]}`| 142 | |tomllib|`{'v': [[1], [1, 2]]}`| 143 | 144 | ## Dumping keeps order of keys? 145 | 146 | Whether the package preserves the order of the keys while dumps 147 | a python dictionary. 148 | 149 | Thus, whether `.dumps({"c": 1, "a": 2, "b": 3})` yields a string 150 | like `c = 1\na = 2\nb = 3\n`. 151 | 152 | 153 | | |Order kept?| 154 | |-|-----------------------| 155 | |toml|Kept| 156 | |tomli/tomli_w|Kept| 157 | |tomlkit|Kept| 158 | |rtoml|Kept| 159 | |qtoml|Kept| 160 | |tomllib|Dumping not supported| 161 | 162 | ## Loading keeps order of keys? 163 | 164 | Whether the package preserves the order of the keys 165 | while loads a TOML string. 166 | 167 | Thus, whether `.loads('c = 1\na = 2\nb = 3\n')` yields 168 | a dictionary with keys in the order of `['c', 'a', 'b']`. 169 | 170 | 171 | | |Order kept?| 172 | |-|-----------------------| 173 | |toml|Kept| 174 | |tomli/tomli_w|Kept| 175 | |tomlkit|Kept| 176 | |rtoml|Kept| 177 | |qtoml|Kept| 178 | |tomllib|Kept| 179 | 180 | ## Dumping unicode 181 | 182 | How the package dumps Unicode in python 183 | 184 | Literally, `.dumps({"你好": "世界"})` 185 | 186 | 187 | | |Dumped value| 188 | |-|-----------------------| 189 | |toml|"你好" = "世界"
| 190 | |tomli/tomli_w|"你好" = "世界"
| 191 | |tomlkit|"你好" = "世界"
| 192 | |rtoml|"你好" = "世界"
| 193 | |qtoml|'你好' = '世界'
| 194 | |tomllib|Dumping not supported| 195 | 196 | ## Loaded unicode 197 | 198 | How the package loads a file with unicode. 199 | 200 | The file was created by: 201 | 202 | ```python 203 | # Create a file with unicode content 204 | with open(self.datafile, "w", encoding="utf-8") as f: 205 | f.write('"你好" = "世界"\n') 206 | 207 | # Use `.load()` to load the file 208 | with open(self.datafile, "r", encoding="utf-8") as f: 209 | loaded = .load(f) 210 | ``` 211 | 212 | 213 | | |Loaded as| 214 | |-|-----------------------| 215 | |toml|{'你好': '世界'}| 216 | |tomli/tomli_w|File must be opened in binary mode, e.g. use `open('foo.toml', 'rb')`
When loaded with `rb`:
{'你好': '世界'}| 217 | |tomlkit|{'你好': '世界'}| 218 | |rtoml|{'你好': '世界'}| 219 | |qtoml|{'你好': '世界'}| 220 | |tomllib|File must be opened in binary mode, e.g. use `open('foo.toml', 'rb')`
When loaded with `rb`:
{'你好': '世界'}| 221 | 222 | ## Compliance with valid tests in toml-test 223 | 224 | Test the compliance with the standard test suites for valid toml files 225 | here: 226 | 227 | > https://github.com/BurntSushi/toml-test/ 228 | 229 | The tests come up with a JSON counterpart that can be used to valid whether 230 | loading the toml file yields the same result as the JSON counterpart. 231 | 232 | 233 | | |Result (toml-test v1.3.0)| 234 | |-|-----------------------| 235 | |toml|[datetime/local-time.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/datetime/local-time.toml) Parsed as unexpected data.
[datetime/datetime.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/datetime/datetime.toml) Parsed as unexpected data.
[comment/tricky.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/comment/tricky.toml) Parsed as unexpected data.
[key/dotted.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/key/dotted.toml) Found invalid character in key name: '"'. Try quoting the key name. (line 12 column 11 char 245)
[key/escapes.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/key/escapes.toml) Parsed as unexpected data.
[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/string/escape-esc.toml) Reserved escape sequence used (line 1 column 1 char 0)
[float/zero.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/float/zero.toml) Weirdness with leading zeroes or underscores in your number. (line 4 column 1 char 47)
[array/mixed-int-string.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/array/mixed-int-string.toml) Not a homogeneous array (line 1 column 1 char 0)
[array/nested-double.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/array/nested-double.toml) Not a homogeneous array (line 1 column 1 char 0)
[array/mixed-int-float.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/array/mixed-int-float.toml) Not a homogeneous array (line 1 column 1 char 0)
[array/mixed-string-table.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/array/mixed-string-table.toml) list index out of range
[array/mixed-int-array.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/array/mixed-int-array.toml) Not a homogeneous array (line 1 column 1 char 0)
[inline-table/multiline.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/inline-table/multiline.toml) Invalid inline table value encountered (line 1 column 1 char 0)
[inline-table/key-dotted.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/inline-table/key-dotted.toml) Parsed as unexpected data.
*86/100 (86.00%) passed*| 236 | |tomli/tomli_w|[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/string/escape-esc.toml) Unescaped '\' in a string (at line 1, column 10)
*99/100 (99.00%) passed*| 237 | |tomlkit|[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/string/escape-esc.toml) Invalid character 'e' in string at line 1 col 8
*99/100 (99.00%) passed*| 238 | |rtoml|[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/string/escape-esc.toml) invalid escape character in string: `e` at line 1 column 9
*99/100 (99.00%) passed*| 239 | |qtoml|[datetime/milliseconds.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/datetime/milliseconds.toml) Didn't find expected newline (line 2, column 27)
[datetime/datetime.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/datetime/datetime.toml) Didn't find expected newline (line 2, column 18)
[comment/tricky.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/comment/tricky.toml) can't parse type (line 11, column 7)
[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/string/escape-esc.toml) \e not a valid escape (line 1, column 33)
[string/multiline-quotes.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/string/multiline-quotes.toml) Didn't find expected newline (line 4, column 26)
*95/100 (95.00%) passed*| 240 | |tomllib|[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//valid/string/escape-esc.toml) Unescaped '\' in a string (at line 1, column 10)
*99/100 (99.00%) passed*| 241 | 242 | ## Compliance with invalid tests in toml-test 243 | 244 | Test the compliance with the standard test suites for invalid toml files 245 | here: 246 | 247 | > https://github.com/BurntSushi/toml-test/ 248 | 249 | - `Not OK`: The toml file is parsed without error, but expected to fail. 250 | - `OK`: All files are failed to parse, as expected. Showing the last 251 | parsing error. 252 | 253 | 254 | | |Result (toml-test v1.3.0)| 255 | |-|-----------------------| 256 | |toml|Not OK: [integer/double-sign-plus.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/integer/double-sign-plus.toml) incorrectly parsed.
Not OK: [integer/us-after-bin.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/integer/us-after-bin.toml) incorrectly parsed.
Not OK: [integer/double-sign-nex.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/integer/double-sign-nex.toml) incorrectly parsed.
Not OK: [integer/us-after-hex.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/integer/us-after-hex.toml) incorrectly parsed.
Not OK: [integer/us-after-oct.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/integer/us-after-oct.toml) incorrectly parsed.
Not OK: [control/comment-del.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/control/comment-del.toml) incorrectly parsed.
Not OK: [control/string-del.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/control/string-del.toml) incorrectly parsed.
Not OK: [control/multi-us.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/control/multi-us.toml) incorrectly parsed.
Not OK: [control/comment-lf.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/control/comment-lf.toml) incorrectly parsed.
Not OK: [control/comment-null.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/control/comment-null.toml) incorrectly parsed.
Not OK: *39 more items incorrectly parsed.*
*188/237 (79.32%) passed*| 257 | |tomli/tomli_w|OK: [inline-table/linebreak-1.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/inline-table/linebreak-1.toml) Unclosed inline table (at line 3, column 18)
*237/237 (100%) passed*| 258 | |tomlkit|Not OK: [control/comment-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/control/comment-cr.toml) incorrectly parsed.
Not OK: [control/bare-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/control/bare-cr.toml) incorrectly parsed.
Not OK: [table/append-with-dotted-keys-1.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/table/append-with-dotted-keys-1.toml) incorrectly parsed.
Not OK: [table/append-with-dotted-keys-2.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/table/append-with-dotted-keys-2.toml) incorrectly parsed.
*233/237 (98.31%) passed*| 259 | |rtoml|Not OK: [integer/positive-hex.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/integer/positive-hex.toml) incorrectly parsed.
Not OK: [integer/positive-bin.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/integer/positive-bin.toml) incorrectly parsed.
Not OK: [control/comment-del.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/control/comment-del.toml) incorrectly parsed.
Not OK: [control/comment-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/control/comment-cr.toml) incorrectly parsed.
Not OK: [control/bare-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/control/bare-cr.toml) incorrectly parsed.
*232/237 (97.89%) passed*| 260 | |qtoml|Not OK: [control/comment-del.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/control/comment-del.toml) incorrectly parsed.
Not OK: [control/comment-lf.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/control/comment-lf.toml) incorrectly parsed.
Not OK: [control/comment-null.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/control/comment-null.toml) incorrectly parsed.
Not OK: [control/comment-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/control/comment-cr.toml) incorrectly parsed.
Not OK: [control/comment-us.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/control/comment-us.toml) incorrectly parsed.
Not OK: [control/bare-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/control/bare-cr.toml) incorrectly parsed.
Not OK: [table/append-with-dotted-keys-1.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/table/append-with-dotted-keys-1.toml) incorrectly parsed.
Not OK: [table/duplicate-key-dotted-table.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/table/duplicate-key-dotted-table.toml) incorrectly parsed.
Not OK: [table/append-with-dotted-keys-2.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/table/append-with-dotted-keys-2.toml) incorrectly parsed.
Not OK: [table/duplicate-key-dotted-table2.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/table/duplicate-key-dotted-table2.toml) incorrectly parsed.
Not OK: *2 more items incorrectly parsed.*
*225/237 (94.94%) passed*| 261 | |tomllib|OK: [inline-table/linebreak-1.toml](https://github.com/BurntSushi/toml-test/blob/v1.3.0/tests//invalid/inline-table/linebreak-1.toml) Unclosed inline table (at line 3, column 18)
*237/237 (100%) passed*| 262 | 263 | ## Compliance with valid tests in python tomllib test data 264 | 265 | Test the compliance with python tomllib test data (since python 3.11) 266 | for valid toml files here: 267 | 268 | > https://github.com/python/cpython/tree/3.11/Lib/test/test_tomllib/data/valid 269 | 270 | The tests come up with a JSON counterpart that can be used to valid whether 271 | loading the toml file yields the same result as the JSON counterpart. 272 | 273 | 274 | | |Result (cpython tag 3.12.4)| 275 | |-|-----------------------| 276 | |toml|[apostrophes-in-literal-string.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/apostrophes-in-literal-string.toml) Unbalanced quotes (line 1 column 50 char 49)
[five-quotes.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/five-quotes.toml) Unterminated string found. Reached end of file. (line 7 column 1 char 97)
[dates-and-times/datetimes.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/dates-and-times/datetimes.toml) Parsed as unexpected data.
[multiline-basic-str/ends-in-whitespace-escape.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/multiline-basic-str/ends-in-whitespace-escape.toml) Reserved escape sequence used (line 6 column 1 char 28)
*8/12 (66.67%) passed*| 277 | |tomli/tomli_w|OK, *12/12 (100%) passed*| 278 | |tomlkit|OK, *12/12 (100%) passed*| 279 | |rtoml|OK, *12/12 (100%) passed*| 280 | |qtoml|[apostrophes-in-literal-string.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/apostrophes-in-literal-string.toml) Didn't find expected newline (line 3, column 3)
[five-quotes.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/five-quotes.toml) Didn't find expected newline (line 3, column 3)
[dates-and-times/datetimes.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/dates-and-times/datetimes.toml) Didn't find expected newline (line 1, column 19)
*9/12 (75.00%) passed*| 281 | |tomllib|OK, *12/12 (100%) passed*| 282 | 283 | ## Compliance with invalid tests in python tomllib test data 284 | 285 | Test the compliance with python tomllib test data (since python 3.11) 286 | for invalid toml files here: 287 | 288 | > https://github.com/python/cpython/tree/main/Lib/test/test_tomllib/data/invalid 289 | 290 | - `Not OK`: The toml file is parsed without error, but expected to fail. 291 | - `OK`: All files are failed to parse, as expected. Showing the last 292 | parsing error. 293 | 294 | 295 | | |Result (cpython tag 3.12.4)| 296 | |-|-----------------------| 297 | |toml|Not OK: [invalid-comment-char.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/invalid-comment-char.toml) incorrectly parsed.
Not OK: [multiline-basic-str/carriage-return.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/multiline-basic-str/carriage-return.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table-with-subtable.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table-with-subtable.toml) incorrectly parsed.
Not OK: [array/unclosed-empty.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/array/unclosed-empty.toml) incorrectly parsed.
Not OK: [array/file-end-after-val.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/array/file-end-after-val.toml) incorrectly parsed.
Not OK: [array/unclosed-after-item.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/array/unclosed-after-item.toml) incorrectly parsed.
Not OK: [inline-table/overwrite-value-in-inner-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/overwrite-value-in-inner-table.toml) incorrectly parsed.
Not OK: [inline-table/unclosed-empty.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/unclosed-empty.toml) incorrectly parsed.
*41/50 (82.00%) passed*| 298 | |tomli/tomli_w|OK: [inline-table/overwrite-implicitly.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/overwrite-implicitly.toml) Cannot overwrite a value (at line 1, column 21)
*50/50 (100%) passed*| 299 | |tomlkit|Not OK: [array-of-tables/overwrite-array-in-parent.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/array-of-tables/overwrite-array-in-parent.toml) incorrectly parsed.
Not OK: [multiline-basic-str/carriage-return.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/multiline-basic-str/carriage-return.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-aot.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-aot.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table-with-subtable.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table-with-subtable.toml) incorrectly parsed.
Not OK: [inline-table/override-val-in-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/override-val-in-table.toml) incorrectly parsed.
*44/50 (88.00%) passed*| 300 | |rtoml|Not OK: [multiline-basic-str/carriage-return.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/multiline-basic-str/carriage-return.toml) incorrectly parsed.
*49/50 (98.00%) passed*| 301 | |qtoml|Not OK: [non-scalar-escaped.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/non-scalar-escaped.toml) incorrectly parsed.
Not OK: [invalid-comment-char.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/invalid-comment-char.toml) incorrectly parsed.
Not OK: [table/redefine-2.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/table/redefine-2.toml) incorrectly parsed.
Not OK: [table/redefine-1.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/table/redefine-1.toml) incorrectly parsed.
Not OK: [multiline-basic-str/carriage-return.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/multiline-basic-str/carriage-return.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table-with-subtable.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table-with-subtable.toml) incorrectly parsed.
Not OK: [inline-table/overwrite-value-in-inner-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/overwrite-value-in-inner-table.toml) incorrectly parsed.
Not OK: [inline-table/override-val-with-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/override-val-with-table.toml) incorrectly parsed.
*41/50 (82.00%) passed*| 302 | |tomllib|OK: [inline-table/overwrite-implicitly.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/overwrite-implicitly.toml) Cannot overwrite a value (at line 1, column 21)
*50/50 (100%) passed*| 303 | 304 | ## Running speed with data provided by `rtoml` 305 | 306 | Test the speed of loading and dumping the loaded using data 307 | provided by `rtoml` 308 | 309 | > https://github.com/samuelcolvin/rtoml/raw/main/tests/data.toml 310 | 311 | 312 | | |Loading speed|Dumping speed| 313 | |-|-|-| 314 | |toml|Excluded (heterogeneous arrays not supported)|Excluded (heterogeneous arrays not supported)| 315 | |tomli/tomli_w|2.16s (5000 iterations)|0.74s (5000 iterations)| 316 | |tomlkit|39.48s (5000 iterations)|1.00s (5000 iterations)| 317 | |rtoml|0.38s (5000 iterations)|0.09s (5000 iterations)| 318 | |qtoml|5.00s (5000 iterations)|1.87s (5000 iterations)| 319 | |tomllib|2.08s (5000 iterations)|Dumping not supported| 320 | 321 | ## Running speed with data provided by `tomli` 322 | 323 | Test the speed of loading and dumping the loaded using data 324 | provided by `tomli` 325 | 326 | > https://github.com/hukkin/tomli/raw/master/benchmark/data.toml 327 | 328 | 329 | | |Loading speed|Dumping speed| 330 | |-|-|-| 331 | |toml|Excluded (heterogeneous arrays not supported)|Excluded (heterogeneous arrays not supported)| 332 | |tomli/tomli_w|1.39s (5000 iterations)|0.47s (5000 iterations)| 333 | |tomlkit|24.42s (5000 iterations)|0.50s (5000 iterations)| 334 | |rtoml|0.32s (5000 iterations)|0.16s (5000 iterations)| 335 | |qtoml|3.63s (5000 iterations)|1.25s (5000 iterations)| 336 | |tomllib|1.38s (5000 iterations)|Dumping not supported| 337 | 338 | -------------------------------------------------------------------------------- /reports/with_toml-test_v1.4.0.md: -------------------------------------------------------------------------------- 1 | # Report 2 | 3 | ## Version 4 | 5 | The verions of the packages tested in this report. 6 | 7 | | |Version| 8 | |-|-----------------------| 9 | |toml|0.10.2| 10 | |tomli/tomli_w|2.0.1; **tomli_w**: 1.0.0| 11 | |tomlkit|0.12.5| 12 | |rtoml|0.11.0| 13 | |qtoml|0.3.1| 14 | |tomllib|(Python 3.12.2)| 15 | 16 | ## Dumping `None` value 17 | 18 | How the package dumps `None` value in python 19 | 20 | Literally `.dumps(None)` 21 | 22 | 23 | | |Dumped value or error| 24 | |-|-----------------------| 25 | |toml|'NoneType' object is not iterable| 26 | |tomli/tomli_w|'NoneType' object has no attribute 'items'| 27 | |tomlkit|Expecting Mapping or TOML Container, given| 28 | |rtoml|"null"
---
rtoml v0.11+ supports dumping None to a desired string:
`rtoml.dumps(data, none_value='@None')`:
"@None"| 29 | |qtoml|'NoneType' object has no attribute 'items'| 30 | |tomllib|module 'tomllib' has no attribute 'dumps'| 31 | 32 | ## Dumping key-`None` pair 33 | 34 | How the package dumps key-value pair with value `None` 35 | 36 | Literally `.dumps({"key": None})` 37 | 38 | 39 | | |Dumped value or error| 40 | |-|-----------------------| 41 | |toml|| 42 | |tomli/tomli_w|Object of type is not TOML serializable| 43 | |tomlkit|Invalid type | 44 | |rtoml|key = "null"

---
rtoml v0.11+ supports dumping None to a desired string:
`rtoml.dumps(data, none_value='@None')`:
key = "@None"
| 45 | |qtoml|TOML cannot encode None| 46 | |tomllib|module 'tomllib' has no attribute 'dumps'| 47 | 48 | ## Dumping list with `None` value 49 | 50 | How the package dumps a list with `None` value in it. 51 | 52 | Literally `.dumps({"key": [1, 2, 3, None, 5]})` 53 | 54 | 55 | | |Dumped value or error| 56 | |-|-----------------------| 57 | |toml|key = [ 1, 2, 3, "None", 5,]
| 58 | |tomli/tomli_w|Object of type is not TOML serializable| 59 | |tomlkit|Invalid type | 60 | |rtoml|key = [1, 2, 3, "null", 5]

---
rtoml v0.11+ supports dumping None to a desired string:
`rtoml.dumps(data, none_value='@None')`:
key = [1, 2, 3, "@None", 5]
| 61 | |qtoml|bad type '' for dump_value| 62 | |tomllib|module 'tomllib' has no attribute 'dumps'| 63 | 64 | ## Loading `None`-like values 65 | 66 | How the package loads `None`-like value in string 67 | 68 | Literally `.loads('v1 = "null" v2 = "None"')` 69 | 70 | 71 | | |Loaded as| 72 | |-|-----------------------| 73 | |toml|{'v1': 'null', 'v2': 'None'}| 74 | |tomli/tomli_w|module 'tomli_w' has no attribute 'loads'| 75 | |tomlkit|{'v1': 'null', 'v2': 'None'}| 76 | |rtoml|{'v1': 'null', 'v2': 'None'}
---
rtoml v0.11+ supports loading custom None values:
`rtoml.loads(data, none_value='None')`:
{'v1': 'null', 'v2': None}
`rtoml.loads(data, none_value='null')`:
{'v1': None, 'v2': 'None'}| 77 | |qtoml|{'v1': 'null', 'v2': 'None'}| 78 | |tomllib|{'v1': 'null', 'v2': 'None'}| 79 | 80 | ## Dumping a heterogenous array 81 | 82 | How the package dumps a python dictionary with a heterogenous array. 83 | 84 | Literally `.dumps({"v": [1, 1.2, True, "string"]})` 85 | 86 | 87 | | |Dumped value or error| 88 | |-|-----------------------| 89 | |toml|v = \[ 1, 1.2, true, "string",\]
| 90 | |tomli/tomli_w|v = \[
    1,
    1.2,
    true,
    "string",
\]
| 91 | |tomlkit|v = \[1, 1.2, true, "string"\]
| 92 | |rtoml|v = \[1, 1.2, true, "string"\]
| 93 | |qtoml|v = \[1, 1.2, true, 'string'\]
| 94 | |tomllib|Dumping not supported| 95 | 96 | ## Loading a heterogenous array 97 | 98 | How the package loads a toml string with a heterogenous array. 99 | 100 | Literally `.loads('v = [1, 1.2, True, "string"]')` 101 | 102 | 103 | | |Loaded as| 104 | |-|-----------------------| 105 | |toml|Not a homogeneous array (line 2 column 1 char 1)| 106 | |tomli/tomli_w|`{'v': [1, 1.2, True, 'string']}`| 107 | |tomlkit|`{'v': [1, 1.2, True, 'string']}`| 108 | |rtoml|`{'v': [1, 1.2, True, 'string']}`| 109 | |qtoml|`{'v': [1, 1.2, True, 'string']}`| 110 | |tomllib|`{'v': [1, 1.2, True, 'string']}`| 111 | 112 | ## Dumping a nested array 113 | 114 | How the package dumps a python dictionary with a nested array. 115 | 116 | Literally `.dumps({"v": [[1], [1, 2]]})` 117 | 118 | 119 | | |Dumped value or error| 120 | |-|-----------------------| 121 | |toml|v = \[ \[ 1,\], \[ 1, 2,\],\]
| 122 | |tomli/tomli_w|v = \[
    \[
        1,
    \],
    \[
        1,
        2,
    \],
\]
| 123 | |tomlkit|v = \[\[1\], \[1, 2\]\]
| 124 | |rtoml|v = \[\[1\], \[1, 2\]\]
| 125 | |qtoml|v = \[\[1\], \[1, 2\]\]
| 126 | |tomllib|Dumping not supported| 127 | 128 | ## Loading a nested array 129 | 130 | How the package loads a toml string with a nested array. 131 | 132 | Literally `.loads('v = [[1], [1, 2]]')` 133 | 134 | 135 | | |Loaded as| 136 | |-|-----------------------| 137 | |toml|`{'v': [[1], [1, 2]]}`| 138 | |tomli/tomli_w|`{'v': [[1], [1, 2]]}`| 139 | |tomlkit|`{'v': [[1], [1, 2]]}`| 140 | |rtoml|`{'v': [[1], [1, 2]]}`| 141 | |qtoml|`{'v': [[1], [1, 2]]}`| 142 | |tomllib|`{'v': [[1], [1, 2]]}`| 143 | 144 | ## Dumping keeps order of keys? 145 | 146 | Whether the package preserves the order of the keys while dumps 147 | a python dictionary. 148 | 149 | Thus, whether `.dumps({"c": 1, "a": 2, "b": 3})` yields a string 150 | like `c = 1\na = 2\nb = 3\n`. 151 | 152 | 153 | | |Order kept?| 154 | |-|-----------------------| 155 | |toml|Kept| 156 | |tomli/tomli_w|Kept| 157 | |tomlkit|Kept| 158 | |rtoml|Kept| 159 | |qtoml|Kept| 160 | |tomllib|Dumping not supported| 161 | 162 | ## Loading keeps order of keys? 163 | 164 | Whether the package preserves the order of the keys 165 | while loads a TOML string. 166 | 167 | Thus, whether `.loads('c = 1\na = 2\nb = 3\n')` yields 168 | a dictionary with keys in the order of `['c', 'a', 'b']`. 169 | 170 | 171 | | |Order kept?| 172 | |-|-----------------------| 173 | |toml|Kept| 174 | |tomli/tomli_w|Kept| 175 | |tomlkit|Kept| 176 | |rtoml|Kept| 177 | |qtoml|Kept| 178 | |tomllib|Kept| 179 | 180 | ## Dumping unicode 181 | 182 | How the package dumps Unicode in python 183 | 184 | Literally, `.dumps({"你好": "世界"})` 185 | 186 | 187 | | |Dumped value| 188 | |-|-----------------------| 189 | |toml|"你好" = "世界"
| 190 | |tomli/tomli_w|"你好" = "世界"
| 191 | |tomlkit|"你好" = "世界"
| 192 | |rtoml|"你好" = "世界"
| 193 | |qtoml|'你好' = '世界'
| 194 | |tomllib|Dumping not supported| 195 | 196 | ## Loaded unicode 197 | 198 | How the package loads a file with unicode. 199 | 200 | The file was created by: 201 | 202 | ```python 203 | # Create a file with unicode content 204 | with open(self.datafile, "w", encoding="utf-8") as f: 205 | f.write('"你好" = "世界"\n') 206 | 207 | # Use `.load()` to load the file 208 | with open(self.datafile, "r", encoding="utf-8") as f: 209 | loaded = .load(f) 210 | ``` 211 | 212 | 213 | | |Loaded as| 214 | |-|-----------------------| 215 | |toml|{'你好': '世界'}| 216 | |tomli/tomli_w|File must be opened in binary mode, e.g. use `open('foo.toml', 'rb')`
When loaded with `rb`:
{'你好': '世界'}| 217 | |tomlkit|{'你好': '世界'}| 218 | |rtoml|{'你好': '世界'}| 219 | |qtoml|{'你好': '世界'}| 220 | |tomllib|File must be opened in binary mode, e.g. use `open('foo.toml', 'rb')`
When loaded with `rb`:
{'你好': '世界'}| 221 | 222 | ## Compliance with valid tests in toml-test 223 | 224 | Test the compliance with the standard test suites for valid toml files 225 | here: 226 | 227 | > https://github.com/BurntSushi/toml-test/ 228 | 229 | The tests come up with a JSON counterpart that can be used to valid whether 230 | loading the toml file yields the same result as the JSON counterpart. 231 | 232 | 233 | | |Result (toml-test v1.4.0)| 234 | |-|-----------------------| 235 | |toml|[spec/array-0.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/spec/array-0.toml) Not a homogeneous array (line 8 column 1 char 261)
[spec/keys-4.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/spec/keys-4.toml) Found invalid character in key name: 'c'. Try quoting the key name. (line 2 column 8 char 57)
[datetime/no-seconds.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/datetime/no-seconds.toml) invalid literal for int() with base 0: '13:37' (line 2 column 1 char 46)
[datetime/local-time.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/datetime/local-time.toml) Parsed as unexpected data.
[datetime/datetime.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/datetime/datetime.toml) Parsed as unexpected data.
[comment/tricky.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/comment/tricky.toml) Parsed as unexpected data.
[key/unicode.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/key/unicode.toml) Found invalid character in key name: '‍'. Try quoting the key name. (line 5 column 2 char 67)
[key/quoted-unicode.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/key/quoted-unicode.toml) Duplicate keys! (line 3 column 1 char 19)
[key/dotted.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/key/dotted.toml) Found invalid character in key name: '"'. Try quoting the key name. (line 12 column 11 char 245)
[key/dotted-empty.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/key/dotted-empty.toml) Duplicate keys! (line 2 column 1 char 17)
[key/escapes.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/key/escapes.toml) Parsed as unexpected data.
[table/empty-name.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/table/empty-name.toml) Can't have a keygroup with an empty name (line 1 column 1 char 0)
[string/hex-escape.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/string/hex-escape.toml) Reserved escape sequence used (line 3 column 1 char 35)
[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/string/escape-esc.toml) Reserved escape sequence used (line 1 column 1 char 0)
[float/zero.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/float/zero.toml) Weirdness with leading zeroes or underscores in your number. (line 4 column 1 char 47)
[array/mixed-int-string.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/array/mixed-int-string.toml) Not a homogeneous array (line 1 column 1 char 0)
[array/nested-double.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/array/nested-double.toml) Not a homogeneous array (line 1 column 1 char 0)
[array/string-with-comma-2.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/array/string-with-comma-2.toml) string index out of range
[array/mixed-int-float.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/array/mixed-int-float.toml) Not a homogeneous array (line 1 column 1 char 0)
[array/mixed-string-table.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/array/mixed-string-table.toml) list index out of range
[array/mixed-int-array.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/array/mixed-int-array.toml) Not a homogeneous array (line 1 column 1 char 0)
[inline-table/multiline.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/inline-table/multiline.toml) Invalid inline table value encountered (line 1 column 1 char 0)
[inline-table/key-dotted.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/inline-table/key-dotted.toml) Parsed as unexpected data.
[inline-table/newline.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/inline-table/newline.toml) Key name found without value. Reached end of line. (line 5 column 2 char 98)
*137/161 (85.09%) passed*| 236 | |tomli/tomli_w|[datetime/no-seconds.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/datetime/no-seconds.toml) Expected newline or end of document after a statement (at line 2, column 23)
[key/unicode.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/key/unicode.toml) Invalid statement (at line 3, column 1)
[string/hex-escape.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/string/hex-escape.toml) Unescaped '\' in a string (at line 3, column 22)
[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/string/escape-esc.toml) Unescaped '\' in a string (at line 1, column 10)
[inline-table/newline.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/inline-table/newline.toml) Invalid initial character for a key part (at line 3, column 21)
*156/161 (96.89%) passed*| 237 | |tomlkit|[datetime/no-seconds.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/datetime/no-seconds.toml) Invalid number at line 2 col 25
[key/unicode.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/key/unicode.toml) Empty key at line 3 col 0
[string/hex-escape.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/string/hex-escape.toml) Invalid character 'x' in string at line 3 col 20
[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/string/escape-esc.toml) Invalid character 'e' in string at line 1 col 8
[inline-table/newline.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/inline-table/newline.toml) Empty key at line 3 col 20
*156/161 (96.89%) passed*| 238 | |rtoml|[spec/table-9.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/spec/table-9.toml) duplicate key: `apple` for key `fruit` at line 8 column 1
[datetime/no-seconds.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/datetime/no-seconds.toml) expected a colon, found a newline at line 2 column 26
[key/unicode.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/key/unicode.toml) unexpected character found: `\u{20ac}` at line 3 column 1
[table/array-within-dotted.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/table/array-within-dotted.toml) duplicate key: `apple` for key `fruit` at line 4 column 1
[string/hex-escape.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/string/hex-escape.toml) invalid escape character in string: `x` at line 3 column 21
[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/string/escape-esc.toml) invalid escape character in string: `e` at line 1 column 9
[inline-table/newline.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/inline-table/newline.toml) expected a table key, found a newline at line 3 column 21
*154/161 (95.65%) passed*| 239 | |qtoml|[spec/string-4.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/spec/string-4.toml) Didn't find expected newline (line 7, column 62)
[spec/string-7.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/spec/string-7.toml) Didn't find expected newline (line 7, column 50)
[datetime/no-seconds.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/datetime/no-seconds.toml) can't parse type (line 2, column 20)
[datetime/milliseconds.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/datetime/milliseconds.toml) Didn't find expected newline (line 2, column 27)
[datetime/datetime.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/datetime/datetime.toml) Didn't find expected newline (line 2, column 18)
[comment/tricky.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/comment/tricky.toml) can't parse type (line 11, column 7)
[key/unicode.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/key/unicode.toml) '€' cannot begin key (line 3, column 0)
[string/hex-escape.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/string/hex-escape.toml) \x not a valid escape (line 3, column 43)
[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/string/escape-esc.toml) \e not a valid escape (line 1, column 33)
[string/multiline-quotes.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/string/multiline-quotes.toml) Didn't find expected newline (line 4, column 26)
[inline-table/newline.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/inline-table/newline.toml) ' ' cannot begin key (line 3, column 20)
*150/161 (93.17%) passed*| 240 | |tomllib|[datetime/no-seconds.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/datetime/no-seconds.toml) Expected newline or end of document after a statement (at line 2, column 23)
[key/unicode.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/key/unicode.toml) Invalid statement (at line 3, column 1)
[string/hex-escape.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/string/hex-escape.toml) Unescaped '\' in a string (at line 3, column 22)
[string/escape-esc.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/string/escape-esc.toml) Unescaped '\' in a string (at line 1, column 10)
[inline-table/newline.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//valid/inline-table/newline.toml) Invalid initial character for a key part (at line 3, column 21)
*156/161 (96.89%) passed*| 241 | 242 | ## Compliance with invalid tests in toml-test 243 | 244 | Test the compliance with the standard test suites for invalid toml files 245 | here: 246 | 247 | > https://github.com/BurntSushi/toml-test/ 248 | 249 | - `Not OK`: The toml file is parsed without error, but expected to fail. 250 | - `OK`: All files are failed to parse, as expected. Showing the last 251 | parsing error. 252 | 253 | 254 | | |Result (toml-test v1.4.0)| 255 | |-|-----------------------| 256 | |toml|Not OK: [integer/double-sign-plus.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/integer/double-sign-plus.toml) incorrectly parsed.
Not OK: [integer/us-after-bin.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/integer/us-after-bin.toml) incorrectly parsed.
Not OK: [integer/double-sign-nex.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/integer/double-sign-nex.toml) incorrectly parsed.
Not OK: [integer/us-after-hex.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/integer/us-after-hex.toml) incorrectly parsed.
Not OK: [integer/us-after-oct.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/integer/us-after-oct.toml) incorrectly parsed.
Not OK: [spec/inline-table-2-0.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/spec/inline-table-2-0.toml) incorrectly parsed.
Not OK: [control/comment-del.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/control/comment-del.toml) incorrectly parsed.
Not OK: [control/string-del.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/control/string-del.toml) incorrectly parsed.
Not OK: [control/multi-us.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/control/multi-us.toml) incorrectly parsed.
Not OK: [control/comment-lf.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/control/comment-lf.toml) incorrectly parsed.
Not OK: *44 more items incorrectly parsed.*
*205/259 (79.15%) passed*| 257 | |tomli/tomli_w|OK: [inline-table/linebreak-1.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/inline-table/linebreak-1.toml) Unclosed inline table (at line 3, column 18)
*259/259 (100%) passed*| 258 | |tomlkit|Not OK: [control/comment-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/control/comment-cr.toml) incorrectly parsed.
Not OK: [control/bare-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/control/bare-cr.toml) incorrectly parsed.
Not OK: [table/append-with-dotted-keys-1.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/table/append-with-dotted-keys-1.toml) incorrectly parsed.
Not OK: [table/append-to-array-with-dotted-keys.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/table/append-to-array-with-dotted-keys.toml) incorrectly parsed.
Not OK: [table/append-with-dotted-keys-2.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/table/append-with-dotted-keys-2.toml) incorrectly parsed.
*254/259 (98.07%) passed*| 259 | |rtoml|Not OK: [integer/positive-hex.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/integer/positive-hex.toml) incorrectly parsed.
Not OK: [integer/positive-bin.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/integer/positive-bin.toml) incorrectly parsed.
Not OK: [control/comment-del.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/control/comment-del.toml) incorrectly parsed.
Not OK: [control/comment-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/control/comment-cr.toml) incorrectly parsed.
Not OK: [control/bare-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/control/bare-cr.toml) incorrectly parsed.
*254/259 (98.07%) passed*| 260 | |qtoml|Not OK: [spec/inline-table-2-0.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/spec/inline-table-2-0.toml) incorrectly parsed.
Not OK: [spec/table-9-1.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/spec/table-9-1.toml) incorrectly parsed.
Not OK: [spec/table-9-0.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/spec/table-9-0.toml) incorrectly parsed.
Not OK: [control/comment-del.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/control/comment-del.toml) incorrectly parsed.
Not OK: [control/comment-lf.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/control/comment-lf.toml) incorrectly parsed.
Not OK: [control/comment-null.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/control/comment-null.toml) incorrectly parsed.
Not OK: [control/comment-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/control/comment-cr.toml) incorrectly parsed.
Not OK: [control/comment-us.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/control/comment-us.toml) incorrectly parsed.
Not OK: [control/bare-cr.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/control/bare-cr.toml) incorrectly parsed.
Not OK: [table/append-with-dotted-keys-1.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/table/append-with-dotted-keys-1.toml) incorrectly parsed.
Not OK: *6 more items incorrectly parsed.*
*243/259 (93.82%) passed*| 261 | |tomllib|OK: [inline-table/linebreak-1.toml](https://github.com/BurntSushi/toml-test/blob/v1.4.0/tests//invalid/inline-table/linebreak-1.toml) Unclosed inline table (at line 3, column 18)
*259/259 (100%) passed*| 262 | 263 | ## Compliance with valid tests in python tomllib test data 264 | 265 | Test the compliance with python tomllib test data (since python 3.11) 266 | for valid toml files here: 267 | 268 | > https://github.com/python/cpython/tree/3.11/Lib/test/test_tomllib/data/valid 269 | 270 | The tests come up with a JSON counterpart that can be used to valid whether 271 | loading the toml file yields the same result as the JSON counterpart. 272 | 273 | 274 | | |Result (cpython tag 3.12.4)| 275 | |-|-----------------------| 276 | |toml|[apostrophes-in-literal-string.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/apostrophes-in-literal-string.toml) Unbalanced quotes (line 1 column 50 char 49)
[five-quotes.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/five-quotes.toml) Unterminated string found. Reached end of file. (line 7 column 1 char 97)
[dates-and-times/datetimes.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/dates-and-times/datetimes.toml) Parsed as unexpected data.
[multiline-basic-str/ends-in-whitespace-escape.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/multiline-basic-str/ends-in-whitespace-escape.toml) Reserved escape sequence used (line 6 column 1 char 28)
*8/12 (66.67%) passed*| 277 | |tomli/tomli_w|OK, *12/12 (100%) passed*| 278 | |tomlkit|OK, *12/12 (100%) passed*| 279 | |rtoml|OK, *12/12 (100%) passed*| 280 | |qtoml|[apostrophes-in-literal-string.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/apostrophes-in-literal-string.toml) Didn't find expected newline (line 3, column 3)
[five-quotes.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/five-quotes.toml) Didn't find expected newline (line 3, column 3)
[dates-and-times/datetimes.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//valid/dates-and-times/datetimes.toml) Didn't find expected newline (line 1, column 19)
*9/12 (75.00%) passed*| 281 | |tomllib|OK, *12/12 (100%) passed*| 282 | 283 | ## Compliance with invalid tests in python tomllib test data 284 | 285 | Test the compliance with python tomllib test data (since python 3.11) 286 | for invalid toml files here: 287 | 288 | > https://github.com/python/cpython/tree/main/Lib/test/test_tomllib/data/invalid 289 | 290 | - `Not OK`: The toml file is parsed without error, but expected to fail. 291 | - `OK`: All files are failed to parse, as expected. Showing the last 292 | parsing error. 293 | 294 | 295 | | |Result (cpython tag 3.12.4)| 296 | |-|-----------------------| 297 | |toml|Not OK: [invalid-comment-char.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/invalid-comment-char.toml) incorrectly parsed.
Not OK: [multiline-basic-str/carriage-return.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/multiline-basic-str/carriage-return.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table-with-subtable.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table-with-subtable.toml) incorrectly parsed.
Not OK: [array/unclosed-empty.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/array/unclosed-empty.toml) incorrectly parsed.
Not OK: [array/file-end-after-val.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/array/file-end-after-val.toml) incorrectly parsed.
Not OK: [array/unclosed-after-item.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/array/unclosed-after-item.toml) incorrectly parsed.
Not OK: [inline-table/overwrite-value-in-inner-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/overwrite-value-in-inner-table.toml) incorrectly parsed.
Not OK: [inline-table/unclosed-empty.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/unclosed-empty.toml) incorrectly parsed.
*41/50 (82.00%) passed*| 298 | |tomli/tomli_w|OK: [inline-table/overwrite-implicitly.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/overwrite-implicitly.toml) Cannot overwrite a value (at line 1, column 21)
*50/50 (100%) passed*| 299 | |tomlkit|Not OK: [array-of-tables/overwrite-array-in-parent.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/array-of-tables/overwrite-array-in-parent.toml) incorrectly parsed.
Not OK: [multiline-basic-str/carriage-return.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/multiline-basic-str/carriage-return.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-aot.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-aot.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table-with-subtable.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table-with-subtable.toml) incorrectly parsed.
Not OK: [inline-table/override-val-in-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/override-val-in-table.toml) incorrectly parsed.
*44/50 (88.00%) passed*| 300 | |rtoml|Not OK: [multiline-basic-str/carriage-return.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/multiline-basic-str/carriage-return.toml) incorrectly parsed.
*49/50 (98.00%) passed*| 301 | |qtoml|Not OK: [non-scalar-escaped.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/non-scalar-escaped.toml) incorrectly parsed.
Not OK: [invalid-comment-char.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/invalid-comment-char.toml) incorrectly parsed.
Not OK: [table/redefine-2.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/table/redefine-2.toml) incorrectly parsed.
Not OK: [table/redefine-1.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/table/redefine-1.toml) incorrectly parsed.
Not OK: [multiline-basic-str/carriage-return.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/multiline-basic-str/carriage-return.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table.toml) incorrectly parsed.
Not OK: [dotted-keys/extend-defined-table-with-subtable.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/dotted-keys/extend-defined-table-with-subtable.toml) incorrectly parsed.
Not OK: [inline-table/overwrite-value-in-inner-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/overwrite-value-in-inner-table.toml) incorrectly parsed.
Not OK: [inline-table/override-val-with-table.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/override-val-with-table.toml) incorrectly parsed.
*41/50 (82.00%) passed*| 302 | |tomllib|OK: [inline-table/overwrite-implicitly.toml](https://github.com/python/cpython/tree/v3.12.4/Lib/test/test_tomllib/data//invalid/inline-table/overwrite-implicitly.toml) Cannot overwrite a value (at line 1, column 21)
*50/50 (100%) passed*| 303 | 304 | ## Running speed with data provided by `rtoml` 305 | 306 | Test the speed of loading and dumping the loaded using data 307 | provided by `rtoml` 308 | 309 | > https://github.com/samuelcolvin/rtoml/raw/main/tests/data.toml 310 | 311 | 312 | | |Loading speed|Dumping speed| 313 | |-|-|-| 314 | |toml|Excluded (heterogeneous arrays not supported)|Excluded (heterogeneous arrays not supported)| 315 | |tomli/tomli_w|2.09s (5000 iterations)|0.75s (5000 iterations)| 316 | |tomlkit|39.63s (5000 iterations)|1.01s (5000 iterations)| 317 | |rtoml|0.38s (5000 iterations)|0.09s (5000 iterations)| 318 | |qtoml|5.02s (5000 iterations)|1.88s (5000 iterations)| 319 | |tomllib|2.03s (5000 iterations)|Dumping not supported| 320 | 321 | ## Running speed with data provided by `tomli` 322 | 323 | Test the speed of loading and dumping the loaded using data 324 | provided by `tomli` 325 | 326 | > https://github.com/hukkin/tomli/raw/master/benchmark/data.toml 327 | 328 | 329 | | |Loading speed|Dumping speed| 330 | |-|-|-| 331 | |toml|Excluded (heterogeneous arrays not supported)|Excluded (heterogeneous arrays not supported)| 332 | |tomli/tomli_w|1.37s (5000 iterations)|0.45s (5000 iterations)| 333 | |tomlkit|24.39s (5000 iterations)|0.51s (5000 iterations)| 334 | |rtoml|0.33s (5000 iterations)|0.16s (5000 iterations)| 335 | |qtoml|3.65s (5000 iterations)|1.27s (5000 iterations)| 336 | |tomllib|1.43s (5000 iterations)|Dumping not supported| 337 | 338 | -------------------------------------------------------------------------------- /run-tests.py: -------------------------------------------------------------------------------- 1 | """Run tests and replace latest report""" 2 | import os 3 | import sys 4 | from pathlib import Path 5 | 6 | REPORTS = { 7 | "latest": "1.5.0", 8 | "v1.4.0": "1.4.0", 9 | "v1.3.0": "1.3.0", 10 | "v1.2.0": "1.2.0", 11 | } 12 | 13 | 14 | def run_test(comver): 15 | print("Running with toml-test version:", comver) 16 | 17 | outfile = f"reports/with_toml-test_{comver}.md" 18 | 19 | cmd = f"python -m toml_bench --iter 5000 --comver {REPORTS[comver]} --report {outfile}" 20 | os.system(cmd) 21 | 22 | 23 | def render_readme(): 24 | print("Rendering README file") 25 | report = [] 26 | rptfile = Path("reports/with_toml-test_latest.md") 27 | outfile = Path("./README.md") 28 | rawfile = Path("./README.raw.md") 29 | 30 | with rptfile.open("r") as f: 31 | for line in f: 32 | if line.startswith("#"): 33 | line = f"#{line}" 34 | report.append(line) 35 | 36 | readme = rawfile.read_text().replace("{{latest-report}}", "".join(report)) 37 | outfile.write_text(readme) 38 | 39 | 40 | if __name__ == "__main__": 41 | if len(sys.argv) > 1 and sys.argv[1] == "run": 42 | for key in REPORTS: 43 | run_test(key) 44 | 45 | render_readme() 46 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | This will not be included in the distribution. 4 | The distribution is managed by poetry 5 | This file is kept only for 6 | 1. Github to index the dependents 7 | 2. pip install -e . 8 | Do NOT use this to install this package, unless you handled the dependencies 9 | by your self: 10 | pip install git+https://... 11 | """ 12 | from setuptools import setup 13 | 14 | setup(name="toml-bench") 15 | -------------------------------------------------------------------------------- /toml_bench/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | __version__ = "0.3.1" 3 | -------------------------------------------------------------------------------- /toml_bench/__main__.py: -------------------------------------------------------------------------------- 1 | from benchwork import run_suite 2 | from argx import ArgumentParser 3 | 4 | from .suite import BenchSuite 5 | 6 | 7 | def init_params() -> ArgumentParser: 8 | """Init the parameters""" 9 | parser = ArgumentParser( 10 | prog="toml_bench", 11 | description="Benchmarking on toml packages in python", 12 | ) 13 | parser.add_argument( 14 | "--datadir", 15 | type="path", 16 | default="/tmp/toml-bench", 17 | help="Where to put the test-data", 18 | ) 19 | parser.add_argument( 20 | "--report", 21 | type="path", 22 | default="/dev/stdout", 23 | help="If provided, the report will be written to this file", 24 | ) 25 | parser.add_argument( 26 | "--title", 27 | default="Report", 28 | help="The title of the report", 29 | ) 30 | parser.add_argument( 31 | "--comver", 32 | default="1.5.0", 33 | help="The version of the toml-test to use in compliance tests", 34 | ) 35 | parser.add_argument( 36 | "--cpyver", 37 | default="3.12.4", 38 | help="The version (tag) of cpython to grab the tomllib test data", 39 | ) 40 | parser.add_argument( 41 | "--nocache", 42 | action="store_true", 43 | help="Do not use cached data, re-download them.", 44 | ) 45 | parser.add_argument( 46 | "--iter", 47 | type="int", 48 | default=5000, 49 | help="The number of iterations to run in speed tests", 50 | ) 51 | return parser 52 | 53 | 54 | def main(): 55 | """Main entrance""" 56 | args = init_params().parse_args() 57 | run_suite( 58 | BenchSuite, 59 | args=args, 60 | title=args.title, 61 | outfile=args.report, 62 | ) 63 | 64 | 65 | if __name__ == "__main__": 66 | main() 67 | -------------------------------------------------------------------------------- /toml_bench/api.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import sys 4 | from abc import ABC, abstractproperty 5 | from typing import TYPE_CHECKING, Any, Mapping 6 | 7 | import importlib_metadata as im 8 | from benchwork import BenchAPI 9 | 10 | import qtoml 11 | import rtoml 12 | import toml 13 | import tomli 14 | import tomli_w 15 | import tomlkit 16 | 17 | try: 18 | import pytomlpp 19 | except ImportError: 20 | pytomlpp = None 21 | 22 | try: 23 | import tomllib 24 | except ImportError: 25 | tomllib = None 26 | 27 | if TYPE_CHECKING: 28 | from os import PathLike 29 | from types import ModuleType 30 | 31 | 32 | class APIBase(BenchAPI, ABC): 33 | _SUBCLASSES = None 34 | OPEN_FLAG = "r" 35 | 36 | @abstractproperty 37 | def package(self) -> ModuleType: 38 | ... 39 | 40 | @abstractproperty 41 | def repo(self) -> str: 42 | ... 43 | 44 | @abstractproperty 45 | def _name(self) -> str: 46 | ... 47 | 48 | @property 49 | def name(self) -> str: 50 | return f'{self._name}' 51 | 52 | def load(self, path: PathLike) -> Mapping[str, Any]: 53 | return self.package.load(path) 54 | 55 | def dumps(self, data: Mapping[str, Any]) -> str: 56 | return self.package.dumps(data) 57 | 58 | def loads(self, data: str) -> Mapping[str, Any]: 59 | return self.package.loads(data) 60 | 61 | def dump(self, data: Mapping[str, Any], path: PathLike) -> None: 62 | self.package.dump(data, path) 63 | 64 | @property 65 | def version(self) -> str: 66 | return im.version(self.package.__name__) 67 | 68 | dumps_none = dumps 69 | loads_none = loads 70 | 71 | 72 | class TOMLAPI(APIBase): 73 | _name = "toml" 74 | package = toml 75 | repo = "https://github.com/uiri/toml" 76 | 77 | 78 | class TOMLiAPI(APIBase): 79 | OPEN_FLAG = "rb" 80 | _name = "tomli/tomli_w" 81 | package = tomli_w 82 | repo = "https://github.com/hukkin/tomli" 83 | loads = lambda self, data: tomli.loads(data) # noqa: E731 84 | load = lambda self, f: tomli.load(f) # noqa: E731 85 | 86 | @property 87 | def version(self) -> str: 88 | return ( 89 | f"{im.version('tomli')}; " 90 | f"**tomli_w**: {im.version(self.package.__name__)}" 91 | ) 92 | 93 | 94 | class TOMLKitAPI(APIBase): 95 | _name = "tomlkit" 96 | package = tomlkit 97 | repo = "https://github.com/sdispater/tomlkit" 98 | 99 | def load(self, f): 100 | content = f.read() 101 | return tomlkit.loads(content) 102 | 103 | 104 | if pytomlpp: 105 | class PyTOMLPPAPI(APIBase): 106 | _name = "pytomlpp" 107 | package = pytomlpp 108 | repo = "https://github.com/bobfang1992/pytomlpp" 109 | 110 | 111 | class RTOMLAPI(APIBase): 112 | _name = "rtoml" 113 | package = rtoml 114 | repo = "https://github.com/samuelcolvin/rtoml" 115 | 116 | def dumps_none(self, data: Any) -> str: 117 | out = self.package.dumps(data) 118 | version = tuple(map(int, self.version.split("."))) 119 | if version < (0, 11, 0): 120 | return out 121 | 122 | return ( 123 | f"{out}\n---\nrtoml v0.11+ supports dumping None to a desired string:\n" 124 | "`rtoml.dumps(data, none_value='@None')`:" 125 | f"\n{self.package.dumps(data, none_value='@None')}" 126 | ) 127 | 128 | def loads_none(self, data: str) -> Mapping[str, Any]: 129 | out = self.package.loads(data) 130 | version = tuple(map(int, self.version.split("."))) 131 | if version < (0, 11, 0): 132 | return out 133 | 134 | return ( 135 | f"{out!r}\n---\nrtoml v0.11+ supports loading custom None values:" 136 | "\n`rtoml.loads(data, none_value='None')`:" 137 | f"\n{self.package.loads(data, none_value='None')}" 138 | "\n`rtoml.loads(data, none_value='null')`:" 139 | f"\n{self.package.loads(data, none_value='null')}" 140 | ) 141 | 142 | 143 | class QTOMLAPI(APIBase): 144 | _name = "qtoml" 145 | package = qtoml 146 | repo = "https://github.com/alethiophile/qtoml" 147 | 148 | 149 | if tomllib: 150 | class TOMLLibAPI(APIBase): 151 | OPEN_FLAG = "rb" 152 | 153 | _name = "tomllib" 154 | package = tomllib 155 | repo = "https://docs.python.org/3/library/tomllib.html" 156 | 157 | @property 158 | def version(self) -> str: 159 | return ( 160 | "(Python " 161 | f"{sys.version_info.major}." 162 | f"{sys.version_info.minor}." 163 | f"{sys.version_info.micro})" 164 | ) 165 | 166 | def dumps(self, data: Mapping[str, Any]) -> str: 167 | raise NotImplementedError("Dumping not supported") 168 | 169 | def dump(self, data: Mapping[str, Any], path: PathLike) -> None: 170 | raise NotImplementedError("Dumping not supported") 171 | -------------------------------------------------------------------------------- /toml_bench/cases/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwwang/toml-bench/6bbe1d4b5fdb47f89ba40c498e5fa6fed5579e27/toml_bench/cases/__init__.py -------------------------------------------------------------------------------- /toml_bench/cases/compliance.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING, Any, List, Type 4 | from benchwork import BenchCase 5 | 6 | from ..utils import load_json_data 7 | 8 | if TYPE_CHECKING: 9 | from argparse import Namespace 10 | from pathlib import Path 11 | from benchwork import BenchAPI 12 | 13 | # used to indicate the souce of the data 14 | TEST_FILE_PREFIX = ( 15 | "https://github.com/BurntSushi/toml-test/blob/v%(ver)s/tests/" 16 | ) 17 | TOMLLIB_TEST_FILE_PREFIX = ( 18 | "https://github.com/python/cpython/" 19 | "tree/v%(ver)s/Lib/test/test_tomllib/data/" 20 | ) 21 | 22 | 23 | class BenchCaseCompliance(BenchCase): 24 | 25 | KIND = None 26 | SUBDIR = "tests" 27 | 28 | def __init__(self, args: Namespace, api_class: Type[BenchAPI]) -> None: 29 | super().__init__(args, api_class) 30 | self.total = 0 31 | self.datadir = None 32 | 33 | def fileurl(self, filename: str) -> str: 34 | return ( 35 | f"{TEST_FILE_PREFIX % {'ver': self.args.comver}}" 36 | f"/{self.__class__.KIND}/{filename}" 37 | ) 38 | 39 | def _runfile( 40 | self, 41 | tomlfile: Path, 42 | subdir: bool = False, 43 | ) -> Any: 44 | ... 45 | 46 | def _result( 47 | self, 48 | out: List[Any], 49 | ) -> Any: 50 | ... 51 | 52 | def run(self) -> Any: 53 | errors = [] 54 | kind = self.__class__.KIND 55 | subdir = self.__class__.SUBDIR 56 | for tomlfile in self.datadir.joinpath(subdir, kind).glob("*.toml"): 57 | out = self._runfile(tomlfile) 58 | if out is not None: 59 | errors.append(out) 60 | 61 | for tomlfile in self.datadir.joinpath(subdir, kind).glob("*/*.toml"): 62 | out = self._runfile(tomlfile, subdir=True) 63 | if out is not None: 64 | errors.append(out) 65 | 66 | return self._result(errors) 67 | 68 | 69 | class BenchCaseComplianceValid(BenchCaseCompliance): 70 | 71 | KIND = "valid" 72 | 73 | def _runfile(self, tomlfile: Path, subdir: bool = False) -> Any: 74 | 75 | self.total += 1 76 | filename = ( 77 | tomlfile.name 78 | if not subdir 79 | else f"{tomlfile.parent.name}/{tomlfile.name}" 80 | ) 81 | url = self.fileurl(filename) 82 | with tomlfile.open(self.api.OPEN_FLAG) as f: 83 | try: 84 | data = self.api.load(f) 85 | except Exception as e: 86 | return Exception(f"[{filename}]({url}) {e}") 87 | with tomlfile.with_suffix(".json").open("r") as f: 88 | expect = load_json_data(f) 89 | 90 | try: 91 | assert ( 92 | data == expect 93 | ), f"[{filename}]({url}) Parsed as unexpected data." 94 | except AssertionError as e: 95 | return e 96 | 97 | def _result(self, out: List[Any]) -> Any: 98 | 99 | if not out: 100 | return f"OK, *{self.total}/{self.total} (100%) passed*" 101 | 102 | passed = self.total - len(out) 103 | out.append( 104 | f"*{passed}/{self.total} " 105 | f"({100.0 * passed / self.total:.2f}%) passed*" 106 | ) 107 | 108 | return "
".join(str(e).replace("\n", " ") for e in out) 109 | 110 | 111 | class BenchCaseComplianceInvalid(BenchCaseCompliance): 112 | 113 | KIND = "invalid" 114 | 115 | def _runfile(self, tomlfile: Path, subdir: bool = False) -> Any: 116 | 117 | self.total += 1 118 | filename = ( 119 | tomlfile.name 120 | if not subdir 121 | else f"{tomlfile.parent.name}/{tomlfile.name}" 122 | ) 123 | url = self.fileurl(filename) 124 | with tomlfile.open(self.api.OPEN_FLAG) as f: 125 | try: 126 | self.api.load(f) 127 | except Exception as e: 128 | return Exception(f"[{filename}]({url}) {e}") 129 | else: 130 | return f"Not OK: [{filename}]({url}) incorrectly parsed." 131 | 132 | def _result(self, out: List[Any]) -> Any: 133 | 134 | replace_newline = lambda s: s.replace("\n", " ") # noqa: E731 135 | passed = [isinstance(e, Exception) for e in out] 136 | failed = [e for e in out if not isinstance(e, Exception)] 137 | if all(passed): 138 | return ( 139 | f"OK: {replace_newline(str(out[-1]))}
" 140 | f"*{len(passed)}/{len(passed)} (100%) passed*" 141 | ) 142 | 143 | if len(failed) > 10: 144 | failed = failed[:10] + [ 145 | f"Not OK: *{len(failed) - 10} more items incorrectly parsed.*" 146 | ] 147 | 148 | failed.append( 149 | f"*{sum(passed)}/{self.total} " 150 | f"({100.0 * sum(passed) / self.total:.2f}%) passed*" 151 | ) 152 | return "
".join(replace_newline(str(e)) for e in failed) 153 | 154 | 155 | class BenchCaseTomllibComplianceValid(BenchCaseComplianceValid): 156 | 157 | SUBDIR = "Lib/test/test_tomllib/data" 158 | 159 | def fileurl(self, filename: str) -> str: 160 | return ( 161 | f"{TOMLLIB_TEST_FILE_PREFIX % {'ver': self.args.cpyver}}" 162 | f"/{self.__class__.KIND}/{filename}" 163 | ) 164 | 165 | 166 | class BenchCaseTomllibComplianceInvalid( 167 | BenchCaseComplianceInvalid, 168 | BenchCaseTomllibComplianceValid, 169 | ): 170 | 171 | ... 172 | -------------------------------------------------------------------------------- /toml_bench/cases/hetero_array.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | from benchwork import BenchCase 3 | 4 | 5 | class BenchCaseDumpsHeteroArray(BenchCase): 6 | def run(self) -> Any: 7 | v1 = { 8 | "v": [1, 1.2, True, "string"] 9 | } 10 | try: 11 | return ( 12 | self.api.dumps(v1) 13 | .replace('[', r'\[') 14 | .replace(']', r'\]') 15 | .replace(' ', ' ') 16 | ) 17 | except Exception as e: 18 | return e 19 | 20 | 21 | class BenchCaseLoadsHeteroArray(BenchCase): 22 | 23 | def run(self) -> Any: 24 | v1 = """ 25 | v = [1, 1.2, true, "string"] 26 | """ 27 | try: 28 | return f"`{self.api.loads(v1)}`" 29 | except Exception as e1: 30 | return e1 31 | 32 | 33 | class BenchCaseDumpsNestedArray(BenchCase): 34 | def run(self) -> Any: 35 | v1 = { 36 | "v": [[1], [1, 2]] 37 | } 38 | try: 39 | return ( 40 | self.api.dumps(v1) 41 | .replace('[', r'\[') 42 | .replace(']', r'\]') 43 | .replace(' ', ' ') 44 | ) 45 | except Exception as e: 46 | return e 47 | 48 | 49 | class BenchCaseLoadsNestedArray(BenchCase): 50 | 51 | def run(self) -> Any: 52 | v1 = """ 53 | v = [[1], [1, 2]] 54 | """ 55 | try: 56 | return f"`{self.api.loads(v1)}`" 57 | except Exception as e1: 58 | return e1 59 | -------------------------------------------------------------------------------- /toml_bench/cases/keys_order.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | from benchwork import BenchCase 3 | 4 | 5 | class BenchCaseDumpKeysOrder(BenchCase): 6 | 7 | def run(self) -> Any: 8 | v1 = {"a": 1, "b": 2, "c": 3} 9 | v2 = {"b": 1, "c": 2, "a": 3} 10 | v3 = {"c": 1, "a": 2, "b": 3} 11 | 12 | e1 = "a = 1\nb = 2\nc = 3\n" 13 | e2 = "b = 1\nc = 2\na = 3\n" 14 | e3 = "c = 1\na = 2\nb = 3\n" 15 | 16 | try: 17 | if ( 18 | self.api.dumps(v1) == e1 19 | and self.api.dumps(v2) == e2 20 | and self.api.dumps(v3) == e3 21 | ): 22 | return "Kept" 23 | return "Lost" 24 | except Exception as ex: 25 | return str(ex) 26 | 27 | 28 | class BenchCaseLoadKeysOrder(BenchCase): 29 | 30 | def run(self) -> Any: 31 | v1 = "a = 1\nb = 2\nc = 3\n" 32 | v2 = "b = 1\nc = 2\na = 3\n" 33 | v3 = "c = 1\na = 2\nb = 3\n" 34 | 35 | e1 = ["a", "b", "c"] 36 | e2 = ["b", "c", "a"] 37 | e3 = ["c", "a", "b"] 38 | 39 | if ( 40 | list(self.api.loads(v1)) == e1 41 | and list(self.api.loads(v2)) == e2 42 | and list(self.api.loads(v3)) == e3 43 | ): 44 | return "Kept" 45 | return "Lost" 46 | -------------------------------------------------------------------------------- /toml_bench/cases/speed.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import TYPE_CHECKING, Any, Type 4 | from benchwork import BenchCaseSpeed 5 | 6 | if TYPE_CHECKING: 7 | from argparse import Namespace 8 | from benchwork import BenchAPI 9 | 10 | 11 | class BenchCaseSpeed(BenchCaseSpeed): 12 | 13 | timeit_number = "iter" 14 | 15 | def __init__(self, args: Namespace, api_class: Type[BenchAPI]) -> None: 16 | super().__init__(args, api_class) 17 | self.data = None 18 | self.loaded = None 19 | 20 | def run_core(self): 21 | ... 22 | 23 | def run_loading(self): 24 | self.loaded = self.api.loads(self.data) 25 | return self.loaded 26 | 27 | def run_dumping(self): 28 | return self.api.dumps(self.loaded) 29 | 30 | def run(self) -> Any: 31 | if self.api._name == "toml": 32 | return [ 33 | "Excluded (heterogeneous arrays not supported)", 34 | "Excluded (heterogeneous arrays not supported)", 35 | ] 36 | 37 | self.run_core = self.run_loading 38 | out = super().run() 39 | loading = f"{out:.2f}s ({self.args.iter} iterations)" 40 | 41 | self.run_core = self.run_dumping 42 | try: 43 | out = super().run() 44 | except Exception as e: 45 | dumping = str(e) 46 | else: 47 | dumping = f"{out:.2f}s ({self.args.iter} iterations)" 48 | 49 | return [loading, dumping] 50 | -------------------------------------------------------------------------------- /toml_bench/cases/unicode.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | from benchwork import BenchCase 3 | 4 | 5 | class BenchCaseDumpsUnicode(BenchCase): 6 | def run(self) -> Any: 7 | v1 = {"你好": "世界"} 8 | try: 9 | return self.api.dumps(v1) 10 | except Exception as e: 11 | return e 12 | 13 | 14 | class BenchCaseLoadsUnicode(BenchCase): 15 | 16 | def run(self) -> Any: 17 | try: 18 | with open(self.datafile, "r", encoding="utf-8") as f1: 19 | return self.api.load(f1) 20 | except Exception as e1: 21 | try: 22 | with open(self.datafile, "rb") as f2: 23 | loaded = self.api.load(f2) 24 | return f"{e1}\nWhen loaded with `rb`:\n{loaded}" 25 | except Exception: 26 | return e1 27 | -------------------------------------------------------------------------------- /toml_bench/cases/value_none.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | from benchwork import BenchCase 3 | 4 | 5 | class BenchCaseDumpNone(BenchCase): 6 | 7 | def run(self) -> Any: 8 | try: 9 | return self.api.dumps_none(None) 10 | except Exception as e: 11 | return e 12 | 13 | 14 | class BenchCaseDumpValueNone(BenchCase): 15 | 16 | def run(self) -> Any: 17 | try: 18 | return self.api.dumps_none({"key": None}) 19 | except Exception as e: 20 | return e 21 | 22 | 23 | class BenchCaseDumpListWithNone(BenchCase): 24 | 25 | def run(self) -> Any: 26 | try: 27 | return self.api.dumps_none({"key": [1, 2, 3, None, 5]}) 28 | except Exception as e: 29 | return e 30 | 31 | 32 | class BenchCaseLoadNoneLike(BenchCase): 33 | 34 | def run(self) -> Any: 35 | try: 36 | return self.api.loads_none('v1 = "null"\nv2 = "None"') 37 | except Exception as e: 38 | return e 39 | -------------------------------------------------------------------------------- /toml_bench/cases/version.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from benchwork import Bench 4 | 5 | 6 | class VersionDummy(TestCaseDummy): 7 | def run(self, case: "TestCase") -> Any: 8 | super().run(case) 9 | return self.api.version() 10 | 11 | 12 | class Version(TestCase): 13 | """The verions of the packages tested in this report.""" 14 | 15 | ORDER = -99 16 | HEADER = "Version" 17 | DUMMY_CLASS = VersionDummy 18 | -------------------------------------------------------------------------------- /toml_bench/sets.py: -------------------------------------------------------------------------------- 1 | import zipfile 2 | import urllib.request 3 | 4 | from benchwork import BenchSetVersion, BenchSetTable, BenchSetMultiColTable 5 | 6 | from .api import APIBase 7 | from .cases.value_none import ( 8 | BenchCaseDumpListWithNone, 9 | BenchCaseDumpValueNone, 10 | BenchCaseDumpNone, 11 | BenchCaseLoadNoneLike, 12 | ) 13 | from .cases.hetero_array import ( 14 | BenchCaseDumpsHeteroArray, 15 | BenchCaseLoadsHeteroArray, 16 | BenchCaseDumpsNestedArray, 17 | BenchCaseLoadsNestedArray, 18 | ) 19 | from .cases.keys_order import ( 20 | BenchCaseDumpKeysOrder, 21 | BenchCaseLoadKeysOrder, 22 | ) 23 | from .cases.unicode import ( 24 | BenchCaseDumpsUnicode, 25 | BenchCaseLoadsUnicode, 26 | ) 27 | from .cases.compliance import ( 28 | BenchCaseComplianceValid, 29 | BenchCaseComplianceInvalid, 30 | BenchCaseTomllibComplianceValid, 31 | BenchCaseTomllibComplianceInvalid, 32 | ) 33 | from .cases.speed import BenchCaseSpeed 34 | 35 | TOML_TEST_REPO = "https://github.com/BurntSushi/toml-test/" 36 | 37 | TOMLLIB_DATA_REPO = "https://github.com/python/cpython" 38 | 39 | PYTOMLPP_DATA_URL = ( 40 | "https://github.com/bobfang1992/pytomlpp/raw/master/benchmark/data.toml" 41 | ) 42 | 43 | TOMLI_DATA_URL = ( 44 | "https://github.com/hukkin/tomli/raw/master/benchmark/data.toml" 45 | ) 46 | 47 | RTOML_DATA_URL = ( 48 | "https://github.com/samuelcolvin/rtoml/raw/main/tests/data.toml" 49 | ) 50 | 51 | 52 | class BenchSetVersion(BenchSetVersion): 53 | """The verions of the packages tested in this report.""" 54 | 55 | header = "Version" 56 | title = "Version" 57 | api_base = APIBase 58 | 59 | 60 | class BenchSetDumpNone(BenchSetTable): 61 | """How the package dumps `None` value in python 62 | 63 | Literally `.dumps(None)` 64 | """ 65 | 66 | title = "Dumping `None` value" 67 | header = "Dumped value or error" 68 | api_base = APIBase 69 | case = BenchCaseDumpNone 70 | 71 | 72 | class BenchSetDumpValueNone(BenchSetTable): 73 | """How the package dumps key-value pair with value `None` 74 | 75 | Literally `.dumps({"key": None})` 76 | """ 77 | 78 | title = "Dumping key-`None` pair" 79 | header = "Dumped value or error" 80 | api_base = APIBase 81 | case = BenchCaseDumpValueNone 82 | 83 | 84 | class BenchSetDumpListWithNone(BenchSetTable): 85 | """How the package dumps a list with `None` value in it. 86 | 87 | Literally `.dumps({"key": [1, 2, 3, None, 5]})` 88 | """ 89 | 90 | title = "Dumping list with `None` value" 91 | header = "Dumped value or error" 92 | api_base = APIBase 93 | case = BenchCaseDumpListWithNone 94 | 95 | 96 | class BenchSetLoadNoneLike(BenchSetTable): 97 | """How the package loads `None`-like value in string 98 | 99 | Literally `.loads('v1 = "null" v2 = "None"')` 100 | """ 101 | 102 | title = "Loading `None`-like values" 103 | header = "Loaded as" 104 | api_base = APIBase 105 | case = BenchCaseLoadNoneLike 106 | 107 | 108 | class BenchSetDumpsHeteroArray(BenchSetTable): 109 | """How the package dumps a python dictionary with a heterogenous array. 110 | 111 | Literally `.dumps({"v": [1, 1.2, True, "string"]})` 112 | """ 113 | 114 | title = "Dumping a heterogenous array" 115 | header = "Dumped value or error" 116 | api_base = APIBase 117 | case = BenchCaseDumpsHeteroArray 118 | 119 | 120 | class BenchSetLoadsHeteroArray(BenchSetTable): 121 | """How the package loads a toml string with a heterogenous array. 122 | 123 | Literally `.loads('v = [1, 1.2, True, "string"]')` 124 | """ 125 | 126 | title = "Loading a heterogenous array" 127 | header = "Loaded as" 128 | api_base = APIBase 129 | case = BenchCaseLoadsHeteroArray 130 | 131 | 132 | class BenchSetDumpsNestedArray(BenchSetTable): 133 | """How the package dumps a python dictionary with a nested array. 134 | 135 | Literally `.dumps({"v": [[1], [1, 2]]})` 136 | """ 137 | 138 | title = "Dumping a nested array" 139 | header = "Dumped value or error" 140 | api_base = APIBase 141 | case = BenchCaseDumpsNestedArray 142 | 143 | 144 | class BenchSetLoadsNestedArray(BenchSetTable): 145 | """How the package loads a toml string with a nested array. 146 | 147 | Literally `.loads('v = [[1], [1, 2]]')` 148 | """ 149 | title = "Loading a nested array" 150 | header = "Loaded as" 151 | api_base = APIBase 152 | case = BenchCaseLoadsNestedArray 153 | 154 | 155 | class BenchSetDumpKeysOrder(BenchSetTable): 156 | """Whether the package preserves the order of the keys while dumps 157 | a python dictionary. 158 | 159 | Thus, whether `.dumps({"c": 1, "a": 2, "b": 3})` yields a string 160 | like `c = 1\\na = 2\\nb = 3\\n`. 161 | """ 162 | 163 | title = "Dumping keeps order of keys?" 164 | header = "Order kept?" 165 | api_base = APIBase 166 | case = BenchCaseDumpKeysOrder 167 | 168 | 169 | class BenchSetLoadKeysOrder(BenchSetTable): 170 | """Whether the package preserves the order of the keys 171 | while loads a TOML string. 172 | 173 | Thus, whether `.loads('c = 1\\na = 2\\nb = 3\\n')` yields 174 | a dictionary with keys in the order of `['c', 'a', 'b']`. 175 | """ 176 | 177 | title = "Loading keeps order of keys?" 178 | header = "Order kept?" 179 | api_base = APIBase 180 | case = BenchCaseLoadKeysOrder 181 | 182 | 183 | class BenchSetDumpsUnicode(BenchSetTable): 184 | """How the package dumps Unicode in python 185 | 186 | Literally, `.dumps({"你好": "世界"})` 187 | """ 188 | 189 | title = "Dumping unicode" 190 | header = "Dumped value" 191 | api_base = APIBase 192 | case = BenchCaseDumpsUnicode 193 | 194 | 195 | class BenchSetLoadsUnicode(BenchSetTable): 196 | """How the package loads a file with unicode. 197 | 198 | The file was created by: 199 | 200 | ```python 201 | # Create a file with unicode content 202 | with open(self.datafile, "w", encoding="utf-8") as f: 203 | f.write('"你好" = "世界"\\n') 204 | 205 | # Use `.load()` to load the file 206 | with open(self.datafile, "r", encoding="utf-8") as f: 207 | loaded = .load(f) 208 | ``` 209 | """ 210 | 211 | title = "Loaded unicode" 212 | header = "Loaded as" 213 | api_base = APIBase 214 | case = BenchCaseLoadsUnicode 215 | 216 | def prepare_cases(self): 217 | 218 | datafile = self.args.datadir / "unicode" / "unicode.toml" 219 | datafile.parent.mkdir(parents=True, exist_ok=True) 220 | with open(datafile, "w", encoding="utf-8") as f: 221 | f.write('"你好" = "世界"\n') 222 | 223 | for case in self.cases: 224 | case.datafile = datafile 225 | case.prepare() 226 | 227 | 228 | class BenchSetComplianceValid(BenchSetTable): 229 | """Test the compliance with the standard test suites for valid toml files 230 | here: 231 | 232 | > https://github.com/BurntSushi/toml-test/ 233 | 234 | The tests come up with a JSON counterpart that can be used to valid whether 235 | loading the toml file yields the same result as the JSON counterpart. 236 | """ 237 | title = "Compliance with valid tests in toml-test" 238 | api_base = APIBase 239 | case = BenchCaseComplianceValid 240 | 241 | @property 242 | def header(self) -> str: 243 | return f"Result (toml-test v{self.args.comver})" 244 | 245 | def prepare_cases(self): 246 | 247 | ver = self.args.comver 248 | datafile = self.args.datadir / "compliance" / f"toml-test-{ver}.zip" 249 | datadir = self.args.datadir / "compliance" / f"toml-test-{ver}" 250 | datadir.parent.mkdir(parents=True, exist_ok=True) 251 | url = f"{TOML_TEST_REPO}/archive/refs/tags/v{ver}.zip" 252 | if not datadir.exists() or self.args.nocache: 253 | if not datafile.exists(): 254 | with urllib.request.urlopen(url) as resp, datafile.open( 255 | "wb" 256 | ) as f: 257 | f.write(resp.read()) 258 | with zipfile.ZipFile(datafile) as zf: 259 | zf.extractall(datadir.parent) 260 | 261 | for case in self.cases: 262 | case.datadir = datadir 263 | case.prepare() 264 | 265 | 266 | class BenchSetComplianceInvalid(BenchSetTable): 267 | """Test the compliance with the standard test suites for invalid toml files 268 | here: 269 | 270 | > https://github.com/BurntSushi/toml-test/ 271 | 272 | - `Not OK`: The toml file is parsed without error, but expected to fail. 273 | - `OK`: All files are failed to parse, as expected. Showing the last 274 | parsing error. 275 | """ 276 | title = "Compliance with invalid tests in toml-test" 277 | api_base = APIBase 278 | case = BenchCaseComplianceInvalid 279 | 280 | @property 281 | def header(self) -> str: 282 | return f"Result (toml-test v{self.args.comver})" 283 | 284 | def prepare_cases(self): 285 | 286 | ver = self.args.comver 287 | # data prepared in BenchSetComplianceValid 288 | for case in self.cases: 289 | case.datadir = self.args.datadir / "compliance" / f"toml-test-{ver}" 290 | case.prepare() 291 | 292 | 293 | class BenchSetTomllibComplianceValid(BenchSetTable): 294 | """Test the compliance with python tomllib test data (since python 3.11) 295 | for valid toml files here: 296 | 297 | > https://github.com/python/cpython/tree/3.11/Lib/test/test_tomllib/data/valid 298 | 299 | The tests come up with a JSON counterpart that can be used to valid whether 300 | loading the toml file yields the same result as the JSON counterpart. 301 | """ # noqa: E501 302 | title = "Compliance with valid tests in python tomllib test data" 303 | api_base = APIBase 304 | case = BenchCaseTomllibComplianceValid 305 | 306 | @property 307 | def header(self) -> str: 308 | return f"Result (cpython tag {self.args.cpyver})" 309 | 310 | def prepare_cases(self): 311 | 312 | datafile = self.args.datadir.joinpath( 313 | "tomllib-compliance", 314 | f"tomllib-data-{self.args.cpyver}.zip", 315 | ) 316 | datadir = self.args.datadir.joinpath( 317 | "tomllib-compliance", 318 | f"tomllib-data-{self.args.cpyver}", 319 | ) 320 | datadir.parent.mkdir(parents=True, exist_ok=True) 321 | url = f"{TOMLLIB_DATA_REPO}/archive/refs/tags/v{self.args.cpyver}.zip" 322 | if not datadir.exists() or self.args.nocache: 323 | if not datafile.exists(): 324 | with urllib.request.urlopen(url) as resp, datafile.open( 325 | "wb" 326 | ) as f: 327 | f.write(resp.read()) 328 | with zipfile.ZipFile(datafile) as zf: 329 | namelist = zf.namelist() 330 | first = namelist[0] 331 | member_data = f"{first}Lib/test/test_tomllib/data/" 332 | members = [m for m in namelist if m.startswith(member_data)] 333 | zf.extractall(datadir.parent, members=members) 334 | datadir.parent.joinpath(first).rename(datadir) 335 | 336 | for case in self.cases: 337 | case.datadir = datadir 338 | case.prepare() 339 | 340 | 341 | class BenchSetTomllibComplianceInvalid(BenchSetTomllibComplianceValid): 342 | """Test the compliance with python tomllib test data (since python 3.11) 343 | for invalid toml files here: 344 | 345 | > https://github.com/python/cpython/tree/main/Lib/test/test_tomllib/data/invalid 346 | 347 | - `Not OK`: The toml file is parsed without error, but expected to fail. 348 | - `OK`: All files are failed to parse, as expected. Showing the last 349 | parsing error. 350 | """ # noqa: E501 351 | title = "Compliance with invalid tests in python tomllib test data" 352 | case = BenchCaseTomllibComplianceInvalid 353 | 354 | def prepare_cases(self): 355 | 356 | # data prepared in BenchSetTomllibComplianceValid 357 | for case in self.cases: 358 | case.datadir = self.args.datadir.joinpath( 359 | "tomllib-compliance", 360 | f"tomllib-data-{self.args.cpyver}", 361 | ) 362 | case.prepare() 363 | 364 | 365 | class BenchSetSpeed(BenchSetMultiColTable): 366 | 367 | header = ["Loading speed", "Dumping speed"] 368 | api_base = APIBase 369 | case = BenchCaseSpeed 370 | 371 | @property 372 | def package_name(self) -> str: 373 | ... 374 | 375 | @property 376 | def url(self) -> str: 377 | ... 378 | 379 | @property 380 | def title(self) -> str: 381 | return f"Running speed with data provided by `{self.package_name}`" 382 | 383 | def prepare_cases(self): 384 | 385 | datafile = self.args.datadir / "speed" / f"{self.package_name}.toml" 386 | 387 | if not datafile.exists() or self.args.nocache: 388 | datafile.parent.mkdir(parents=True, exist_ok=True) 389 | with urllib.request.urlopen(self.url) as resp, datafile.open( 390 | "wb" 391 | ) as f: 392 | f.write(resp.read()) 393 | 394 | data = datafile.read_text() 395 | 396 | for case in self.cases: 397 | case.data = data 398 | case.prepare() 399 | 400 | 401 | class BenchSetSpeedWithPytomlppData(BenchSetSpeed): 402 | """Test the speed of loading and dumping the loaded 403 | using data provided by `pytomlpp` 404 | 405 | > https://github.com/bobfang1992/pytomlpp/raw/master/benchmark/data.toml 406 | """ 407 | package_name = "pytomlpp" 408 | url = PYTOMLPP_DATA_URL 409 | 410 | 411 | class BenchSetSpeedWithRtomlData(BenchSetSpeed): 412 | """Test the speed of loading and dumping the loaded using data 413 | provided by `rtoml` 414 | 415 | > https://github.com/samuelcolvin/rtoml/raw/main/tests/data.toml 416 | """ 417 | package_name = "rtoml" 418 | url = RTOML_DATA_URL 419 | 420 | 421 | class BenchSetSpeedWithTomliData(BenchSetSpeed): 422 | """Test the speed of loading and dumping the loaded using data 423 | provided by `tomli` 424 | 425 | > https://github.com/hukkin/tomli/raw/master/benchmark/data.toml 426 | """ 427 | package_name = "tomli" 428 | url = TOMLI_DATA_URL 429 | -------------------------------------------------------------------------------- /toml_bench/suite.py: -------------------------------------------------------------------------------- 1 | from benchwork import BenchSuite 2 | 3 | from .sets import ( 4 | BenchSetVersion, 5 | BenchSetDumpNone, 6 | BenchSetDumpValueNone, 7 | BenchSetDumpListWithNone, 8 | BenchSetLoadNoneLike, 9 | BenchSetDumpsHeteroArray, 10 | BenchSetLoadsHeteroArray, 11 | BenchSetDumpsNestedArray, 12 | BenchSetLoadsNestedArray, 13 | BenchSetDumpKeysOrder, 14 | BenchSetLoadKeysOrder, 15 | BenchSetDumpsUnicode, 16 | BenchSetLoadsUnicode, 17 | BenchSetComplianceValid, 18 | BenchSetComplianceInvalid, 19 | BenchSetTomllibComplianceValid, 20 | BenchSetTomllibComplianceInvalid, 21 | # BenchSetSpeedWithPytomlppData, 22 | BenchSetSpeedWithRtomlData, 23 | BenchSetSpeedWithTomliData, 24 | ) 25 | 26 | 27 | class BenchSuite(BenchSuite): 28 | set_classes = [ 29 | BenchSetVersion, 30 | BenchSetDumpNone, 31 | BenchSetDumpValueNone, 32 | BenchSetDumpListWithNone, 33 | BenchSetLoadNoneLike, 34 | BenchSetDumpsHeteroArray, 35 | BenchSetLoadsHeteroArray, 36 | BenchSetDumpsNestedArray, 37 | BenchSetLoadsNestedArray, 38 | BenchSetDumpKeysOrder, 39 | BenchSetLoadKeysOrder, 40 | BenchSetDumpsUnicode, 41 | BenchSetLoadsUnicode, 42 | BenchSetComplianceValid, 43 | BenchSetComplianceInvalid, 44 | BenchSetTomllibComplianceValid, 45 | BenchSetTomllibComplianceInvalid, 46 | # BenchSetSpeedWithPytomlppData, 47 | BenchSetSpeedWithRtomlData, 48 | BenchSetSpeedWithTomliData, 49 | ] 50 | -------------------------------------------------------------------------------- /toml_bench/utils.py: -------------------------------------------------------------------------------- 1 | 2 | import json 3 | import math 4 | from typing import Any, Mapping 5 | from dateutil import parser 6 | 7 | 8 | class NAN: 9 | def __eq__(self, other): 10 | return math.isnan(other) 11 | 12 | def __ne__(self, other): 13 | return not math.isnan(other) 14 | 15 | 16 | def _cast_value(value: Any) -> Any: 17 | if isinstance(value, list): 18 | return [_cast_value(v) for v in value] 19 | if not isinstance(value, dict): 20 | return value 21 | 22 | out = value.copy() 23 | if len(value) == 2 and "value" in value and "type" in value: 24 | if value["type"] == "integer": 25 | return int(value["value"]) 26 | if value["type"] == "array": 27 | return [_cast_value(v) for v in value["value"]] 28 | if value["type"] == "string": 29 | return str(value["value"]) 30 | if value["type"] == "float": 31 | out = float(value["value"]) 32 | if math.isnan(out): 33 | out = NAN() 34 | return out 35 | if value["type"] == "bool": 36 | return value["value"] == "true" 37 | if value["type"] == "datetime": 38 | return parser.isoparse(value["value"]) 39 | if value["type"] == "datetime-local": 40 | return parser.parse(value["value"]) 41 | if value["type"] == "date": 42 | return parser.isoparse(value["value"]).date() 43 | if value["type"] == "date-local": 44 | return parser.parse(value["value"]).date() 45 | if value["type"] == "time": 46 | return parser.isoparse(value["value"]).time() 47 | if value["type"] == "time-local": 48 | return parser.parse(value["value"]).time() 49 | 50 | return out["value"] 51 | 52 | for key, val in value.items(): 53 | out[key] = _cast_value(val) 54 | return out 55 | 56 | 57 | def load_json_data(file) -> Mapping[str, Any]: 58 | data = json.load(file) 59 | return _cast_value(data) 60 | --------------------------------------------------------------------------------