首页 > 编程语言 > 详细

python之environs

时间:2020-07-04 16:01:50      阅读:60      评论:0      收藏:0      [点我收藏+]

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

安装:

  

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)

 

python之environs

原文:https://www.cnblogs.com/xingxia/p/python_environs.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!