type
status
date
slug
summary
tags
category
icon
password
URL
1. 关于TOML
TOML (Tom's Obvious, Minimal Language)是由Tom Preston-Werner, Pradyun Gedam等人制订的一种配置文件语言,就像json和yaml一样。TOML的目标是保持配置文件的小体积并不失其可读性。它的设计可使配置的键和值很容易的映射到哈希表中,这也使其很容易被解析成不同语言对应的数据结构。
1.1 和其他配置语言的比较
TOML和其他配置语言有很多的共同点。比如TOML和JSON都有简单且通用的数据类型,这使得他们很容易被机器编码或者识别。TOML和YAML都很注意可读性。比如他们都支持注释。但是TOML是取二者之长,允许注释(JSON不允许)和保持简单(YAML结构看起来稍微复杂)。
INI看起来很你TOML,但是它没有一个统一的标准,没有数据类型,也不支持嵌套。
2. TOML在python中的应用
TOML已经被广泛应该在python中。最著名的应该是 PEP 621 - Storing project metadata in pyproject.toml, 这个PEP已经final了,也就是说官方鼓励使用pyproject.toml来保存python项目的元信息。越来越多的python工具也都支持从pyproject.toml中读取配置,例如poetry, mypy, pytest, black等等,就连pip也开始支持了。
2022年1月,PEP 680 - tomllib: Support for Parsing TOML in the Standard Library 也已经被接受了。也就是说在python3.11中,一个新的内置库tomllib将加入用来解析toml配置文件。
3. python中的toml包
目前已经有很多包可以解析和写入toml配置文件,包括:
- toml是一个完全python解析的toml库
- rtoml的底层则是用rust实现的
- pytomlpp是用c++来解析toml
- tomli也是用python来解析的,值得一提的是PEP680也将主要由该包的作者贡献
- qtoml也是由python来解析。作者写这个包纯粹是觉得uiri/toml太慢了
- tomlkit也是由python解析。该包是由大名鼎鼎的poetry的作者所写,也是poetry的依赖之一
4. 该用哪个包呢?
由于每个项目的应该场景不同,每个开发者关心的点也不同,所以在使用的时候就需要做出取舍。下面我们将从几个不同的方面来讨论:
4.1 版本
文章中讨论所用到的包的版本如下:
4.2 对 None 的处理
标准的TOML是不支持None的 github.com/toml-lang/to
那么如果要写入一个含有None值的配置,各个包是如何处理的呢?
- 如果单纯的只dump None,也就是
<package>.dumps(None):
rtoml会给出一个
"null"字符串,而其他的包都是报错- 如果是dump一个值为None的键值对,也就是
<package>.dumps({"key": None})
toml会忽略这个键值对,给出一个空字符串;rtoml会给出
'key = "null"\n' ;而其他的包都会报错- 如果None在一个列表中 (
<package>.dumps({"key": [1, 2, 3, None, 5]})
toml给出
'key = [ 1, 2, 3, "None", 5,]\n'; rtoml给出 'key = [1, 2, 3, "null", 5]\n'; 而其余的包则均报错- 有的包会把None dump成
"None"或者"null", 那么反过来呢,这些特殊的字符串在被解析的时候会不会解析成None呢?然而都不会,所有的包都会保留为字符串,不会解析成python中的None
4.3 键的有序性保留
我们知道,从python3.7开始,字典中键的顺序是保留的,也就是说
{"a": 1, "b": 2} 的键的顺序始终为["a", "b"] 。那么当我们dump这个字典的时候,是否一定是而不是
呢?
那么反过来呢?如果toml配置,其中
"b" 键在前(上面最后那个例子),那么加载的时候,是加载成 {"b": 2, "a": 1} 还是随机的呢?在这些包中,除子 pytomlpp,其他的包都保留了键的顺序。并且pytomlpp并不打算实现这个功能:github.com/bobfang1992/
4.4 对unicode的支持
经过测试,所有的包都支持unicode。值得一提的是,toml配置文件必须是utf-8编码的:
A TOML file must be a valid UTF-8 encoded Unicode document.
当加载一个配置文件时,
tomli.load(f)必须值"rb" 模式打开。open(file, "r", encoding="utf-8")是不被允许的。4.5 对TOML标准的兼容性
为了测试对TOML标准的兼容性,
专门收集了一些针对TOML标准的toml配置文件,包括一些有效合法的toml文件和不合TOML标准的toml文件。也就是说要做到对TOML标准的兼容,一个包不仅仅要正确的解析那些合法的toml文件,还需要在解析不合标准的toml文件时给出错误提示。下面使用toml-test v1.1.0 进行测试。
- 对合法的toml文件的解析
- toml: 86/99 (86.87%) passed
- rtoml:99/99 (100%) passed
- pytomlpp:99/99 (100%) passed
- tomli:99/99 (100%) passed
- qtoml:95/99 (95.96%) passed
- tomlkit:99/99 (100%) passed
- 对非法toml文件的解析 (通过则说明在解析时给出了适当的错误提示)
- toml: 169/214 (78.97%) passed
- rtoml:209/214 (97.66%) passed
- pytomlpp:214/214 (100%) passed
- tomli:214/214 (100%) passed
- qtoml:95/99 (95.96%) passed
- tomlkit:208/214 (97.20%) passed
从上面的结果可以看到,pytomlpp和tomli是兼容TOML标准对好的包,rtoml和qtoml次之。
4.6 速度
解析速度也是很多人比如关心的一个特性。我们使用了3个toml文件进行评估。所有的测试结果都是对数据文件进行5000次读取所产生的。
- 由pytomlpp提供的Benchmarkgithub.com/bobfang1992/
- toml:6.93s (5000 iterations)
- rtoml:0.72s (5000 iterations)
- pytomlpp:0.96s (5000 iterations)
- tomli:3.46s (5000 iterations)
- qtoml:10.77s (5000 iterations)
- tomlkit:66.00s (5000 iterations)
- 由rtoml提供的benchmarkgithub.com/samuelcolvin
- toml:18.47s (5000 iterations)
- rtoml:1.00s (5000 iterations)
- pytomlpp:1.35s (5000 iterations)
- tomli:8.12s (5000 iterations)
- qtoml:20.72s (5000 iterations)
- tomlkit:160.62s (5000 iterations)
- 由tomli提供的benchmark github.com/hukkin/tomli
- toml:10.63s (5000 iterations)
- rtoml:0.87s (5000 iterations)
- pytomlpp:1.21s (5000 iterations)
- tomli:4.49s (5000 iterations)
- qtoml:16.00s (5000 iterations)
- tomlkit:97.27s (5000 iterations)
rtoml在速度上胜出,pytomlpp的差距也很小。tomlkit最慢。
5. 总结
总体来看,各个包各有优缺点。但无论是从兼容性还是速度上,toml和tomlkit都不推荐。如果你不需要保留键的顺序,那么pytomlpp是一个不错的选择。如果你考虑到将来要兼容python11的tomllib,那tomli也是不错的选择。但我个人不喜欢的一点是,它把读(tomli)和写(tomli_w)分成了两个包。rtoml是我目前的选择。
需要提到的是,对于rtoml和pytomlpp,目前在win,linux,macos是有编译好的binary的,在没有binary的平台上,要从头编译c++或者rust,也是需要考虑的一个问题。
文中所有的测试代码和报告来自: