Compare commits

...

10 Commits
v0.1.3 ... main

Author SHA1 Message Date
cbb3e36127 Bump version 2023-08-19 21:46:00 +09:00
a19fb22f54 Add simpler tests 2023-08-19 21:45:26 +09:00
1f9e42f222 Update readme with new functionality () 2023-08-19 13:05:57 +09:00
e3fa33cafb Bump version 2023-08-15 15:01:03 +09:00
3e77f0b2b4 Simplify tests 2023-08-15 08:43:40 +09:00
5a591454e9 Refactor changes (simplify) 2023-08-15 08:41:44 +09:00
cc47b0b746 Refactor code and add option -ab=c 2023-08-15 08:41:27 +09:00
Patrick
2c4c16360c Bump version 2023-08-14 15:21:35 +09:00
Patrick
a077a6b569 Minor code improvements and a new test 2023-08-14 15:18:52 +09:00
6488a3d40a Avoid changing argv of input 2023-08-14 11:35:58 +09:00
6 changed files with 110 additions and 94 deletions

1
.gitignore vendored
View File

@ -138,3 +138,4 @@ dmypy.json
# Cython debug symbols # Cython debug symbols
cython_debug/ cython_debug/
.vscode

View File

@ -29,7 +29,7 @@ args, kwargs = parse_args(argv=['pargv.py', '--name=pargv'])
# Specification # 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', '-ab', 'one', 'two']` `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: By calling `args, kwargs = parse_args()`, this would return the following list and dict:
@ -52,3 +52,4 @@ kwargs = {
- One value of an option is stored as a string, multiple values are stored as a list - 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 - 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) - 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`.

View File

@ -1,7 +1,7 @@
import sys import sys
__version__ = '0.1.3' __version__ = '0.2.1'
def parse_args(argv=None): def parse_args(argv=None):
@ -16,33 +16,31 @@ def parse_args(argv=None):
if arg.startswith('-'): if arg.startswith('-'):
break break
args.append(arg.replace('-', '_')) args.append(arg.replace('-', '_'))
argv = argv[len(args):]
kwargs = {} kwargs = {}
values = [] value = None
for arg in argv: for arg in argv[len(args):]:
if not arg.startswith('-'):
values.append(arg) if not arg.startswith('-'):
else: if isinstance(kwargs[key], bool):
if len(values): kwargs[key] = arg
kwargs[key] = values elif isinstance(kwargs[key], str):
values = [] kwargs[key] = [kwargs[key]] + [arg]
key = arg[:2].lstrip('-') + arg[2:].replace('-', '_') elif isinstance(kwargs[key], list):
if arg.startswith('--'): kwargs[key].append(arg)
if '=' in arg: continue
key, value = key.split('=')
values.append(value) key = arg[:2].lstrip('-') + arg[2:].replace('-', '_')
kwargs[key] = values
else: if '=' in key:
kwargs[key] = True key, value = key.split('=')
elif arg.startswith('-'):
for k in key: if not arg.startswith('--'):
kwargs[k] = True for k in key[:-1]:
key = arg[-1] kwargs[k] = True
if len(values): key = key[-1]
kwargs[key] = values
kwargs[key] = value if value else True
value = None
for k, v in kwargs.items():
if isinstance(v, list) and len(v) == 1:
kwargs[k] = v[0]
return (args, kwargs) return (args, kwargs)

View File

@ -6,7 +6,7 @@ with open('README.md') as f:
setup( setup(
name='pargv', name='pargv',
version='0.1.3', version='0.2.1',
description='Parse command line arguments into a list of args and a dict of kwargs.', description='Parse command line arguments into a list of args and a dict of kwargs.',
long_description=long_description, long_description=long_description,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",

View File

@ -6,91 +6,49 @@ from pargv import parse_args
def test_no_argv(): def test_no_argv():
with mock.patch('sys.argv', []): with mock.patch('sys.argv', []):
args, kwargs = parse_args() assert parse_args() == ([], {})
assert args == []
assert kwargs == {}
def test_use_sys_argv_by_default(): def test_use_sys_argv_by_default():
with mock.patch('sys.argv', ['app', 'command', '--option=one', 'two', '--flag', '-io']): with mock.patch('sys.argv', ['argument', '--flag']):
args, kwargs = parse_args() assert parse_args() == (['argument'], {'flag': True})
assert args == ['app', 'command']
assert kwargs == {
'option': ['one', 'two'],
'flag': True,
'i': True,
'o': True
}
def test_single_positional_argument(): def test_single_positional_argument():
args, kwargs = parse_args(['app']) assert parse_args(['argument']) == (['argument'], {})
assert args == ['app']
assert kwargs == {}
def test_single_positional_argument_with_underscore(): def test_single_positional_argument_with_underscore():
args, kwargs = parse_args(['the-app']) assert parse_args(['the-argument']) == (['the_argument'], {})
assert args == ['the_app'] assert parse_args(['a-b']) == (['a_b'], {})
assert kwargs == {}
def test_optional_argument_with_underscore():
assert parse_args(['--a-b']) == ([], {'a_b': True})
def test_positional_arguments(): def test_positional_arguments():
args, kwargs = parse_args(['app', 'command']) assert parse_args(['argument', 'command']) == (['argument', 'command'], {})
assert args == ['app', 'command']
assert kwargs == {}
def test_one_positional_and_optional_argument(): def test_one_positional_and_optional_argument():
args, kwargs = parse_args(['app', '--help']) assert parse_args(['argument', '--help']) == (['argument'], {'help': True})
assert args == ['app']
assert kwargs == {'help': True}
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_one_positional_and_optional_argument_with_values():
assert parse_args(['argument', '--amount=2', '3']) == (['argument'], {'amount': ['2', '3']})
def test_short_arg_with_single_option(): def test_short_arg_with_single_option():
args, kwargs = parse_args(['-a', 'b', '--abc', 'd']) assert parse_args(['-a', 'b', '--abc', 'd']) == ([], {'a': 'b', 'abc': 'd'})
assert args == []
assert kwargs == {
'a': 'b',
'abc': 'd',
}
def test_short_arg_with_multiple_options(): def test_short_arg_with_multiple_options():
args, kwargs = parse_args(['-i', 'a', 'b', '--input', 'c', 'd']) assert parse_args(['-i', 'a', 'b']) == ([], {'i': ['a', 'b']})
assert args == []
assert kwargs == {
'i': ['a', 'b'],
'input': ['c', 'd']
}
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(): def test_long_args_with_equals():
args, kwargs = parse_args(['--input-file=a.py', '--output-file=b.py']) assert parse_args(['--input-file=a.py']) == ([], {'input_file': 'a.py'})
assert args == []
assert kwargs == {
'input_file': 'a.py',
'output_file': 'b.py'
}
def test_unintended_hyphen(): def test_unintended_hyphen():
args, kwargs = parse_args(['---triple-hyphen-']) assert parse_args(['---triple-hyphen-']) == ([], {'_triple_hyphen_': True})
assert args == []
assert kwargs == {'_triple_hyphen_': True}
@pytest.mark.parametrize('argv, args, kwargs', (( @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'], ['/pargv/pargv.py', 'command', 'positional'],
{ {
'flag': True, 'flag': True,
@ -102,6 +60,4 @@ def test_unintended_hyphen():
} }
),)) ),))
def test_all_parameters_at_once(argv, args, kwargs): def test_all_parameters_at_once(argv, args, kwargs):
_args, _kwargs = parse_args(argv) assert parse_args(argv) == (args, kwargs)
assert _args == args
assert _kwargs == kwargs

60
tests/test_parse_args.py Normal file
View 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)