Source code for datadings.tools.argparse

"""
A collection of useful helper functions to create argument parsers.
Using pre-defined arguments ensures that arguments are consistent
across different tools in datadings.

All helper functions in this module follow the convention that a
function called ``argument_indir`` adds the ``indir`` argument to
the given parser, including additional configuration and help text.

Note:
    Any of the default arguments given by the ``argument_*``
    functions can be overwritten.
    For example, ``argument_shuffle`` defines
    ``choices = ['yes', 'no']``.
    If those are too formal for your liking, you can also use
    ``argument_shuffle(parser, choices=['yeah', 'naw']``.
    But please don't.
"""

import argparse
from functools import partial
from multiprocessing import cpu_count


[docs]class YesNoAction(argparse.Action): """ Action like ``store_true`` that checks if ``value == yes``. """ def __call__(self, parser, namespace, yesno, option_string=None): setattr(namespace, self.dest, yesno == 'yes')
[docs]class MinMaxAction(argparse.Action): """ Action to clamp value between given min and max values. Create subclass to set ``min_value`` and ``max_value``:: class Action(MinMaxAction): min_value = 1 max_value = 7 parser.add_argument('onetoseven', action=Action) """ min_value = None max_value = None def __call__(self, parser, namespace, value, option_string=None): value = max(self.min_value, value or self.max_value) value = min(value, self.max_value) setattr(namespace, self.dest, value)
[docs]def make_parser( description, indir=True, outdir=True, no_confirm=True, skip_verification=True, shuffle=True, formatter_class=argparse.RawDescriptionHelpFormatter, **kwargs ) -> argparse.ArgumentParser: """ Create an ``ArgumentParser`` with a set of common arguments. Parameters: description: Description text displayed before arguments. Usually ``__doc__`` is fine. indir: If True, add ``indir`` argument. outdir: If True, add ``outdir`` argument. no_confirm: If True, add ``no_confirm`` argument. skip_verification: If True, add ``skip_verification`` argument. shuffle: If True, add ``shuffle`` argument. formatter_class: Description formatter, defaults to raw. kwargs: kwargs given to ``ArgumentParser``. Returns: ``ArgumentParser``. """ parser = argparse.ArgumentParser( description=description, formatter_class=formatter_class, **kwargs, ) if indir: argument_indir(parser) if outdir: argument_outdir(parser) if no_confirm: argument_no_confirm(parser) if skip_verification: argument_skip_verification(parser) if shuffle: argument_shuffle(parser) return parser
[docs]def make_parser_simple( description, indir=False, outdir=False, no_confirm=False, skip_verification=False, shuffle=False, formatter_class=argparse.RawDescriptionHelpFormatter, **kwargs ) -> argparse.ArgumentParser: """ Same as :py:func:`make_parser`, but add no arguments by default. """ return make_parser( description, indir=indir, outdir=outdir, no_confirm=no_confirm, skip_verification=skip_verification, shuffle=shuffle, formatter_class=formatter_class, **kwargs )
def __add_argument(parser_pos, *args, **kwargs): parser = args[parser_pos] args = args[:parser_pos] + args[parser_pos+1:] parser.add_argument(*args, **kwargs) def __make_argument(*args, **kwargs): p = partial(__add_argument, len(args), *args, **kwargs) p.__doc__ = \ """Add the following argument to the given ``ArgumentParser``: .. code-block:: parser.add_argument( {args}, {kwargs} ) Parameters: parser: Call add_argument on this ``ArgumentParser``. args: Additional positional arguments for add_argument. kwargs: Additional keyword arguments for add_argument. Can override keyword arguments specified above. """.format(args=', '.join(map(repr, args)), kwargs='\n '.join('%s=%r,' % arg for arg in kwargs.items())) return p argument_indir = __make_argument( 'indir', type=str, default='.', metavar='INDIR', help='Directory that contains dataset source files.' ) argument_outdir = __make_argument( '-o', '--outdir', type=str, default=None, metavar='PATH', help='Output directory. Defaults to indir.', ) argument_infile = __make_argument( 'infile', type=str, default=None, help='Input file.', ) argument_outfile = __make_argument( '-o', '--outfile', type=str, default=None, metavar='PATH', help='Output file.', ) argument_outfile_positional = __make_argument( 'outfile', type=str, help='Output file.', ) argument_outfiles = __make_argument( '-o', '--outfiles', type=str, default=None, metavar='PATH', help='Output files.', nargs='+' ) argument_no_confirm = __make_argument( '-y', '--no-confirm', dest='no_confirm', action='store_true', help='Don’t require user interaction.', ) argument_skip_verification = __make_argument( '-s', '--skip-verification', action='store_true', help='Skip verification of source files.' ) argument_shuffle = __make_argument( '--shuffle', default='yes', choices=['yes', 'no'], action=YesNoAction, help='Write samples in random order. (default: yes)' ) argument_calculate_weights = __make_argument( '--calculate-weights', action='store_true', help='Calculate median-frequency class weights.' )
[docs]def argument_threads(parser, default=1, max_threads=0): """ Add threads argument to parser. Parameters: parser: Argument is added here. default: Default number of threads. max_threads: Maximum number of threads. If >0, use given number. If 0 use ``cpu_count()``. if <0, use ``-max_threads*cpu_count()`` """ if max_threads < 0: cpus = cpu_count() * -max_threads else: cpus = max_threads or cpu_count() class Action(MinMaxAction): min_value = 1 max_value = cpus default = min(default, cpus) parser.add_argument( '-t', '--threads', default=default, metavar='0-%d' % cpus, type=int, action=Action, help='Number of threads for conversion. ' '0 uses all available CPUs (default %d).' % default )