Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 42 additions & 55 deletions argunparse/argument_unparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@ def option_should_be_skipped(value: t.Any) -> bool:
class ArgumentUnparser:
"""For performing reverse operation to what argparse.ArgumentParser does."""

# pylint: disable = too-many-arguments
# pylint: disable = too-many-arguments, too-many-positional-arguments
def __init__(
self, short_opt: str = '-', long_opt: str = '--', opt_value: str = '=',
begin_delim: str = '"', end_delim: str = '"') -> None:

assert isinstance(short_opt, str)
assert isinstance(long_opt, str)
assert isinstance(opt_value, str)
Expand All @@ -46,78 +45,66 @@ def unparse_arg(self, arg: t.Any) -> str: # pylint: disable = no-self-use
arg = '""'
return arg

def unparse_args(self, arguments: t.Sequence[t.Any],
*, to_list: bool = False) -> t.Union[str, t.List[str]]:
"""Convert list to string of command-line args."""
def unparse_args_to_list(self, arguments: t.Sequence[t.Any]) -> list[str]:
"""Convert list of objects to a list of command-line args."""
unparsed_list = []
for arg in arguments:
unparsed_list.append(self.unparse_arg(arg))
_LOG.debug('%s: unparsed args to %s', self, unparsed_list)
if to_list:
return unparsed_list
unparsed = ' '.join(unparsed_list)
return unparsed_list

def unparse_args(self, arguments: t.Sequence[t.Any]) -> str:
"""Convert list to string of command-line args."""
unparsed = ' '.join(self.unparse_args_to_list(arguments))
_LOG.debug('%s: converted unparsed args to string "%s"', self, unparsed)
return unparsed

def unparse_option(self, key: str, value: t.Any,
*, to_list: bool = False) -> t.Union[str, t.List[str]]:
"""Convert a key-value pair into a string that can be used as a command-line option."""
def unparse_option_to_list(self, key: str, value: t.Any) -> list[str]:
"""Convert a key-value pair into a short list to be used as a command-line option."""
if option_should_be_skipped(value):
return [] if to_list else ''
return []
unparsed_key = f'{self._long_opt if len(key) > 1 else self._short_opt}{key}'
if not treat_as_option_with_no_value(value):
unparsed_value = self.unparse_arg(value)
if to_list and (self._opt_value == ' ' or treat_as_option_with_no_value(value)):
if treat_as_option_with_no_value(value):
return [unparsed_key]
return [unparsed_key, unparsed_value]
if not treat_as_option_with_no_value(value):
unparsed_option = f'{unparsed_key}{self._opt_value}{unparsed_value}'
if to_list:
return [unparsed_option]
if treat_as_option_with_no_value(value):
return unparsed_key
return unparsed_option
return [unparsed_key]
unparsed_value = self.unparse_arg(value)
if self._opt_value == ' ':
return [unparsed_key, unparsed_value]
return [f'{unparsed_key}{self._opt_value}{unparsed_value}']

def unparse_options(self, options: t.Mapping[str, t.Any],
*, to_list: bool = False) -> t.Union[str, t.List[str]]:
"""Convert dictionary to string of command-line args."""
def unparse_option(self, key: str, value: t.Any) -> str:
"""Convert a key-value pair into a string that can be used as a command-line option."""
if option_should_be_skipped(value):
return ''
unparsed = ' '.join(self.unparse_option_to_list(key, value))
return unparsed

def unparse_options_to_list(self, options: t.Mapping[str, t.Any]) -> list[str]:
"""Convert dictionary to a list of command-line args."""
unparsed_list: t.List[str] = []
for key, value in options.items():
if option_should_be_skipped(value):
continue
unparsed_option = self.unparse_option(key, value, to_list=to_list)
if to_list:
unparsed_list += unparsed_option
else:
assert isinstance(unparsed_option, str), type(unparsed_option)
unparsed_list.append(unparsed_option)
unparsed_list += self.unparse_option_to_list(key, value)
_LOG.debug('%s: unparsed options to %s', self, unparsed_list)
if to_list:
return unparsed_list
unparsed = ' '.join(unparsed_list)
return unparsed_list

def unparse_options(self, options: t.Mapping[str, t.Any]) -> str:
"""Convert dictionary to string of command-line args."""
unparsed = ' '.join(self.unparse_options_to_list(options))
_LOG.debug('%s: converted unparsed options to string "%s"', self, unparsed)
return unparsed

def unparse_options_and_args(self, options: t.Mapping[str, t.Any], arguments: t.Sequence[t.Any],
*, to_list: bool = False) -> t.Union[str, t.List[str]]:
def unparse_options_and_args_to_list(
self, options: t.Mapping[str, t.Any], arguments: t.Sequence[t.Any]) -> list[str]:
"""Convert dictionary and list to a list of command-line args."""
unparsed_options = [] if options is None else self.unparse_options_to_list(options)
unparsed_args = [] if arguments is None else self.unparse_args_to_list(arguments)
return unparsed_options + unparsed_args

def unparse_options_and_args(
self, options: t.Mapping[str, t.Any], arguments: t.Sequence[t.Any]) -> str:
"""Convert dictionary and list to string of command-line args."""
if options is None:
unparsed_options = [] if to_list else ''
else:
unparsed_options = self.unparse_options(options, to_list=to_list)
if arguments is None:
unparsed_args = [] if to_list else ''
else:
unparsed_args = self.unparse_args(arguments, to_list=to_list)
if to_list:
return unparsed_options + unparsed_args
unparsed = []
if unparsed_options:
unparsed.append(unparsed_options)
if unparsed_args:
unparsed.append(unparsed_args)
return ' '.join(unparsed)
return ' '.join(self.unparse_options_and_args_to_list(options, arguments))

def unparse_to_list(self, *args, **kwargs) -> list:
"""Unparse given args as command-line arguments and kwargs as command-line options.
Expand All @@ -126,7 +113,7 @@ def unparse_to_list(self, *args, **kwargs) -> list:

This process is a reverse of what built-in argparse module does with parse_args() method.
"""
return self.unparse_options_and_args(kwargs, args, to_list=True)
return self.unparse_options_and_args_to_list(kwargs, args)

def unparse(self, *args, **kwargs) -> str:
"""Unparse given args as command-line arguments and kwargs as command-line options.
Expand Down
2 changes: 1 addition & 1 deletion requirements_test.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
-r requirements.txt
boilerplates[logging,packaging_tests] ~= 1.2
boilerplates[logging,packaging-tests] ~= 1.2
5 changes: 2 additions & 3 deletions test/examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
import collections
import itertools
import pathlib
import typing as t

_UNPARSER_INIT_ARGS_EXAMPLES = [
('short_opt', {'-', '--', '_', '__'}),
('long_opt', {'-', '--', '_', '__'}),
('opt_value', {'=', ' ', ''})]

OPTIONS = {
OPTIONS: dict[str, dict[str, t.Any]] = {
'-h': {'h': True}, '--verbosity=100': {'verbosity': 100},
'--DEFINE=NDEBUG': {'DEFINE': 'NDEBUG'},
'--long_flag': {'long_flag': True}, '-o=out_file.txt': {'o': 'out_file.txt'},
Expand All @@ -35,8 +36,6 @@
('-123456790', -123456790), (str(2**16), 2**16), ('x', 'x'), ('y', 'y'), ('z', 'z'),
(repr('hello world'), 'hello world')}

ARGUMENTS_SKIPPED = {}

ARGUMENTS_VARIANTS = [([ref1, ref2], [arg1, arg2])
for ((ref1, arg1), (ref2, arg2)) in itertools.permutations(ARGUMENTS, 2)]

Expand Down
34 changes: 17 additions & 17 deletions test/test_argument_unparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,41 +24,41 @@ def test_option(self):
for reference, option in OPTIONS.items():
with self.subTest(option=option):
key, value = list(*itertools.chain(option.items()))
list_result = unparser.unparse_option_to_list(key, value)
self.assertListEqual([reference], list_result)
result = unparser.unparse_option(key, value)
self.assertEqual(reference, result)
list_result = unparser.unparse_option(key, value, to_list=True)
self.assertListEqual([reference], list_result)

def test_option_skipped(self):
unparser = ArgumentUnparser()
for option in OPTIONS_SKIPPED.items():
with self.subTest(option=option):
key, value = option
list_result = unparser.unparse_option_to_list(key, value)
self.assertListEqual(list_result, [])
result = unparser.unparse_option(key, value)
self.assertEqual(result, '')
list_result = unparser.unparse_option(key, value, to_list=True)
self.assertListEqual(list_result, [])

def test_option_space(self):
unparser = ArgumentUnparser(opt_value=' ')
for reference, option in OPTIONS.items():
with self.subTest(option=option):
key, value = list(*itertools.chain(option.items()))
list_result = unparser.unparse_option_to_list(key, value)
self.assertListEqual(reference.split('='), list_result)
result = unparser.unparse_option(key, value)
self.assertEqual(reference.replace('=', ' '), result)
list_result = unparser.unparse_option(key, value, to_list=True)
self.assertListEqual(reference.split('='), list_result)

def test_options(self):
_LOG.debug('testing %i option variants...',
len(OPTIONS_VARIANTS) + len(OPTIONS_SKIPPED_VARIANTS))
unparser = ArgumentUnparser()
for reference, options in itertools.chain(OPTIONS_VARIANTS, OPTIONS_SKIPPED_VARIANTS):
with self.subTest(options=options):
list_result = unparser.unparse_options_to_list(options)
self.assertListEqual(reference, list_result)
result = unparser.unparse_options(options)
self.assertEqual(' '.join(reference), result)
list_result = unparser.unparse_options(options, to_list=True)
self.assertListEqual(reference, list_result)

def test_options_space(self):
_LOG.debug('testing %i option variants...',
Expand All @@ -68,7 +68,7 @@ def test_options_space(self):
with self.subTest(options=options):
result = unparser.unparse_options(options)
self.assertEqual(' '.join(reference).replace('=', ' '), result)
list_result = unparser.unparse_options(options, to_list=True)
list_result = unparser.unparse_options_to_list(options)
fixed_reference = list(itertools.chain.from_iterable(
[_.split('=') if '=' in _ else [_] for _ in reference]))
self.assertListEqual(fixed_reference, list_result)
Expand All @@ -84,30 +84,30 @@ def test_args(self):
unparser = ArgumentUnparser()
for reference, args in ARGUMENTS_VARIANTS:
with self.subTest(args=args):
list_result = unparser.unparse_args_to_list(args)
self.assertListEqual(reference, list_result)
result = unparser.unparse_args(args)
self.assertEqual(' '.join(reference), result)
list_result = unparser.unparse_args(args, to_list=True)
self.assertListEqual(reference, list_result)

def test_options_and_args(self):
unparser = ArgumentUnparser()
for (reference_options, options), (reference_args, args) in OPTIONS_AND_ARGUMENTS_VARIANTS:
with self.subTest(options=options, args=args):
list_result = unparser.unparse_options_and_args_to_list(None, args)
self.assertListEqual(reference_args, list_result)
result = unparser.unparse_options_and_args(None, args)
self.assertEqual(' '.join(reference_args), result)
list_result = unparser.unparse_options_and_args(None, args, to_list=True)
self.assertListEqual(reference_args, list_result)

list_result = unparser.unparse_options_and_args_to_list(options, None)
self.assertListEqual(reference_options, list_result)
result = unparser.unparse_options_and_args(options, None)
self.assertEqual(' '.join(reference_options), result)
list_result = unparser.unparse_options_and_args(options, None, to_list=True)
self.assertListEqual(reference_options, list_result)

list_result = unparser.unparse_options_and_args_to_list(options, args)
self.assertListEqual(reference_options + reference_args, list_result)
result = unparser.unparse_options_and_args(options, args)
self.assertEqual(
' '.join(itertools.chain(reference_options, reference_args)), result)
list_result = unparser.unparse_options_and_args(options, args, to_list=True)
self.assertListEqual(reference_options + reference_args, list_result)

def test_unparse(self):
unparser = ArgumentUnparser()
Expand Down