关于如何配置一个Python程序,如果你还停留在使用ini,json,yaml,xml等文件格式之争,或者还在为使用哪个库来加载配置纠结,只能说明你还没有开发过一个严肃认真的大型Python应用程序。
关于Python程序读取配置,一个严肃认真的大型程序,真正面临的问题其实是这些:
- 不小心写错了一个配置项,却没有语法检查工具来帮助检查,直到程序崩溃,才发现是配置项写错了。
- 部署时要在不同的机器间不断地copy/paste配置文件,而且还要逐一修改。比如开发完成后,测试人员要拿着开发给的配置文件,在测试机器上按照测试环境进行修改,运维人员在部署时,也需要修改配置文件,而"修改"这个动作又是无法被测试覆盖的。
- 账号和口令写在配置文件中,有可能在分享源文件的过程中泄漏出去。特别是在项目是开源的、或者未来可能开源的情况下。
- 搭建新工程时,要把时间花在配置诸如logging, database, caceh, message queue这些服务上,而不是真正有效的业务逻辑上。
- 服务是由许多台负载均衡的服务器来完成的。如果使用文件型的配置,在修改配置时,就需要登录到这些机器上逐一修改;如果使用数据库来进行中心化配置,但有时为了故障排除或者其它原因,又需要对某几台机器进行小量的配置修改。
- 更改配置必须重启服务程序,从而使得服务中断。
现在, cfg4py为上述问题提供了一个完整的解决方案。
cfg4py是一个层次化(cascading design)、部署环境自适应(Adaptive deployment)的配置系统。 它支持配置中心化,但又能通过本地文件覆盖全局配置;为不同的环境(开发、测试和生产环境)准备了不同的配置文件,相同的配置项提取在defaults.yaml中,使得部署过程中几乎无须修改。
它还提供了模板功能,可以供在项目创建之初,快速配置各种服务和日志模块。
它通过将yaml文件中的配置项编译成为Python class,从而使得你可以享受到代码自动补全的便捷,代码重构也成为可能:现在你可以象重命名普通变量一样来重命名一个配置项了!
此外,它还提供了环境变量宏,使用cfg4py,你只需要在配置文件中引用定义账号、密码的环境变量,程序就能最终获得这些信息,而无须在源码和配置文件中暴露这些秘密。
模板
当你新建一个Python工程时,你可以在安装了cfg4py的环境下,运行 python scaffold命令,按照提示将对应的配置项加入你的工程中:
Auto-complete (自动补全)
当你写好配置后,在命令行下运行cfg4py build,这样就会生成一个名为cfg4py_auto_gen.py的文件,将它导入到工程中,就可以使用代码自动提示了:
当然,每次新增或者修改了配置文件时,都需要做一次build。如果你需要对配置文件进行重构,现在变得很简单,因为所有的配置项目都变成了对象的一个属性,所以,如果你在开发过程中,突然觉得某个配置项的名称不顺口,没关系,象重命名一个普通变量一样来重构它,所以引用这个配置项的地方,都将自动更新!
使用宏来读取环境变量
使用宏来读取环境变量有两个用处,一是不在任何文件中配置账号和密码。二是你也可以将因机器差异,而需要进行不同配置的项放进环境变量,从而使得配置文件在不同的机器上完全一致,无需修改。这样会减少很多部署的工作,避免一些错误。
比如,你申请了一个第三方的服务账号,需要配置在程序中,你当然不希望将账号和密码泄露出去。现在,你可以机器上定义环境变量:
export vendor_account=my_account
export vendor_password=my_password
然后在你的配置文件中引用它们:
# in your configuration file, for example: defaults.yaml
third_party_service:
account: ${vendor_account}
password: ${password}
# this is how you could access these secrets:
cfg = cfg4py.init(...)
print(cfg.third_party_service.account, cfg.thired_party_service.password)
甚至开发团队之间都不应该共享这些账号。现在,这些账号都配置为本机的环境变量,从而避免了泄漏。
Cascading design (多层设计)
cfg4py允许你通过远程的中央数据源来统一进行配置。
如果这个中央数据源是redis,那么只要指定redis连接和配置项的键值,cfg4py将自动为你加载这些配置(按照你指定的refresh period)。注意cfg4py只支持yaml格式,所以即使这些配置不是写在文件中,而是在cache中,它们也应该是yaml格式。
如果中央数据源是其它类型的数据库,则你需要自己实现一个RemoteConfigFetcher, 并实现它的fetcher接口。
class MyConfigFetcher(RemoteConfigFetcher):
def __init__(self, *arg, **kwargs):
def fetch(self) -> str:
# fetch settings from database, decode as string, and return pass
然后调用config_remote_fetcher,让cfg4py加载这个fetcher:
fetcher = MyConfigFetcher()
cfg4py.config_remote_fetcher(fetcher, interval = 500)
在上述例子中,cfg4py每5分钟会从中央数据源更新一次配置。 如果个别机器需要有自己的配置,则只需要在本地写一个配置文件,改写部分配置即可。cfg4py会自动完成配置的合并和改写。
Adaptive deployment (部署环境自适应)
一个典型的cfg4py配置文件系统共有4个配置文件:
1. defaults.yaml
2. dev.yaml
3. test.yaml
4. production.yaml
通过cfg4py scaffold命令初始化后,你的工程中就会出现这些文件。然后你需要配置当前机器的角色环境变量:
export __cfg4py_server_role__=DEV
这个变量名很长,不容易记忆。但是你可以在控制台下通过cfg4py set-server-role来查看帮助,这个命令会显示跟上面一样的提示。
如果这个命令也太长,不容易记忆?你只要记住cfg4py就好,cfg4py提供了命令行提示,连续敲两个tab,就会提示你所有的命令:
其它功能
更新配置是热更新。如果配置是从本地文件加载的,更新将是实时更新;如果配置是远程加载的,那么由你定义的更新间隔来决定。
忘记该怎么设置pip源或者conda源?试试cfg4py hint命令吧。
这么好的模块,怎么以前没有发现它呢?不错,这是个新开发出来的库,不过质量应该到了可以使用的阶段了,测试覆盖率也达到了94%。