Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cbb3e36127 | |||
| a19fb22f54 | |||
| 1f9e42f222 | |||
| e3fa33cafb | |||
| 3e77f0b2b4 | |||
| 5a591454e9 | |||
| cc47b0b746 | |||
|
|
2c4c16360c | ||
|
|
a077a6b569 | ||
| 6488a3d40a | |||
| de1fcf5164 | |||
| 94d774ed71 | |||
| 563b895e42 | |||
| 421fc7107b |
1
.gitignore
vendored
1
.gitignore
vendored
@ -138,3 +138,4 @@ dmypy.json
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
.vscode
|
||||
|
||||
17
README.md
17
README.md
@ -29,7 +29,7 @@ args, kwargs = parse_args(argv=['pargv.py', '--name=pargv'])
|
||||
|
||||
# Specification
|
||||
|
||||
`parse_args` parses arguments in the following way, assuming the following command line arguments (`sys.argv`): `['/pargv/pargv.py', 'command', 'positional', '--flag', '--optional=value', 'test', '--output-file', 'filename', '-flg', 'name', 'name2']`
|
||||
`parse_args` parses arguments in the following way, assuming the following command line arguments (`sys.argv`): `['/pargv/pargv.py', 'command', 'positional', '--flag', '--optional=value', 'test', '--output-file', 'filename', '-ab=one', 'two']`
|
||||
|
||||
By calling `args, kwargs = parse_args()`, this would return the following list and dict:
|
||||
|
||||
@ -38,10 +38,9 @@ args = ['/pargv/pargv.py', 'command', 'positional']
|
||||
kwargs = {
|
||||
'flag': True,
|
||||
'optional': ['value', 'test'],
|
||||
'output_file': ['filename'],
|
||||
'f': True,
|
||||
'l': True,
|
||||
'g': ['name', 'name2'],
|
||||
'output_file': 'filename',
|
||||
'a': True,
|
||||
'b': ['one', 'two'],
|
||||
}
|
||||
```
|
||||
|
||||
@ -49,6 +48,8 @@ kwargs = {
|
||||
|
||||
- All arguments before the first option (starting with a hyphen) are considered positional arguments (`args`)
|
||||
- All other arguments are considered optional keyword arguments (`kwargs`)
|
||||
- Optional arguments without leading hyphens are considered as values to preceding keyword arguments and saved as list
|
||||
- Flags are recorded with the value `True` in the dict
|
||||
- Up to 2 leading hyphens are stripped from options, all other hyphens are converted into underscores (`---test-this-` would become `_test_this_`)
|
||||
- Optional arguments without leading hyphens are considered as values to preceding keyword arguments
|
||||
- One value of an option is stored as a string, multiple values are stored as a list
|
||||
- Flags are stored with the value `True` in the dict
|
||||
- Up to 2 leading hyphens are stripped from options, all other hyphens are converted into underscores (`---test-this-` would become the key `_test_this_` in the dict)
|
||||
- For multiple short options such as `-vao=file.ext` or `-vao file.ext`, the last option (`o`) is set to the specified value, while other options (`v` and `a`) are set to `True`.
|
||||
|
||||
45
pargv.py
45
pargv.py
@ -1,6 +1,9 @@
|
||||
import sys
|
||||
|
||||
|
||||
__version__ = '0.2.1'
|
||||
|
||||
|
||||
def parse_args(argv=None):
|
||||
"""
|
||||
Returns a tuple of (args, kwargs) from a given list of command line arguments.
|
||||
@ -12,30 +15,32 @@ def parse_args(argv=None):
|
||||
for arg in argv:
|
||||
if arg.startswith('-'):
|
||||
break
|
||||
args.append(arg)
|
||||
argv = argv[len(args):]
|
||||
args.append(arg.replace('-', '_'))
|
||||
|
||||
kwargs = {}
|
||||
values = []
|
||||
for arg in argv:
|
||||
value = None
|
||||
for arg in argv[len(args):]:
|
||||
|
||||
if not arg.startswith('-'):
|
||||
values.append(arg)
|
||||
else:
|
||||
if len(values):
|
||||
kwargs[key] = values
|
||||
values = []
|
||||
if isinstance(kwargs[key], bool):
|
||||
kwargs[key] = arg
|
||||
elif isinstance(kwargs[key], str):
|
||||
kwargs[key] = [kwargs[key]] + [arg]
|
||||
elif isinstance(kwargs[key], list):
|
||||
kwargs[key].append(arg)
|
||||
continue
|
||||
|
||||
key = arg[:2].lstrip('-') + arg[2:].replace('-', '_')
|
||||
if arg.startswith('--'):
|
||||
if '=' in arg:
|
||||
|
||||
if '=' in key:
|
||||
key, value = key.split('=')
|
||||
values.append(value)
|
||||
kwargs[key] = values
|
||||
else:
|
||||
kwargs[key] = True
|
||||
elif arg.startswith('-'):
|
||||
for k in key:
|
||||
|
||||
if not arg.startswith('--'):
|
||||
for k in key[:-1]:
|
||||
kwargs[k] = True
|
||||
key = arg[-1]
|
||||
if len(values):
|
||||
kwargs[key] = values
|
||||
key = key[-1]
|
||||
|
||||
kwargs[key] = value if value else True
|
||||
value = None
|
||||
|
||||
return (args, kwargs)
|
||||
|
||||
2
setup.py
2
setup.py
@ -6,7 +6,7 @@ with open('README.md') as f:
|
||||
|
||||
setup(
|
||||
name='pargv',
|
||||
version='0.1.1',
|
||||
version='0.2.1',
|
||||
description='Parse command line arguments into a list of args and a dict of kwargs.',
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
|
||||
@ -6,87 +6,58 @@ from pargv import parse_args
|
||||
|
||||
def test_no_argv():
|
||||
with mock.patch('sys.argv', []):
|
||||
args, kwargs = parse_args()
|
||||
assert args == []
|
||||
assert kwargs == {}
|
||||
|
||||
assert parse_args() == ([], {})
|
||||
|
||||
def test_use_sys_argv_by_default():
|
||||
with mock.patch('sys.argv', ['app', 'command', '--option=one', 'two', '--flag', '-io']):
|
||||
args, kwargs = parse_args()
|
||||
assert args == ['app', 'command']
|
||||
assert kwargs == {
|
||||
'option': ['one', 'two'],
|
||||
'flag': True,
|
||||
'i': True,
|
||||
'o': True
|
||||
}
|
||||
|
||||
with mock.patch('sys.argv', ['argument', '--flag']):
|
||||
assert parse_args() == (['argument'], {'flag': True})
|
||||
|
||||
def test_single_positional_argument():
|
||||
args, kwargs = parse_args(['app'])
|
||||
assert args == ['app']
|
||||
assert kwargs == {}
|
||||
assert parse_args(['argument']) == (['argument'], {})
|
||||
|
||||
def test_single_positional_argument_with_underscore():
|
||||
assert parse_args(['the-argument']) == (['the_argument'], {})
|
||||
assert parse_args(['a-b']) == (['a_b'], {})
|
||||
|
||||
def test_optional_argument_with_underscore():
|
||||
assert parse_args(['--a-b']) == ([], {'a_b': True})
|
||||
|
||||
def test_positional_arguments():
|
||||
args, kwargs = parse_args(['app', 'command'])
|
||||
assert args == ['app', 'command']
|
||||
assert kwargs == {}
|
||||
|
||||
assert parse_args(['argument', 'command']) == (['argument', 'command'], {})
|
||||
|
||||
def test_one_positional_and_optional_argument():
|
||||
args, kwargs = parse_args(['app', '--help'])
|
||||
assert args == ['app']
|
||||
assert kwargs == {'help': True}
|
||||
assert parse_args(['argument', '--help']) == (['argument'], {'help': True})
|
||||
|
||||
def test_one_positional_and_optional_argument_with_values():
|
||||
assert parse_args(['argument', '--amount=2', '3']) == (['argument'], {'amount': ['2', '3']})
|
||||
|
||||
def test_positional_and_optional_arguments():
|
||||
args, kwargs = parse_args(['app', 'command', '--inputfile', '--outputfile'])
|
||||
assert args == ['app', 'command']
|
||||
assert kwargs == {
|
||||
'inputfile': True,
|
||||
'outputfile': True
|
||||
}
|
||||
|
||||
def test_short_arg_with_single_option():
|
||||
assert parse_args(['-a', 'b', '--abc', 'd']) == ([], {'a': 'b', 'abc': 'd'})
|
||||
|
||||
def test_short_arg_with_multiple_options():
|
||||
args, kwargs = parse_args(['-a', '-i', 'a', 'b'])
|
||||
assert args == []
|
||||
assert kwargs == {
|
||||
'a': True,
|
||||
'i': ['a', 'b']
|
||||
}
|
||||
assert parse_args(['-i', 'a', 'b']) == ([], {'i': ['a', 'b']})
|
||||
|
||||
def test_short_args_with_equals():
|
||||
assert parse_args(['-ab=c']) == ([], {'a': True, 'b': 'c'})
|
||||
assert parse_args(['-ab=c', 'd']) == ([], {'a': True, 'b': ['c', 'd']})
|
||||
|
||||
def test_long_args_with_equals():
|
||||
args, kwargs = parse_args(['--input-file=a.py', '--output-file=b.py'])
|
||||
assert args == []
|
||||
assert kwargs == {
|
||||
'input_file': ['a.py'],
|
||||
'output_file': ['b.py']
|
||||
}
|
||||
|
||||
assert parse_args(['--input-file=a.py']) == ([], {'input_file': 'a.py'})
|
||||
|
||||
def test_unintended_hyphen():
|
||||
args, kwargs = parse_args(['---triple-hyphen-'])
|
||||
assert args == []
|
||||
assert kwargs == {'_triple_hyphen_': True}
|
||||
|
||||
assert parse_args(['---triple-hyphen-']) == ([], {'_triple_hyphen_': True})
|
||||
|
||||
@pytest.mark.parametrize('argv, args, kwargs', ((
|
||||
['/pargv/pargv.py', 'command', 'positional', '--flag', '--optional=value', 'test', '--output-file', 'filename', '-flg', 'name', 'name2'],
|
||||
['/pargv/pargv.py', 'command', 'positional', '--flag', '--optional=value', 'test', '--output-file', 'filename', '-flg=name', 'name2'],
|
||||
['/pargv/pargv.py', 'command', 'positional'],
|
||||
{
|
||||
'flag': True,
|
||||
'optional': ['value', 'test'],
|
||||
'output_file': ['filename'],
|
||||
'output_file': 'filename',
|
||||
'f': True,
|
||||
'l': True,
|
||||
'g': ['name', 'name2'],
|
||||
}
|
||||
),))
|
||||
def test_all_parameters_at_once(argv, args, kwargs):
|
||||
_args, _kwargs = parse_args(argv)
|
||||
assert _args == args
|
||||
assert _kwargs == kwargs
|
||||
assert parse_args(argv) == (args, kwargs)
|
||||
|
||||
60
tests/test_parse_args.py
Normal file
60
tests/test_parse_args.py
Normal file
@ -0,0 +1,60 @@
|
||||
from pargv import parse_args
|
||||
import pytest
|
||||
|
||||
|
||||
def test_io():
|
||||
for (argv, args, kwargs) in (
|
||||
# Arguments
|
||||
('name', ['name'], {}),
|
||||
('name command', ['name', 'command'], {}),
|
||||
# Hyphens
|
||||
('a-b', ['a_b'], {}),
|
||||
('---a', [], {'_a': True}),
|
||||
('-a-', [], {'a': True, '_': True}),
|
||||
# Options
|
||||
('--option', [], {'option': True}),
|
||||
('--option=2', [], {'option': '2'}),
|
||||
('--option=2 4', [], {'option': ['2', '4']}),
|
||||
('--option 2', [], {'option': '2'}),
|
||||
('--option 2 4', [], {'option': ['2', '4']}),
|
||||
# Short options
|
||||
('-o', [], {'o': True}),
|
||||
('-o=2', [], {'o': '2'}),
|
||||
('-o=2 4', [], {'o': ['2', '4']}),
|
||||
('-o 2', [], {'o': '2'}),
|
||||
('-o 2 4', [], {'o': ['2', '4']}),
|
||||
# Multiple short options
|
||||
('-ox', [], {'o': True, 'x': True}),
|
||||
('-ox=2', [], {'o': True, 'x': '2'}),
|
||||
('-ox=2 4', [], {'o': True, 'x': ['2', '4']}),
|
||||
('-ox 2', [], {'o': True, 'x': '2'}),
|
||||
('-ox 2 4', [], {'o': True, 'x': ['2', '4']}),
|
||||
):
|
||||
assert parse_args(argv.split()) == (args, kwargs)
|
||||
|
||||
def test_empty_input():
|
||||
for argv in (
|
||||
'',
|
||||
[],
|
||||
{},
|
||||
None,
|
||||
False,
|
||||
):
|
||||
args, kwargs = parse_args(argv)
|
||||
assert (len(args), len(kwargs)) == (1, 0)
|
||||
|
||||
def test_type_errors():
|
||||
for argv in (
|
||||
1,
|
||||
True,
|
||||
{'a': 1},
|
||||
):
|
||||
with pytest.raises(TypeError):
|
||||
parse_args(argv)
|
||||
|
||||
def test_attribute_errors():
|
||||
for argv in (
|
||||
[1],
|
||||
):
|
||||
with pytest.raises(AttributeError):
|
||||
parse_args(argv)
|
||||
Loading…
x
Reference in New Issue
Block a user