Python 使用 environs 库定义环境变量的方法

Environs是解析环境变量的Python库。它的开发受envparse启发,底层使用marshmallow验证并序列化值。

在运行一个项目的时候,我们经常会遇到设置不同环境的需求,如设置是开发环境、测试环境还是生产环境,或者在某些设置里面可能还需要设置一些变量开关,如设置调试开关、日志开关、功能开关等等。

这些变量其实就是在项目运行时我们给项目设置的一些参数。这些参数一般情况来说,可以有两种设置方法,一种是通过命令行参数,一种是通过环境变量。二者的适用范围不同,在不同的场景下我们可以选用更方便的方式来实现参数的设置。

本节我们以 Python 项目为例,说说环境变量的设置。

设置和获取环境变量

首先,我们先来了解一下在 Python 项目里面怎样设置和获取变量。

首先让我们定义一个最简单的 Python 文件,命名为 main.py,内容如下:

import os
print(os.environ['VAR1'])

在这里我们导入了 os 模块,它的 environ 对象里面就包含了当前运行状态下的所有环境变量,它其实是一个 os._Environ 对象,我们可以通过类似字典取值的方式从中获取里面包含的环境变量的值,如代码所示。

好,接下来我们什么也不设置,直接运行,看下结果:

python3 main.py

结果如下:

raise KeyError(key) from None
KeyError: 'VAR1'

直接抛出来了一个错误,这很正常,我们此时并没有设置 VAR1 这个环境变量,当然会抛出键值异常的错误了。

接下来我们在命令行下进行设置,运行如下命令:

VAR1=germey python3 main.py

运行结果如下:

germey

可以看到我们在运行之前,在命令行之前通过键值对的形式对环境变量进行设置,程序就可以获取到 VAR1 这个值了,成功打印出来了 germey。

但这个环境变量是永久的吗?我们这次再运行一遍原来的命令:

python3 main.py

结果如下:

raise KeyError(key) from None
KeyError: 'VAR1'

嗯,又抛错了。

这说明了什么,在命令行的前面加上的这个环境变量声明只能对当前执行的命令生效。

好,那既然如此,我难道每次运行都要在命令行前面加上这些声明吗?那岂不麻烦死了。

当然有解决方法,我们使用 export 就可以了。

比如这里,我们执行如下命令:

export VAR1=germey

执行完这个命令之后,当前运行环境下 VAR1 就被设置成功了,下面我们运行的命令都能获取到 VAR1 这个环境变量了。

下面来试试,还是执行原来的命令:

python3 main.py

结果如下:

germey

可以,成功获取到了 VAR1 这个变量,后面我们运行的每一个命令就都会生效了。

但等一下,这个用了 export 就是永久生效了吗?

其实并不是,其实这个 export 只对当前的命令行运行环境生效,我们只要把命令行关掉再重新打开,之前用 export 设置的环境变量就都没有了。

可以试试,重新打开命令行,再次执行原来的命令,就会又抛出键值异常的错误了。

那又有同学会问了,我要在每次命令行运行时都想自动设置好环境变量怎么办呢?

这个就更好办了,只需要把 export 的这些命令加入到 ~/.bashrc 文件里面就好了,每次打开命令行的时候,系统都会自动先执行以下这个脚本里面的命令,这样环境变量就设置成功了。当然这里面还有很多不同的文件,如 ~/.bash_profile 、~/.zshrc 、~/.profile、/etc/profile 等等,其加载是有先后顺序的,大家感兴趣可以去了解下。

好了,扯远了,我们现在已经了解了如何设置环境变量和基本的环境变量获取方法了。

更安全的获取方式

但是上面的这种获取变量的方式实际上是非常不友好的,万一这个环境变量没设置好,那岂不是就报错了,这是很不安全的。

所以,下面再介绍几种比较友好的获取环境变量的方式,即使没有设置过,也不会报错。

我们可以把中括号取值的方式改成 get 方法,如下所示:

import os
print(os.environ.get('VAR1'))

这样就不会报错了,如果 VAR1 没设置,会直接返回 None,而不是直接报错。

另外我们也可以给 get 方法传入第二个参数,表示默认值,如下所示:

import os
print(os.environ.get('VAR1', 'germey'))

这样即使我们如果设置过 VAR1,他就会用 germey 这个字符串代替,这就完成了默认环境变量的设置。

下面还有几种获取环境变量的方式,总结如下:

import os
print(os.getenv('VAR1', 'germey'))

这个方式比上面的写法更简单,功能完全一致。

弊端

但其实上面的方法有一个不方便的地方,如果我们想要设置非字符串类型的环境变量怎么办呢?比如设置 int 类型、float 类型、list 类型,可能我们的写法就会变成这个样子:

import os
import json

VAR1 = int(os.getenv('VAR1', 1))
VAR2 = float(os.getenv('VAR2', 5.5))
VAR3 = json.loads(os.getenv('VAR3'))

然后设置环境变量的时候就变成这样子:

export VAR1=1
export VAR2=2.3
export VAR3='["1", "2"]'

这样才能成功获取到结果,打印出来结果如下:

1
2.3
['1', '2']

不过看下这个,写法也太奇葩了吧,又是类型转换,又是 json 解析什么的,有没有更好的方法来设置。

environs

当然有的,下面推荐一个 environs 库,利用它我们可以轻松地设置各种类型的环境变量。

这是一个第三方库,可以通过 pip 来安装:

pip3 install environs

好,安装之后,我们再来体验一下使用 environs 来设置环境变量的方式。

from environs import Env

env = Env()
VAR1 = env.int('VAR1', 1)
VAR2 = env.float('VAR2', 5.5)
VAR3 = env.list('VAR3')

这里 environs 直接提供了 int、float、list 等方法,我们就不用再去进行类型转换了。

与此同时,设置环境变量的方式也有所变化:

export VAR1=1
export VAR2=2.3
export VAR3=1,2

这里 VAR3 是列表,我们可以直接用逗号分隔开来。

打印结果如下:

1
2.3
['1', '2']

官方示例

下面我们再看一个官方示例,这里示例了一些常见的用法。

首先我们来定义一些环境变量,如下:

export GITHUB_USER=sloria
export MAX_CONNECTIONS=100
export SHIP_DATE='1984-06-25'
export TTL=42
export ENABLE_LOGIN=true
export GITHUB_REPOS=webargs,konch,ped
export COORDINATES=23.3,50.0
export LOG_LEVEL=DEBUG

这里有字符串、有日期、有日志级别、有字符串列表、有浮点数列表、有布尔。

我们来看下怎么获取,写法如下:

from environs import Env

env = Env()
env.read_env() # read .env file, if it exists
# required variables
gh_user = env("GITHUB_USER") # => 'sloria'
secret = env("SECRET") # => raises error if not set

# casting
max_connections = env.int("MAX_CONNECTIONS") # => 100
ship_date = env.date("SHIP_DATE") # => datetime.date(1984, 6, 25)
ttl = env.timedelta("TTL") # => datetime.timedelta(0, 42)
log_level = env.log_level("LOG_LEVEL") # => logging.DEBUG

# providing a default value
enable_login = env.bool("ENABLE_LOGIN", False) # => True
enable_feature_x = env.bool("ENABLE_FEATURE_X", False) # => False

# parsing lists
gh_repos = env.list("GITHUB_REPOS") # => ['webargs', 'konch', 'ped']
coords = env.list("COORDINATES", subcast=float) # => [23.3, 50.0]

通过观察代码可以发现它提供了这些功能:

  • 通过 env 可以设置必需定义的变量,如果没有定义,则会报错。
  • 通过 date、timedelta 方法可以对日期或时间进行转化,转成 datetime.date 或 timedelta 类型。
  • 通过 log_level 方法可以对日志级别进行转化,转成 logging 里的日志级别定义。
  • 通过 bool 方法可以对布尔类型变量进行转化。
  • 通过 list 方法可以对逗号分隔的内容进行 list 转化,并可以通过 subcast 方法对 list 的每个元素进行类型转化。

可以说有了这些方法,定义各种类型的变量都不再是问题了。

支持类型

总的来说,environs 支持的转化类型有这么多:

env.str
env.bool
env.int
env.float
env.decimal
env.list (accepts optional subcast keyword argument)
env.dict (accepts optional subcast keyword argument)
env.json
env.datetime
env.date
env.timedelta (assumes value is an integer in seconds)
env.url
env.uuid
env.log_level
env.path (casts to a pathlib.Path)

这里 list、dict、json、date、url、uuid、path 个人认为都还是比较有用的,另外 list、dict 方法还有一个 subcast 方法可以对元素内容进行转化。

对于 dict、url、date、uuid、path 这里我们来补充说明一下。

下面我们定义这些类型的环境变量:

export VAR_DICT=name=germey,age=25
export VAR_JSON='{"name": "germey", "age": 25}'
export VAR_URL=https://cuiqingcai.com
export VAR_UUID=762c8d53-5860-4d5d-81bc-210bf2663d0e
export VAR_PATH=/var/py/env

需要注意的是,DICT 的解析,需要传入的是逗号分隔的键值对,JSON 的解析是需要传入序列化的字符串。

解析写法如下:

from environs import Env

env = Env()
VAR_DICT = env.dict('VAR_DICT')
print(type(VAR_DICT), VAR_DICT)

VAR_JSON = env.json('VAR_JSON')
print(type(VAR_JSON), VAR_JSON)

VAR_URL = env.url('VAR_URL')
print(type(VAR_URL), VAR_URL)

VAR_UUID = env.uuid('VAR_UUID')
print(type(VAR_UUID), VAR_UUID)

VAR_PATH = env.path('VAR_PATH')
print(type(VAR_PATH), VAR_PATH)

运行结果如下:

<class 'dict'> {'name': 'germey', 'age': '25'}
<class 'dict'> {'name': 'germey', 'age': 25}
<class 'urllib.parse.ParseResult'> ParseResult(scheme='https', netloc='cuiqingcai.com', path='', params='', query='', fragment='')
<class 'uuid.UUID'> 762c8d53-5860-4d5d-81bc-210bf2663d0e
<class 'pathlib.PosixPath'> /var/py/env

可以看到,它分别给我们转化成了 dict、dict、ParseResult、UUID、PosixPath 类型了。

在代码中直接使用即可。

文件读取

如果我们的一些环境变量是定义在文件中的,environs 还可以进行读取和加载,默认会读取本地当前运行目录下的 .env 文件。

示例如下:

from environs import Env

env = Env()
env.read_env()
APP_DEBUG = env.bool('APP_DEBUG')
APP_ENV = env.str('APP_ENV')
print(APP_DEBUG)
print(APP_ENV)

下面我们在 .env 文件中写入如下内容:

APP_DEBUG=false
APP_ENV=prod

运行结果如下:

False
prod

没问题,成功读取。

当然我们也可以自定义读取的文件,如 .env.test 文件,内容如下:

APP_DEBUG=false
APP_ENV=test

代码则可以这么定义:

from environs import Env

env = Env()
env.read_env(path='.env.test')
APP_DEBUG = env.bool('APP_DEBUG')
APP_ENV = env.str('APP_ENV')

这里就通过 path 传入了定义环境变量的文件路径即可。

前缀处理

environs 还支持前缀处理,一般来说我们定义一些环境变量,如数据库的连接,可能有 host、port、password 等,但在定义环境变量的时候往往会加上对应的前缀,如 MYSQL_HOST、MYSQL_PORT、MYSQL_PASSWORD 等,但在解析时,我们可以根据前缀进行分组处理,见下面的示例:

# export MYAPP_HOST=lolcathost
# export MYAPP_PORT=3000

with env.prefixed("MYAPP_"):
 host = env("HOST", "localhost") # => 'lolcathost'
 port = env.int("PORT", 5000) # => 3000

# nested prefixes are also supported:

# export MYAPP_DB_HOST=lolcathost
# export MYAPP_DB_PORT=10101

with env.prefixed("MYAPP_"):
 with env.prefixed("DB_"):
  db_host = env("HOST", "lolcathost")
  db_port = env.int("PORT", 10101)

可以看到这里通过 with 和 priefixed 方法组合使用即可实现分区处理,这样在每个分组下再赋值到一个字典里面即可。

合法性验证

有些环境变量的传入是不可预知的,如果传入一些非法的环境变量很可能导致一些难以预料的问题。比如说一些可执行的命令,通过环境变量传进来,如果是危险命令,那么会非常危险。

所以在某些情况下我们需要验证传入的环境变量的有效性,看下面的例子:

# export TTL=-2
# export NODE_ENV='invalid'
# export EMAIL='^_^'

from environs import Env
from marshmallow.validate import OneOf, Length, Email

env = Env()

# simple validator
env.int("TTL", validate=lambda n: n > 0)
# => Environment variable "TTL" invalid: ['Invalid value.']

# using marshmallow validators
env.str(
 "NODE_ENV",
 validate=OneOf(
  ["production", "development"], error="NODE_ENV must be one of: {choices}"
 ),
)
# => Environment variable "NODE_ENV" invalid: ['NODE_ENV must be one of: production, development']

# multiple validators
env.str("EMAIL", validate=[Length(min=4), Email()])
# => Environment variable "EMAIL" invalid: ['Shorter than minimum length 4.', 'Not a valid email address.']

在这里,我们通过 validate 方法,并传入一些判断条件。如 NODE_ENV 只允许传入 production 和 develpment 其中之一;EMAIL 必须符合 email 的格式。

这里依赖于 marshmallow 这个库,里面有很多验证条件,大家可以了解下。

如果不符合条件的,会直接抛错,例如:

marshmallow.exceptions.ValidationError: ['Invalid value.']

关于 marshmallow 库的用法,大家可以参考:https://marshmallow.readthedocs.io/en/stable/,后面我也抽空写一下介绍下。

最后再附一点我平时定义环境变量的一些常见写法,如:

import platform
from os.path import dirname, abspath, join
from environs import Env
from loguru import logger

env = Env()
env.read_env()

# definition of flags
IS_WINDOWS = platform.system().lower() == 'windows'

# definition of dirs
ROOT_DIR = dirname(dirname(abspath(__file__)))
LOG_DIR = join(ROOT_DIR, env.str('LOG_DIR', 'logs'))

# definition of environments
DEV_MODE, TEST_MODE, PROD_MODE = 'dev', 'test', 'prod'
APP_ENV = env.str('APP_ENV', DEV_MODE).lower()
APP_DEBUG = env.bool('APP_DEBUG', True if APP_ENV == DEV_MODE else False)
APP_DEV = IS_DEV = APP_ENV == DEV_MODE
APP_PROD = IS_PROD = APP_DEV == PROD_MODE
APP_TEST = IS_TEST = APP_ENV = TEST_MODE

# redis host
REDIS_HOST = env.str('REDIS_HOST', '127.0.0.1')
# redis port
REDIS_PORT = env.int('REDIS_PORT', 6379)
# redis password, if no password, set it to None
REDIS_PASSWORD = env.str('REDIS_PASSWORD', None)
# redis connection string, like redis://[password]@host:port or rediss://[password]@host:port
REDIS_CONNECTION_STRING = env.str('REDIS_CONNECTION_STRING', None)

# definition of api
API_HOST = env.str('API_HOST', '0.0.0.0')
API_PORT = env.int('API_PORT', 5555)
API_THREADED = env.bool('API_THREADED', True)

# definition of flags
ENABLE_TESTER = env.bool('ENABLE_TESTER', True)
ENABLE_GETTER = env.bool('ENABLE_GETTER', True)
ENABLE_SERVER = env.bool('ENABLE_SERVER', True)

# logger
logger.add(env.str('LOG_RUNTIME_FILE', 'runtime.log'), level='DEBUG', rotation='1 week', retention='20 days')
logger.add(env.str('LOG_ERROR_FILE', 'error.log'), level='ERROR', rotation='1 week')

这里定义了一些开发环境、日志路径、数据库连接、API 设置、开关设置等等,是从我之前写的一个代理池项目拿来的,大家可以参考:https://github.com/Python3WebSpider/ProxyPool

总结

到此这篇关于Python 使用 environs 库来更好地定义环境变量的文章就介绍到这了,更多相关python 使用 environs 库定义环境变量内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • python 环境变量和import模块导入方法(详解)

    1.定义 模块:本质就是.py结尾的文件(逻辑上组织python代码)模块的本质就是实现一个功能 文件名就是模块名称 包: 一个有__init__.py的文件夹:用来存放模块文件 2.导入模块 import 模块名 form 模块名 import * from 模块名 import 模块名 as 新名称 3. 导入模块本质 import 模块名 ===> 将模块中所有的数据赋值给模块名,调用时需要模块名.方法名() from 模块名 import 方法名 ==>将该方法单独放到当前文件运行一遍

  • python anaconda 安装 环境变量 升级 以及特殊库安装的方法

    Anaconda 是一个旗舰版的python安装包, 因为普通的python没有库, 如果需要安装一些重要的库, 要经常一个一个下载,会非常麻烦. 所以这个一个集成的, 可以手动批量升级的软件. 而且库的安装也很全下载速度快. 从官网下载完以后, next 安装好. 配置环境变量, 把安装的文件夹的绝对路径拷贝到 环境变量的path里面. 不配置python都启动不了, 当然,如果之前安装过其他版本的python 可以考虑把之前多余的环境变量路径删掉. 打开anaconda prompt, 输入

  • Python环境变量设置方法

    Alias Maya中的脚本语言是Mel 和 Python,据说Houdini未来也会把Python作为主要的脚本语言,作为影视特效师,掌握Python语言是必备技能:虽然Maya内置了Python运行时,但是,如果要系统学习Python语言,环境变量还是需要配置一下~ 默认情况下,在windows下安装python之后,系统不会自动添加相应的环境变量.此时在命令行输入python命令是不能执行的,配置方法如下: 1. 首先需要在系统中注册python环境变量:假设python的安装路径为c:\

  • windows下添加Python环境变量的方法汇总

    如果安装的时候没有选择Add Python.exe to Path 导致在命令提示符下执行python 提示 'python' 不是内部或外部命令,也不是可运行的程序或批处理文件. 可以通过 如下两种方法: 方法一:使用cmd命令添加path环境变量 在cmd下输入: path=%path%;D:\Python27  接着按"Enter"回车键. 其中: D:\Python27  是Python的安装目录. 方法二:在环境变量中添加Python目录 (1) 右键点击"计算机&

  • python访问系统环境变量的方法

    本文实例讲述了python访问系统环境变量的方法.分享给大家供大家参考.具体如下: #-------------------------------- # Name: enviroment_variables.py # Author: Kevin Harris # Last Modified: 02/13/04 # Description: This Python script demonstrates # how to acces enviroment variables. #--------

  • Python读取环境变量的方法和自定义类分享

    使用os.environ来读取和修改环境变量: 复制代码 代码如下: import os print (os.environ["TEMP"]) mydir = "c:\\mydir" os.environ["MYDIR"] = mydir print (os.environ["MYDIR"]) pathV = os.environ["PATH"] print (pathV) os.environ["

  • Win10下python3.5和python2.7环境变量配置教程

    本文分别介绍了安装python2和python3的详细方法,分享给大家. 一.Windows系统 很多童鞋问之前的教程怎么没有介绍安装python3.5的,现予以补充更新一下. (一)安装python3.5 1.下载 进入Python官网www.python.org,在"Downloads"下拉菜单中的右半部分直接点击python3.5.2版本即可下载,它会自动下载32位的. 如果需要64位,点击左半部分"Windows",选择第二项"Latest Pyt

  • Python 使用 environs 库定义环境变量的方法

    Environs是解析环境变量的Python库.它的开发受envparse启发,底层使用marshmallow验证并序列化值. 在运行一个项目的时候,我们经常会遇到设置不同环境的需求,如设置是开发环境.测试环境还是生产环境,或者在某些设置里面可能还需要设置一些变量开关,如设置调试开关.日志开关.功能开关等等. 这些变量其实就是在项目运行时我们给项目设置的一些参数.这些参数一般情况来说,可以有两种设置方法,一种是通过命令行参数,一种是通过环境变量.二者的适用范围不同,在不同的场景下我们可以选用更方

  • python解释器pycharm安装及环境变量配置教程图文详解

    1.python解释器安装 下载地址:https://www.python.org/ 打开官网,点击downloads,选择操作系统,以windows为例: 选择python2与python3解释器版本(以python3.6.6及python2.7.16为例): 以python3.6.6为例,根据操作系统下载不同安装包(python2.7.16类似) 下载完成后,双击打开,选择自定义安装: 点击完成后,选择下一步,修改默认安装路径(方便查找,添加环境变量),完成后点击安装 2.配置环境变量 右击

  • thinkPHP5.0框架环境变量配置方法

    本文实例讲述了thinkPHP5.0框架环境变量配置方法.分享给大家供大家参考,具体如下: 允许使用环境变量配置,并且优先级别比在配置文件中要高,因为在读取配置参数的时候,首先会判断环境变量中是否存在该配置. 在开发过程中,可以在应用根目录下面的.env来模拟环境变量配置,.env文件中的配置参数定义格式采用ini方式,例如: app_debug = true app_trace = true 如果你的部署环境单独配置了环境变量,那么请删除.env配置文件,避免冲突. 环境变量配置的参数会全部转

  • Python使用PIL库实现验证码图片的方法

    本文实例讲述了Python使用PIL库实现验证码图片的方法.分享给大家供大家参考,具体如下: 现在的网页中,为了防止机器人提交表单,图片验证码是很常见的应对手段之一.这里就不详细介绍了,相信大家都遇到过. 现在就给出用Python的PIL库实现验证码图片的代码.代码中有详细注释. #!/usr/bin/env python #coding=utf-8 import random from PIL import Image, ImageDraw, ImageFont, ImageFilter _l

  • 在Linux操作系统中修改环境变量的方法

    方法一:在/etc/profile文件中添加变量[对所有用户生效(永久的)] 用VI在文件/etc/profile文件中增加变量,该变量将会对Linux下所有用户有效,并且是"永久的". 要让刚才的修改马上生效,需要执行以下代码 复制代码 代码如下: # source /etc/profile 方法二:在用户目录下的.bash_profile文件中增加变量[对单一用户生效(永久的)] 用VI在用户目录下的.bash_profile文件中增加变量,改变量仅会对当前用户有效,并且是&quo

  • Python 利用pydub库操作音频文件的方法

    最近使用Python调用百度的REST API实现语音识别,但是百度要求音频文件的压缩方式只能是pcm(不压缩).wav.opus.speex.amr,这里面也就wav还常见一点,但是一般设备录音得到的文件都是mp3,这就要把mp3转换为wav,由于python的效率并不高,很多实现都是使用C++或者Java,不过GitHub上有一个项目pydub(https://github.com/jiaaro/pydub/tree/master/pydub)可以暂时解决问题. 安装pydub 直接执行以下

  • python使用openpyxl库读写Excel表格的方法(增删改查操作)

    一.前言 嗨,大家好,我是新发. 最近需要做个小工具,可以通过python来读写Excel,实现增删改查操作.以前用的是xlrd和xlwt这两个python库,今天我要讲的是openpyxl库,我觉得openpyxl比xlrd和xlwt更强大更好用,话不多说,开始吧. 二.安装openpyxl 可以直接通过命令行安装 pip install openpyxl 如果你是内网环境,则可以先在外网下载openpyxl库然后转到内网再安装. openpyxl下载地址:https://pypi.org/p

  • Python基于numpy灵活定义神经网络结构的方法

    本文实例讲述了Python基于numpy灵活定义神经网络结构的方法.分享给大家供大家参考,具体如下: 用numpy可以灵活定义神经网络结构,还可以应用numpy强大的矩阵运算功能! 一.用法 1). 定义一个三层神经网络: '''示例一''' nn = NeuralNetworks([3,4,2]) # 定义神经网络 nn.fit(X,y) # 拟合 print(nn.predict(X)) #预测 说明: 输入层节点数目:3 隐藏层节点数目:4 输出层节点数目:2 2).定义一个五层神经网络:

  • python基于pyDes库实现des加密的方法

    本文实例讲述了python基于pyDes库实现des加密的方法.分享给大家供大家参考,具体如下: 下载及简介地址:https://twhiteman.netfirms.com/des.html 如需要在python中使用des加密,可以直接使用pyDes库加密,该库提供了CBC和ECB两种加密方式. 1.Windows下安装 下载后pyDes-x.x.x.zip并解压后,里面有setup.py文件,使用命令 setup.py --help可查看详细使用. 你可以使用命令python setup.

  • python嵌套函数使用外部函数变量的方法(Python2和Python3)

    python嵌套函数使用外部函数变量的方法,Python2和Python3均可使用 python3 def b(): b = 1 def bchange(): nonlocal b b += 1 bchange() print(b) Python 2 只能这样(利用 mutable 对象): def b(): b = [1] def bchange(): b[0] += 1 bchange() print b[0]

随机推荐