Setting options from environment variables when using argparse
我有一个脚本,它有一些选项,可以在命令行上传递,也可以从环境变量传递。如果两者都存在,则应优先使用CLI;如果两者都不设置,则会出现错误。
我可以检查在解析之后是否分配了该选项,但是我更愿意让argparse进行繁重的工作,并在解析失败时负责显示用法语句。
我已经想出了一些替代方法(我将在下面发布作为答案,以便单独讨论),但它们对我来说相当笨拙,我认为我遗漏了一些东西。
是否有一种公认的"最佳"方法来做到这一点?
(编辑以在未设置cli选项和环境变量时清除所需的行为)
我只需要在向get of os.environ添加参数时设置
1 2 3 4 5 6 7 8 9 | import argparse import os parser = argparse.ArgumentParser(description='test') parser.add_argument('--url', default=os.environ.get('URL', None)) args = parser.parse_args() if not args.url: exit(parser.print_usage()) |
我经常使用这个模式,因此我打包了一个简单的动作类来处理它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import argparse import os class EnvDefault(argparse.Action): def __init__(self, envvar, required=True, default=None, **kwargs): if not default and envvar: if envvar in os.environ: default = os.environ[envvar] if required and default: required = False super(EnvDefault, self).__init__(default=default, required=required, **kwargs) def __call__(self, parser, namespace, values, option_string=None): setattr(namespace, self.dest, values) |
然后,我可以使用以下代码调用此代码:
1 2 3 4 5 6 7 8 | import argparse from envdefault import EnvDefault parser=argparse.ArgumentParser() parser.add_argument( "-u","--url", action=EnvDefault, envvar='URL', help="Specify the URL to process (can also be specified using URL environment variable)") args=parser.parse_args() |
configargparse向argparse添加了对环境变量的支持,因此可以执行以下操作:
1 2 3 | p = configargparse.ArgParser() p.add('-m', '--moo', help='Path of cow', env_var='MOO_PATH') options = p.parse_args() |
我通常需要对多个参数(身份验证和API密钥)执行此操作。这是简单而直接的。使用*kWAGS。
1 2 3 4 5 6 7 | def environ_or_required(key): if os.environ.get(key): return {'default': os.environ.get(key)} else: return {'required': True} parser.add_argument('--thing', **environ_or_required('THING')) |
这个话题很古老,但我也有类似的问题,我想我会和你分享我的解决方案。不幸的是,@russell heilling建议的自定义操作解决方案不适用于我,原因如下:
- 它阻止我使用预先定义的操作(如
store_true ) - 我希望在
envvar 不在os.environ 的情况下(这可以很容易地解决),它能退回到default 。 - 我希望这种行为适用于我所有的论点,而不指定
action 或envvar (应该始终是action.dest.upper() )。
这是我的解决方案(在Python3中):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class CustomArgumentParser(argparse.ArgumentParser): class _CustomHelpFormatter(argparse.ArgumentDefaultsHelpFormatter): def _get_help_string(self, action): help = super()._get_help_string(action) if action.dest != 'help': help += ' [env: {}]'.format(action.dest.upper()) return help def __init__(self, *, formatter_class=_CustomHelpFormatter, **kwargs): super().__init__(formatter_class=formatter_class, **kwargs) def _add_action(self, action): action.default = os.environ.get(action.dest.upper(), action.default) return super()._add_action(action) |
一个选项是检查是否设置了环境变量,并修改调用以相应地添加参数例如
1 2 3 4 5 6 7 8 9 10 11 12 | import argparse import os parser=argparse.ArgumentParser() if 'CVSWEB_URL' in os.environ: cvsopt = { 'default': os.environ['CVSWEB_URL'] } else: cvsopt = { 'required': True } parser.add_argument( "-u","--cvsurl", help="Specify url (overrides CVSWEB_URL environment variable)", **cvsopt) args=parser.parse_args() |
在
1 2 3 4 5 6 7 8 9 10 11 | import os, argparse defaults = {'color': 'red', 'user': 'guest'} parser = argparse.ArgumentParser() parser.add_argument('-u', '--user') parser.add_argument('-c', '--color') namespace = parser.parse_args() command_line_args = {k:v for k, v in vars(namespace).items() if v} combined = ChainMap(command_line_args, os.environ, defaults) |
我是从一个关于美丽的、惯用的Python的伟大演讲中得到的。
但是,我不知道如何处理小写和大写字典键的区别。如果两个
我想我会把我的答案贴出来,因为最初的问题/答案给了我很多帮助。
我的问题与Russell的有点不同。我使用的是OptionParser,而不是每个参数的环境变量,我只有一个参数来模拟命令行。
即
我的环境——args=--arg1"马耳他"-arg2"猎鹰"-r"1930"-h
解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | def set_defaults_from_environment(oparser): if 'MY_ENVIRONMENT_ARGS' in os.environ: environmental_args = os.environ[ 'MY_ENVIRONMENT_ARGS' ].split() opts, _ = oparser.parse_args( environmental_args ) oparser.defaults = opts.__dict__ oparser = optparse.OptionParser() oparser.add_option('-a', '--arg1', action='store', default="Consider") oparser.add_option('-b', '--arg2', action='store', default="Phlebas") oparser.add_option('-r', '--release', action='store', default='1987') oparser.add_option('-h', '--hardback', action='store_true', default=False) set_defaults_from_environment(oparser) options, _ = oparser.parse_args(sys.argv[1:]) |
在这里,如果找不到参数,我不会抛出错误。但如果我愿意,我可以做一些像
1 2 3 | for key in options.__dict__: if options.__dict__[key] is None: # raise error/log problem/print to console/etc |