Cofig with ini,xml,json,yaml,toml
23-08-29-config.md
TLDR: 选TOML类型
关于参数
众所周知, 写代码有的东西 可能会变
而 常用的方法有, 写死, 写死但是一个全局的变量, 接受环境变量, 接受命令行参数, 接受接口参数, 接受配置文件
对于 参数优先级
1 | 命令行参数(api参数) |
就python的cli程序而言
那么对于 命令行参数,有 argparse/click/自己解析argc,argv
环境变量 有 os.getenv()
内置默认值, 怎么写其实都行
那么本文主要关注的是, 配置文件
配置文件
同语言
对于python的配置文件, 其实完全可以用python来写, 也就是用同语言直接来写,同理ts的软件的配置可以用ts来写,
优点是,支持了复杂性,易读(本身是逻辑语言),而且如果本身用户也是 从编码层面使用会很方便
缺点是,对于简单的配置不必要,用户不需要学相应的语言,有些语言是编译语言, 而如果真的是这种情况,可能你更需要的是开发一个配置管理界面,而不是考虑配置文件格式?
而其它配置文件, 如果层级缺失, 当工程变大时, 多个配置项的交叉发生时,容易出现 “配置的表述” 和 “期望的逻辑” 之间的 不一致, 这个时候你还需要去理解工具的工作原理, 反而把本来只需要理解配置的工作扩大成理解工具了
比较
其实除了 同语言,其它都是”简单的配置文件”了, 而关于模块化,应该是工具关心的不是格式关心的?
- 环境变量(1979)
- https://en.wikipedia.org/wiki/Environment_variable
- 你可以在~/.bashrc 中source其它的文件,实现”模块化”
- 同样注释 是使用的shell的注释
- ini(1981), initialization
- https://en.wikipedia.org/wiki/INI_file
- 其中wiki里 parser的部分可以看到不同parser的支持程度不同
- 虽然有支持section nesting的parser, 但是 书写上还是 Key/Value的形式, 在书写上看不见层级
- 另外虽然configparser提供了getboolean/getint/getfloat, 但实际上 直接读取还是字符串
- xml(1996), Extensible Markup Language
- https://en.wikipedia.org/wiki/XML
- 层级!层级!层级!
- 主要就是靠start-tag/end-tag
- 而本质上还是没有类型规范
- 同时还有 字符串转义的问题
- json(2000), JavaScript Object Notation
- https://en.wikipedia.org/wiki/JSON
- 类型!类型!类型!
- 从名字可以看出和js有关,而这关联性,让它的类型系统更明确了
- 有了 (string,number,object(字典),array,boolean,null),同时python和其它语言也有支持的库
- 而且从书写上讲, 上面的start-tag/end-tag需要重复的内容,这里书写上更简洁了
- 然后不能注释,也是问题所在,虽然现在有jsonc可以写注释了,例如tsconfig.json中可以了,但是还没有变成默认标准
- 同样,因为有了 花括号 双引号这些,你可以一定程度上多行写成一行
- yaml(2001),yet another markup language
- https://yaml.org/
- 书写上可以比json少不少 可见字符,(花括号,双引号)
- 类型上多了date/timestamp
- 还提供了inheritance/reference, 的操作符号
&/*/<<
- 然而经常被大家说的还是 yaml的类型, 单引号,双引号,无引号都可以是字符串, 然而如果有一天,一个人想把 版本号 3.10.3(字符串) 改成 3.10 那么这将是浮点类型 而不是字符串
- 很依赖于缩进
- toml(2013), Tom’s Obvious Minimal Language
- https://toml.io/en/
- 在yaml的类型上多了table
- 比yaml的类型系统的书写更精确
- 不依赖缩进,而层级可以自由选择是否tab,也可以链式写法
- custom
- proxychains
- apt
- nginx
- 有自己的语法,和特殊的支持,有的因为历史原因,有的自己东西做大了可以自己实现解析器,而conf也可以是ini格式
- | 环境变量 | 同语言 | ini | xml | json | yaml | toml | custom |
---|---|---|---|---|---|---|---|---|
层级支持 | ❌ | ✅ | 半支持 | ✅ | ✅ | ✅ | ✅ | ? |
类型系统 | 字符串 | 同语言 | 字符串 | 字符串 | string,number,map,array,bool,null | 比json多date,timestamp | 比yaml多了table,而类型描述的书写更精确 | ? |
注释支持 | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ? |
现有工具 | os.getenv() |
不需要额外工具 | configparser | xml | json | pyyaml | tomllib | ? |
实际例子 | http_proxy | flask config,webpack.config.js | desktop.ini,setup.ini | pom.xml | 前端配置,前端RESTFUL接口 | docker compose,github actions,jenkins | pyproject.toml | /etc/proxychains.conf ,/etc/apt/sources.list ,/etc/nginx/sites-available/default |
其它 | - | 非编译语言会采用 | - | - | - | 类型书写不够明确,引用感觉多余 | - | - |
锐评
当一个配置文件提供层级,但是相关软件滥用层级时,不是配置文件的问题,是软件设计的问题
另外,当配置出错时,你的程序也应该提示配置错误会更好,而不是简单的炸掉
有的时候可能同时采取多个,例如同时支持命令行参数+环境变量+配置文件, 但配置文件就不要多个类型了,求求了
其实从功能上讲, json+注释+日期类型 就很理想了, yaml的 引用感觉太重了,到这种程度就还不如直接用your language,或者用户配置面板, 而除了功能以外,就是书写上了, toml 可以说即精确又小巧