Python 基于多环境的配置方式

部署到不同环境的应用会使用到各自的配置,如 Dev, QA, Stg, Prod 有自己的数据库等资源。Spring Boot 可采用 Profile 对应不同的环境,不同 Profile 选择自己的配置文件 application-${profile}.properties。本人还是偏爱在同一个文件中分组配置,容易查错与编辑,类如在 application.properties 文件中以下面的方式

db.host=aaa
%dev.db.host=bbb
%prod.db.host=ccc 

那么在 Python 的项目中应该如何针对不同环境进行配置呢?大概有以下几种

  1. 不同环境的 Config 类
  2. YAML 文件
  3. TOML 文件
  4. JSON 文件
  5. INI 文件
  6. dotenv(.env) 文件

第一种方式是本人推荐的,其他的方式只是在不同格式的配置文件中,按环境组织不同的配置值,其他方式的不同配置读入内存中基本是体现为字典变量。在 Python 配置中要支持像配置的 placeholder(像 ${host} 还需自己实现。

不同环境的 Config 类

在同一个文件中配置,方便用点号引用

使用

配置环境变量 APP_ENVdev, qa, 或 prod 会输出不同的 DB_HOST 值

YAML 文件

需安装依赖 pyyaml

pip install pyyaml

config.yml 文件配置

使用

cfg 是一个字典,所以上面的输出为

<class 'dict'>
{'default': {'db_host': 'qa.example.com'}, 'dev': {'db_host': 'dev.example.com'}, 'qa': None, 'prod': {'db_host': 'prod.example.com'}}

如果结合环境变量 APP_ENV 从 cfg 中获取配置值

每次取值有些麻烦, 更高级一点的玩法是让 YAML 序列化为一个自定义对象,然后在自定义类中做文章,如新的 config.yml 配置中要告诉对应的类名

然后定义 Config 类,并使用相应的配置项

上面的代码输出

<class '__main__.Config'>
dev.example.com

 再改变 APP_ENV 环境变量为 qa 和 prod 时对应的 cfg["db_host"] 的值分别为

qa.example.com
prod.example.com

TOML 文件

TOML(Tom's Obvious Minimal Language),初看它的格式像 ini 文件,其实它对 ini 格式强悍许多,支持丰富的数据类型,如布尔型,整数,浮点数,时间,日期,列表和字典等,下面是官方的一个配置样例

Python 的项目管理工具 Poetry 就是用 pyproject.toml 文件来管理依赖配置的。

使用 toml 一般安装

pip install toml

把前面的 config.yml 文件转换为 config.toml 文件,内容如下

加载该 toml 文件

输出为

<class 'dict'>
{'default': {'db_host': 'qa.example.com'}, 'dev': {'db_host': 'dev.example.com'}, 'qa': {}, 'prod': {'db_host': 'prod.example.com'}}

要取随 APP_ENV 环境而变的 db_host 的话,代码可实现为

变更 APP_ENV 环境变量的值为 qa, prod, 会输出以下相应的值

qa.example.com
prod.example.com

Config 类的 __init____getitem__ 方法实现与前面的完全一样。

TOML 配置文件的表现力很丰富,更强大的功能还有待于日后去发掘。

JSON 文件

上面相应的配置文件变成 config.json 就是

使用代码

输出

<class 'dict'>
{'default': {'db_host': 'qa.example.com'}, 'dev': {'db_host': 'dev.example.com'}, 'qa': {}, 'prod': {'db_host': 'prod.example.com'}}
qa.example.com

切换 APP_ENV 环境变量测试不同环境下的 db_host 值

json.load() 方法也能由 JSON 格式数据反序列化为一个自定义的对象,直接用 object_hook 参数把有嵌套的 JSON 转换成一个自定义对象可就不那么容易了。但通过自定义的 __init__ 方法就和前面 YAML 的例子差不多了

或者整体 JSON 对象可转换为一个 SimpleNamespace

INI 文件

ini 文件以前广泛应用在 Windows 中作为配置文件的格式,Python 也内置了对它的支持,格式上有点像 TOML 但它不支持嵌套类型。这里只提下 INI 文件的简单读取

config.ini

使用

输出为

<class 'configparser.ConfigParser'>
dev.example.com

由于只有一个层次的 Section 系列,不易于扩展,实际中应用较为狭窄,不作细究。

dotenv(.env) 文件

基本思路是把 .env 文件中的配置转换为环境变量,可由 os.environ().get(key) 获得,相当于 Linux 下的环境配置 env.sh

然后

source env.sh

相应的 DOMAIN 和 ADMIN_EMAIL 就出现在了 env 列出的环境变量中

Python 的 dotenv 有两个实现库

第一个是 python-dotenv, 安装

pip install python-dotenv

我们在当前目录中创建一个 .env 文件,其中内容为

使用方式

输出

example.org
admin@example.org

load_dotenv() 可以指定不同的文件, 例如采用基于环境区分的文件命名

  • .env      -- 默认的配置
  • .env_dev
  • .env_qa
  • .env_prod

加载文件

先加载默认的 .env, 再加载环境相关的 .env_dev,这样 .env_dev 中的相同属性会覆盖 .env 中的配置。

还有一个 django 的实现 django-environ, 但其中夹带了太多的私货, 如 environ.Env() 中有一些特定的配置项(db_url, cache_url 等),严格来说,它算不上通用 dotenv 实现。它加载 .env 文件时的行为与 python-dotenv 类似,如

django-environ 中配置的值可以有类型,如 str, bool, int 等。它也像 python-dotenv 一样把 .env 文件中配置加到 os.environ 中去,因此既可通过 os.environ 来获取 .env 文件中配置的值,也能用它自己专有的 environ.Env() 的方式取得值。

另外,比起 python-dotenv 弱的地方就是它不支持 placeholder 的解析,.env 配置中的 ${DOMAIN} 将会被原样输出。

本文链接 https://yanbin.blog/python-multi-envs-configurations/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments