我想在本地环境中运行一个Python脚本,通常在Docker容器中运行。docker-compose。Yml指定了一个env_file,看起来(部分)像下面这样:

DB_ADDR=rethinkdb
DB_PORT=28015
DB_NAME=ipercron

为了在本地运行,我希望将这些行转换为

os.environ['DB_ADDR'] = 'rethinkdb'
os.environ['DB_PORT'] = '28015'
os.environ['DB_NAME'] = 'ipercron'

我可以编写我的解析器,但我想知道是否有任何现有的模块/工具可以从配置文件中读取环境变量?


当前回答

对于这些类型的文件,python- decoupling是一个很好的选择。虽然它通常用于django应用程序,但它对任意环境文件都是可扩展的。

from decouple import Config, RepositoryEnv

env=Config(RepositoryEnv('.env'))

env.get('DB_ADDR') #=> 'rethinkdb'
env.get('DB_PORT') #=> '28015'
env.get('DB_NAME') #=> 'ipercron'

其他回答

我认为您应该把它留给外部工具来为您管理环境。

这样,您就可以轻松地使用像1password cli这样的秘密管理器从加密库中加载环境变量

op run --env-file=.env -- python your_script.py

有了这个load_dotenv足够聪明,如果环境中存在.env变量,而其他一些解决方案则不加载。

如果你没有任何外部工具,可以使用'bash':

set -o allexport; source .env; set +o allexport

方法:从键/值对文件中设置环境变量

我不建议直接从程序中读取.env文件。12因素应用程序中的配置部分的思想是,你从环境变量中读取配置——即不是从文件中。

.env文件只是将这些变量放入环境的一种方便方法(见下文)。如果您开始在代码中直接读取文件,那么这一步就跳过了环境变量和所有后果,基本上又回到了起点,从文件中读取配置。

所以你应该做的是,使用一个工具,如dotenv-cli,读取。env文件,将变量导出到环境中,并在临时修改的环境中运行你的应用程序,如下所示:

$ dotenv yourapp

您可以使用ConfigParser。示例可以在这里找到。

但是这个库希望你的key=value数据出现在某些[标题]下。例如:

[mysqld]
user = mysql  # Key with values
pid-file = /var/run/mysqld/mysqld.pid
skip-external-locking
old_passwords = 1
skip-bdb      # Key without value
skip-innodb

只使用python std

import re

envre = re.compile(r'''^([^=]+)\s+?=\s+?(?:[\s"']*)(.+?)(?:[\s"']*)$''')
result = {}
with open('/etc/os-release') as ins:
    for line in ins:
        match = envre.match(line)
        if match is not None:
            result[match.group(1)] = match.group(2)

这也适用于你:

env_vars = [] # or dict {}
with open(env_file) as f:
    for line in f:
        if line.startswith('#') or not line.strip():
            continue
        # if 'export' not in line:
        #     continue
        # Remove leading `export `, if you have those
        # then, split name / value pair
        # key, value = line.replace('export ', '', 1).strip().split('=', 1)
        key, value = line.strip().split('=', 1)
        # os.environ[key] = value  # Load to local environ
        # env_vars[key] = value # Save to a dict, initialized env_vars = {}
        env_vars.append({'name': key, 'value': value}) # Save to a list

print(env_vars)

在评论中,你会发现一些不同的方法来保存env变量,也有一些解析选项,即摆脱领先的export关键字。另一种方法是使用python-dotenv库。欢呼。

更新:我设置了自己的envvar_utils.py来处理从字符串等的转换。

"""Utility functions for dealing with env variables and reading variables from env file"""
import os
import logging
import json

BOOLEAN_TYPE = 'boolean'
INT_TYPE = 'int'
FLOAT_TYPE = 'float'
STRING_TYPE = 'str'
LIST_TYPE = 'list'
DICT_TYPE = 'dict'


def get_envvars(env_file='.env', set_environ=True, ignore_not_found_error=False, exclude_override=()):
    """
    Set env vars from a file
    :param env_file:
    :param set_environ:
    :param ignore_not_found_error: ignore not found error
    :param exclude_override: if parameter found in this list, don't overwrite environment
    :return: list of tuples, env vars
    """
    env_vars = []
    try:

        with open(env_file) as f:
            for line in f:
                line = line.replace('\n', '')

                if not line or line.startswith('#'):
                    continue

                # Remove leading `export `
                if line.lower().startswith('export '):
                    key, value = line.replace('export ', '', 1).strip().split('=', 1)
                else:
                    try:
                        key, value = line.strip().split('=', 1)
                    except ValueError:
                        logging.error(f"envar_utils.get_envvars error parsing line: '{line}'")
                        raise

                if set_environ and key not in exclude_override:
                    os.environ[key] = value

                if key in exclude_override:
                    env_vars.append({'name': key, 'value': os.getenv(key)})
                else:
                    env_vars.append({'name': key, 'value': value})
    except FileNotFoundError:
        if not ignore_not_found_error:
            raise

    return env_vars


def create_envvar_file(env_file_path, envvars):
    """
    Writes envvar file using env var dict
    :param env_file_path: str, path to file to write to
    :param envvars: dict, env vars
    :return:
    """
    with open(env_file_path, "w+") as f:
        for key, value in envvars.items():
            f.write("{}={}\n".format(key, value))
    return True


def convert_env_var_flag_to(env_var_name, required_type, default_value):
    """
    Convert env variable string flag values to required_type
    :param env_var_name: str, environment variable name
    :param required_type: str, required type to cast the env var to
    :param default_value: boolean, default value to use if the environment variable is not available
    :return: environment variable value in required type
    """
    env_var_orginal_value = os.getenv(env_var_name, default_value)
    env_var_value = ""
    try:
        if required_type == INT_TYPE:
            env_var_value = int(env_var_orginal_value)
        elif required_type == FLOAT_TYPE:
            env_var_value = float(env_var_orginal_value)
        elif required_type == BOOLEAN_TYPE:
            env_var_value = bool(int(env_var_orginal_value))
        elif required_type == STRING_TYPE:
            env_var_value = str(env_var_orginal_value)
        elif required_type == LIST_TYPE:
            env_var_value = env_var_orginal_value.split(',') if len(env_var_orginal_value) > 0 else default_value
        elif required_type == DICT_TYPE:
            try:
                env_var_value = json.loads(env_var_orginal_value) if env_var_orginal_value else default_value
            except Exception as e:
                logging.error(f"convert_env_var_flag_to: failed loading {env_var_orginal_value} error {e}")
                env_var_value = default_value
        else:
            logging.error("Unrecognized type {} for env var {}".format(required_type, env_var_name))

    except ValueError:
        env_var_value = default_value
        logging.warning("{} is {}".format(env_var_name, env_var_orginal_value))

    return env_var_value