Python의 표준 라이브러리에는 구성 파일 구문 분석 ( configparser ), 환경 변수 읽기 ( os.environ ) 및 명령 줄 인수 구문 분석 ( argparse )을 위한 모듈이 있습니다 . 이 모든 작업을 수행하는 프로그램을 작성하고 싶습니다.
-
있습니다 옵션 값의 폭포 :
- 기본 옵션 값, 재정의
- 구성 파일 옵션, 재정의
- 에 의해 재정의되는 환경 변수
- 명령 줄 옵션.
-
예를 들어 명령 줄에 지정된 하나 이상의 구성 파일 위치를 허용
--config-file foo.conf
하고이를 읽습니다 (일반 구성 파일 대신 또는 추가로). 이것은 여전히 위의 캐스케이드를 따라야합니다. -
수 있도록 한 곳에서 옵션 정의를 구성 파일 및 명령 줄의 구문 분석 동작을 결정합니다.
-
구문 분석 된 옵션을 단일 옵션 값 모음 으로 통합하여 프로그램의 출처를 신경 쓰지 않고 액세스 할 수 있도록합니다.
내가 필요한 모든 것은 분명히 Python 표준 라이브러리에 있지만 원활하게 작동하지 않습니다.
파이썬 표준 라이브러리에서 최소한의 편차로 어떻게 이것을 달성 할 수 있습니까?
답변
argparse 모듈은 명령 줄처럼 보이는 구성 파일에 만족하는 한 이것을 괴롭히지 않습니다. (사용자가 하나의 구문 만 배워야하기 때문에 이것이 장점이라고 생각합니다.) fromfile_prefix_chars 를 다음과 같이 설정합니다.@
하면 다음과 같이됩니다.
my_prog --foo=bar
다음과 같다
my_prog @baz.conf
경우 @baz.conf
이며,
--foo
bar
foo.conf
수정 하여 코드가 자동으로 찾도록 할 수도 있습니다.argv
if os.path.exists('foo.conf'):
argv = ['@foo.conf'] + argv
args = argparser.parse_args(argv)
이러한 구성 파일의 형식은 ArgumentParser의 하위 클래스를 만들고 convert_arg_line_to_args 메서드를 추가하여 수정할 수 있습니다.
답변
업데이트 : 나는 마침내 이것을 pypi에 넣었습니다. 다음을 통해 최신 버전을 설치하십시오.
pip install configargparser
원본 게시물
제가 함께 해킹 한 것이 있습니다. 의견에 개선 / 버그 보고서를 자유롭게 제안하십시오.
import argparse
import ConfigParser
import os
def _identity(x):
return x
_SENTINEL = object()
class AddConfigFile(argparse.Action):
def __call__(self,parser,namespace,values,option_string=None):
# I can never remember if `values` is a list all the time or if it
# can be a scalar string; this takes care of both.
if isinstance(values,basestring):
parser.config_files.append(values)
else:
parser.config_files.extend(values)
class ArgumentConfigEnvParser(argparse.ArgumentParser):
def __init__(self,*args,**kwargs):
"""
Added 2 new keyword arguments to the ArgumentParser constructor:
config --> List of filenames to parse for config goodness
default_section --> name of the default section in the config file
"""
self.config_files = kwargs.pop('config',[]) #Must be a list
self.default_section = kwargs.pop('default_section','MAIN')
self._action_defaults = {}
argparse.ArgumentParser.__init__(self,*args,**kwargs)
def add_argument(self,*args,**kwargs):
"""
Works like `ArgumentParser.add_argument`, except that we've added an action:
config: add a config file to the parser
This also adds the ability to specify which section of the config file to pull the
data from, via the `section` keyword. This relies on the (undocumented) fact that
`ArgumentParser.add_argument` actually returns the `Action` object that it creates.
We need this to reliably get `dest` (although we could probably write a simple
function to do this for us).
"""
if 'action' in kwargs and kwargs['action'] == 'config':
kwargs['action'] = AddConfigFile
kwargs['default'] = argparse.SUPPRESS
# argparse won't know what to do with the section, so
# we'll pop it out and add it back in later.
#
# We also have to prevent argparse from doing any type conversion,
# which is done explicitly in parse_known_args.
#
# This way, we can reliably check whether argparse has replaced the default.
#
section = kwargs.pop('section', self.default_section)
type = kwargs.pop('type', _identity)
default = kwargs.pop('default', _SENTINEL)
if default is not argparse.SUPPRESS:
kwargs.update(default=_SENTINEL)
else:
kwargs.update(default=argparse.SUPPRESS)
action = argparse.ArgumentParser.add_argument(self,*args,**kwargs)
kwargs.update(section=section, type=type, default=default)
self._action_defaults[action.dest] = (args,kwargs)
return action
def parse_known_args(self,args=None, namespace=None):
# `parse_args` calls `parse_known_args`, so we should be okay with this...
ns, argv = argparse.ArgumentParser.parse_known_args(self, args=args, namespace=namespace)
config_parser = ConfigParser.SafeConfigParser()
config_files = [os.path.expanduser(os.path.expandvars(x)) for x in self.config_files]
config_parser.read(config_files)
for dest,(args,init_dict) in self._action_defaults.items():
type_converter = init_dict['type']
default = init_dict['default']
obj = default
if getattr(ns,dest,_SENTINEL) is not _SENTINEL: # found on command line
obj = getattr(ns,dest)
else: # not found on commandline
try: # get from config file
obj = config_parser.get(init_dict['section'],dest)
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): # Nope, not in config file
try: # get from environment
obj = os.environ[dest.upper()]
except KeyError:
pass
if obj is _SENTINEL:
setattr(ns,dest,None)
elif obj is argparse.SUPPRESS:
pass
else:
setattr(ns,dest,type_converter(obj))
return ns, argv
if __name__ == '__main__':
fake_config = """
[MAIN]
foo:bar
bar:1
"""
with open('_config.file','w') as fout:
fout.write(fake_config)
parser = ArgumentConfigEnvParser()
parser.add_argument('--config-file', action='config', help="location of config file")
parser.add_argument('--foo', type=str, action='store', default="grape", help="don't know what foo does ...")
parser.add_argument('--bar', type=int, default=7, action='store', help="This is an integer (I hope)")
parser.add_argument('--baz', type=float, action='store', help="This is an float(I hope)")
parser.add_argument('--qux', type=int, default='6', action='store', help="this is another int")
ns = parser.parse_args([])
parser_defaults = {'foo':"grape",'bar':7,'baz':None,'qux':6}
config_defaults = {'foo':'bar','bar':1}
env_defaults = {"baz":3.14159}
# This should be the defaults we gave the parser
print ns
assert ns.__dict__ == parser_defaults
# This should be the defaults we gave the parser + config defaults
d = parser_defaults.copy()
d.update(config_defaults)
ns = parser.parse_args(['--config-file','_config.file'])
print ns
assert ns.__dict__ == d
os.environ['BAZ'] = "3.14159"
# This should be the parser defaults + config defaults + env_defaults
d = parser_defaults.copy()
d.update(config_defaults)
d.update(env_defaults)
ns = parser.parse_args(['--config-file','_config.file'])
print ns
assert ns.__dict__ == d
# This should be the parser defaults + config defaults + env_defaults + commandline
commandline = {'foo':'3','qux':4}
d = parser_defaults.copy()
d.update(config_defaults)
d.update(env_defaults)
d.update(commandline)
ns = parser.parse_args(['--config-file','_config.file','--foo=3','--qux=4'])
print ns
assert ns.__dict__ == d
os.remove('_config.file')
할 것
이 구현은 아직 불완전합니다. 다음은 부분적인 TODO 목록입니다.
문서화 된 행동 준수
- (쉬운) 함수를 쓰기 것을 그림
dest
에서args
의add_argument
대신에 의존의Action
객체 - (사소한) 기록
parse_args
된 함수를 사용parse_known_args
. (예 : 구현parse_args
에서 복사cpython
하여를 호출합니다parse_known_args
.)
덜 쉬운 것…
아직 시도한 적이 없습니다. 가능성은 낮지 만 여전히 가능합니다!
답변
정확히 configglue 라는 라이브러리가 있습니다 .
configglue는 python의 optparse.OptionParser 및 ConfigParser.ConfigParser를 결합하는 라이브러리이므로 동일한 옵션을 구성 파일 및 명령 줄 인터페이스로 내보낼 때 반복 할 필요가 없습니다.
또한 환경 변수를 지원 합니다.
라는 다른 라이브러리도 있습니다 ConfigArgParse 입니다
구성 파일 및 / 또는 환경 변수를 통해 옵션을 설정할 수도있는 argparse의 드롭 인 대체입니다.
Łukasz Langa의 구성에 대한 PyCon 이야기에 관심이있을 수 있습니다.- Let Them Configure!
답변
내가 직접 시도하지는 않았지만 원하는 대부분의 작업을 수행한다는 ConfigArgParse 라이브러리가 있습니다.
구성 파일 및 / 또는 환경 변수를 통해 옵션을 설정할 수도있는 argparse의 드롭 인 대체입니다.
답변
자갈 각 프로그래머 떠나, 표준 라이브러리는이 문제를 해결하지 않는 것 같습니다 configparser
과 argparse
와 os.environ
어설픈 방법으로 모두 함께.
답변
내가 아는 한 파이썬 표준 라이브러리는 이것을 제공하지 않습니다. 명령 줄 및 구성 파일 을 사용 optparse
하고 ConfigParser
구문 분석하고 그 위에 추상화 계층을 제공하는 코드를 작성하여이 문제를 해결 했습니다. 그러나 이전 의견에서 불쾌한 것처럼 보이는 별도의 종속성으로 이것을 필요로합니다.
내가 작성한 코드를보고 싶다면 http://liw.fi/cliapp/에 있습니다. 프레임 워크가 수행해야하는 작업의 대부분을 차지하기 때문에 내 “명령 줄 응용 프로그램 프레임 워크”라이브러리에 통합되었습니다.
답변
최근에 “optparse”를 사용하여 이와 같은 시도를했습니다.
나는 ‘–Store’및 ‘–Check’명령을 사용하여 OptonParser의 하위 클래스로 설정했습니다.
아래 코드는 거의 다룰 것입니다. 사전을 수락 / 반환하는 자체 ‘로드’및 ‘저장’방법을 정의하기 만하면됩니다.
class SmartParse(optparse.OptionParser):
def __init__(self,defaults,*args,**kwargs):
self.smartDefaults=defaults
optparse.OptionParser.__init__(self,*args,**kwargs)
fileGroup = optparse.OptionGroup(self,'handle stored defaults')
fileGroup.add_option(
'-S','--Store',
dest='Action',
action='store_const',const='Store',
help='store command line settings'
)
fileGroup.add_option(
'-C','--Check',
dest='Action',
action='store_const',const='Check',
help ='check stored settings'
)
self.add_option_group(fileGroup)
def parse_args(self,*args,**kwargs):
(options,arguments) = optparse.OptionParser.parse_args(self,*args,**kwargs)
action = options.__dict__.pop('Action')
if action == 'Check':
assert all(
value is None
for (key,value) in options.__dict__.iteritems()
)
print 'defaults:',self.smartDefaults
print 'config:',self.load()
sys.exit()
elif action == 'Store':
self.store(options.__dict__)
sys.exit()
else:
config=self.load()
commandline=dict(
[key,val]
for (key,val) in options.__dict__.iteritems()
if val is not None
)
result = {}
result.update(self.defaults)
result.update(config)
result.update(commandline)
return result,arguments
def load(self):
return {}
def store(self,optionDict):
print 'Storing:',optionDict