2012-05-11 21 views
17

我有一个脚本,它有某些选项可以在命令行或环境变量上传递。如果两者都存在,则应优先使用CLI,如果两者均未设置,则会发生错误。使用argparse时设置环境变量的选项

我可以在解析后检查该选项是否已分配,但我更愿意让argparse完成繁重的工作,并在解析失败时负责显示使用情况声明。

我已经想出了一些替代方法来解决这个问题(我将在后面作为答案进行发布,以便他们可以分开讨论),但他们对我来说感觉相当糟糕,我认为我错过了一些东西。

是否有一个公认的“最好”的方式来做到这一点?

(编辑以使所期望的行为明确当两个CLI选项和环境变量是未设置)

回答

26

我用这种模式足够频繁,我已经打包一个简单的动作类来处理它:

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() 
+1

默认情况下,这将被编辑查找os.environ? “如果在os.environ中的envvar:default = envvar” - >“如果在os.environ中的envvar:default = os.environ [envvar]” – spazm

+1

如果没有默认值,为什么只使用envvar指定的值?它不应该重写默认值,因为调用者明确提供了一个值吗? –

+0

这个解决方案非常棒。但是,它有一个副作用:取决于您的环境(是否设置了env var),使用情况文本可能有所不同。 –

2

一种选择是检查环境变量是否被设置,并修改调用相应add_argument 例如

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() 
31

我:

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) 

我可以从我的代码,然后把这个将变量添加到要获取的变量的os.environ中时,将只设置default变量。如果.get()未找到该名称的环境变量,则.get()调用中的第二个参数是默认值。

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()) 
+0

无是.get()的默认值,所以不需要像这样明确地声明。我应该在问题中更加清楚 - 该选项至少需要位于环境变量或CLI中的一个环境变量中。如果你像这样设置默认值,那么args.url最终可能会是None,这是我想要避免的... –

+1

啊,我明白你在找什么。我老实说,只是使用我写的东西,解析完参数后,只需检查'如果不是args.url:exit(parser.print_usage())'并退出。 –

+1

处理它的好方法。我已经打包了自己的动作处理程序,因为我使用了这种模式很多,但这肯定会成为我快速而简单的脚本的后备。 –

0

以为我会发布我的解决方案,原来的问题/答案给了我很多帮助。

我的问题与罗素的有点不同。我使用OptionParser,而不是每个参数的环境变量,我只有一个模拟命令行。

MY_ENVIRONMENT_ARGS = --arg1 “马耳他” --arg2 “猎鹰” -r “1930” -h

解决方案:

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:]) 

这里,我并不抛出错误,如果没有找到参数。但是,如果我想我可以做这样的事情

for key in options.__dict__: 
    if options.__dict__[key] is None: 
     # raise error/log problem/print to console/etc 
2

的话题是很老,但我有类似的问题,我想我会与大家分享我的解决方案。不幸的是通过@Russell Heilling建议的自定义操作解决方案并没有为我工作了几个原因:

  • 这使我不能用predefined actions(如store_true
  • 我宁愿喜欢它退却到defaultenvvaros.environ(也可以很容易地固定)
  • 我想有这种行为对我所有的参数,而无需指定actionenvvar(应该始终是action.dest.upper()

这里是我的解决方案(在Python 3):

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) 
10

ConfigArgParse增加了对环境变量argparse的支持,所以你可以做这样的事情:

p = configargparse.ArgParser() 
p.add('-m', '--moo', help='Path of cow', env_var='MOO_PATH') 
options = p.parse_args() 
6

我通常要做到这一点的多个参数(认证和API密钥)..这很简单直接。使用** kwargs。

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'))