Skip to content

Commit 726ac12

Browse files
authored
Merge pull request #144 from shakefu/master
Prepend environment variables and config values for subparser support
2 parents 8bbc7de + 1d55ace commit 726ac12

File tree

6 files changed

+85
-34
lines changed

6 files changed

+85
-34
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,5 @@ docs/_build
4545

4646
# IDEs
4747
.idea
48+
49+
.eggs/

.travis.yml

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
11
# Config file for automatic testing at travis-ci.org
22

3-
language: python
4-
5-
python:
6-
- "3.5"
7-
- "3.4"
8-
- "3.3"
9-
- "3.2"
10-
- "2.7"
11-
- "2.6"
12-
- "pypy"
3+
matrix:
4+
include:
5+
- dist: xenial # Needed for Python 3.7
6+
python: 3.7
7+
language: python
8+
- dist: xenial # Needed for Python 3.6
9+
python: 3.6
10+
language: python
11+
- python: 3.5
12+
language: python
13+
- python: 3.4
14+
language: python
15+
- python: 3.3
16+
language: python
17+
- python: 3.2
18+
language: python
19+
- python: 2.7
20+
language: python
21+
- python: 2.6
22+
language: python
23+
- python: pypy
24+
language: python
25+
- python: pypy3
26+
language: python
1327

1428
# command to install dependencies:
1529
install: pip install mock
@@ -18,4 +32,4 @@ install: pip install mock
1832
script: python setup.py test
1933

2034
# migrate to container-based travis.ci: http://docs.travis-ci.com/user/migrating-from-legacy
21-
sudo: false
35+
sudo: false

configargparse.py

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -265,22 +265,7 @@ class ArgumentParser(argparse.ArgumentParser):
265265
environment variables and .ini or .yaml-style config files.
266266
"""
267267

268-
def __init__(self,
269-
add_config_file_help=True,
270-
add_env_var_help=True,
271-
auto_env_var_prefix=None,
272-
default_config_files=[],
273-
ignore_unknown_config_file_keys=False,
274-
config_file_parser_class=DefaultConfigFileParser,
275-
args_for_setting_config_path=[],
276-
config_arg_is_required=False,
277-
config_arg_help_message="config file path",
278-
args_for_writing_out_config_file=[],
279-
write_out_config_file_arg_help_message="takes the current command line "
280-
"args and writes them out to a config file at the given path, then "
281-
"exits",
282-
**kwargs
283-
):
268+
def __init__(self, *args, **kwargs):
284269

285270
"""Supports args of the argparse.ArgumentParser constructor
286271
as **kwargs, as well as the following additional args.
@@ -331,11 +316,34 @@ def __init__(self,
331316
write_out_config_file_arg_help_message: The help message to use for
332317
the args in args_for_writing_out_config_file.
333318
"""
319+
# This is the only way to make positional args (tested in the argparse
320+
# main test suite) and keyword arguments work across both Python 2 and
321+
# 3. This could be refactored to not need extra local variables.
322+
add_config_file_help = kwargs.pop('add_config_file_help', True)
323+
add_env_var_help = kwargs.pop('add_env_var_help', True)
324+
auto_env_var_prefix = kwargs.pop('auto_env_var_prefix', None)
325+
default_config_files = kwargs.pop('default_config_files', [])
326+
ignore_unknown_config_file_keys = kwargs.pop(
327+
'ignore_unknown_config_file_keys', False)
328+
config_file_parser_class = kwargs.pop('config_file_parser_class',
329+
DefaultConfigFileParser)
330+
args_for_setting_config_path = kwargs.pop(
331+
'args_for_setting_config_path', [])
332+
config_arg_is_required = kwargs.pop('config_arg_is_required', False)
333+
config_arg_help_message = kwargs.pop('config_arg_help_message',
334+
"config file path")
335+
args_for_writing_out_config_file = kwargs.pop(
336+
'args_for_writing_out_config_file', [])
337+
write_out_config_file_arg_help_message = kwargs.pop(
338+
'write_out_config_file_arg_help_message', "takes the current "
339+
"command line args and writes them out to a config file at the "
340+
"given path, then exits")
341+
334342
self._add_config_file_help = add_config_file_help
335343
self._add_env_var_help = add_env_var_help
336344
self._auto_env_var_prefix = auto_env_var_prefix
337345

338-
argparse.ArgumentParser.__init__(self, **kwargs)
346+
argparse.ArgumentParser.__init__(self, *args, **kwargs)
339347

340348
# parse the additional args
341349
if config_file_parser_class is None:
@@ -431,6 +439,7 @@ def parse_known_args(self, args = None, namespace = None,
431439

432440
# add env var settings to the commandline that aren't there already
433441
env_var_args = []
442+
nargs = False
434443
actions_with_env_var_values = [a for a in self._actions
435444
if not a.is_positional_arg and a.env_var and a.env_var in env_vars
436445
and not already_on_command_line(args, a.option_strings)]
@@ -439,20 +448,23 @@ def parse_known_args(self, args = None, namespace = None,
439448
value = env_vars[key]
440449
# Make list-string into list.
441450
if action.nargs or isinstance(action, argparse._AppendAction):
451+
nargs = True
442452
element_capture = re.match('\[(.*)\]', value)
443453
if element_capture:
444454
value = [val.strip() for val in element_capture.group(1).split(',') if val.strip()]
445455
env_var_args += self.convert_item_to_command_line_arg(
446456
action, key, value)
447457

448-
args = args + env_var_args
458+
if nargs:
459+
args = args + env_var_args
460+
else:
461+
args = env_var_args + args
449462

450463
if env_var_args:
451464
self._source_to_settings[_ENV_VAR_SOURCE_KEY] = OrderedDict(
452465
[(a.env_var, (a, env_vars[a.env_var]))
453466
for a in actions_with_env_var_values])
454467

455-
456468
# before parsing any config files, check if -h was specified.
457469
supports_help_arg = any(
458470
a for a in self._actions if isinstance(a, argparse._HelpAction))
@@ -484,6 +496,7 @@ def parse_known_args(self, args = None, namespace = None,
484496

485497
# add each config item to the commandline unless it's there already
486498
config_args = []
499+
nargs = False
487500
for key, value in config_items.items():
488501
if key in known_config_keys:
489502
action = known_config_keys[key]
@@ -503,9 +516,14 @@ def parse_known_args(self, args = None, namespace = None,
503516
if source_key not in self._source_to_settings:
504517
self._source_to_settings[source_key] = OrderedDict()
505518
self._source_to_settings[source_key][key] = (action, value)
519+
if (action and action.nargs or
520+
isinstance(action, argparse._AppendAction)):
521+
nargs = True
506522

507-
args = args + config_args
508-
523+
if nargs:
524+
args = args + config_args
525+
else:
526+
args = config_args + args
509527

510528
# save default settings for use by print_values()
511529
default_settings = OrderedDict()

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def launch_http_server(directory):
8484

8585
setup(
8686
name='ConfigArgParse',
87-
version="0.14.0",
87+
version="0.14.1",
8888
description='A drop-in replacement for argparse that allows options to '
8989
'also be set via config files and/or environment variables.',
9090
long_description=long_description,

tests/test_configargparse.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,14 @@ def test_FormatHelp(self):
755755
'--flag \s*Flag help text'
756756
)
757757

758+
def test_FormatHelpProg(self):
759+
self.initParser('format_help_prog')
760+
self.assertRegex(self.format_help(), 'usage: format_help_prog .*')
761+
762+
def test_FormatHelpProgLib(self):
763+
parser = argparse.ArgumentParser('format_help_prog')
764+
self.assertRegex(parser.format_help(), 'usage: format_help_prog .*')
765+
758766
class CustomClass(object):
759767
def __init__(self, name):
760768
self.name = name

tox.ini

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[tox]
2-
envlist = py26, py27, py32, py33, py34, py35
2+
envlist = py26, py27, py32, py33, py34, py35, py36, py37, pypy, pypy3
33
# py34 # bug with virtualenv so tox doesn't work?
44

55

@@ -27,5 +27,14 @@ basepython=python3.4
2727
[testenv:py35]
2828
basepython=python3.5
2929

30+
[testenv:py36]
31+
basepython=python3.6
32+
33+
[testenv:py37]
34+
basepython=python3.7
35+
3036
[testenv:pypy]
3137
basepython=pypy
38+
39+
[testenv:pypy3]
40+
basepython=pypy3

0 commit comments

Comments
 (0)