diff --git a/patacrep/tools/__main__.py b/patacrep/tools/__main__.py index 13794b14..1bfc3fab 100644 --- a/patacrep/tools/__main__.py +++ b/patacrep/tools/__main__.py @@ -2,85 +2,26 @@ """Command line client to :mod:`tools`""" -import argparse import logging -import operator -import os -import pkgutil -import re import sys +import argdispatch + import patacrep # Logging configuration logging.basicConfig(level=logging.INFO) LOGGER = logging.getLogger("patatools") -def _execlp(program, args): - """Call :func:`os.execlp`, adding `program` as the first argument to itself.""" - return os.execlp(program, program, *args) - -def _iter_subcommands(): - """Iterate over subcommands. - - The objects returned are tuples of: - - the name of the command; - - its description; - - the function to call to execute the subcommand. - """ - subcommands = [] - - # Get python subcommands - path = [os.path.join(item, "patacrep", "tools") for item in sys.path] - prefix = "patacrep.tools." - module_re = re.compile(r'{}(?P[^\.]*)\.__main__'.format(prefix)) - for module_loader, name, _ in pkgutil.walk_packages(path, prefix): - match = module_re.match(name) - if match: - module = module_loader.find_module(match.string).load_module() - if hasattr(module, "SUBCOMMAND_DESCRIPTION"): - subcommands.append(match.groupdict()['subcommand']) - yield ( - match.groupdict()['subcommand'], - getattr(module, "SUBCOMMAND_DESCRIPTION"), - module.main, - ) - -class ArgumentParser(argparse.ArgumentParser): - """Proxy class to circumvent an :mod:`argparse` bug. - - Contrarily to what documented, the `argparse.REMAINDER - `_ `nargs` setting - does not include the remainder arguments if the first one begins with `-`. - - This bug is reperted as `17050 `_. This - class can be deleted once this bug has been fixed. - """ - - def parse_args(self, args=None, namespace=None): - if args is None: - args = sys.argv[1:] - subcommands = [command[0] for command in set(_iter_subcommands())] - if len(args) > 0: - if args[0] in subcommands: - args = [args[0], "--"] + args[1:] - - value = super().parse_args(args, namespace) - - if hasattr(value, 'remainder'): - value.remainder = value.remainder[1:] - return value - - def commandline_parser(): """Return a command line parser.""" - parser = ArgumentParser( + parser = argdispatch.ArgumentParser( prog="patatools", description=( "Miscellaneous tools for patacrep." ), - formatter_class=argparse.RawTextHelpFormatter, + formatter_class=argdispatch.RawTextHelpFormatter, ) parser.add_argument( @@ -96,11 +37,7 @@ def commandline_parser(): ) subparsers.required = True subparsers.dest = "subcommand" - - for command, message, function in sorted(_iter_subcommands(), key=operator.itemgetter(0)): - sub1 = subparsers.add_parser(command, help=message, add_help=False) - sub1.add_argument('remainder', nargs=argparse.REMAINDER) - sub1.set_defaults(function=function) + subparsers.add_submodules("patacrep.tools") return parser @@ -108,9 +45,7 @@ def main(args=None): """Main function""" if args is None: args = sys.argv - parser = commandline_parser() - args = parser.parse_args(args[1:]) - args.function(["patatools-{}".format(args.subcommand)] + args.remainder) + commandline_parser().parse_args(args[1:]) if __name__ == "__main__": main() diff --git a/patacrep/tools/cache/__main__.py b/patacrep/tools/cache/__main__.py index 6bf2cd8e..5c75d52d 100644 --- a/patacrep/tools/cache/__main__.py +++ b/patacrep/tools/cache/__main__.py @@ -1,4 +1,4 @@ -"""`patatools cache` command: cache manipulation.""" +"""Perform operations on cache.""" import argparse import logging @@ -11,7 +11,6 @@ from patacrep import errors from patacrep.songbook import open_songbook LOGGER = logging.getLogger("patatools.cache") -SUBCOMMAND_DESCRIPTION = "Perform operations on cache." def filename(name): """Check that argument is an existing, readable file name. @@ -27,7 +26,7 @@ def commandline_parser(): parser = argparse.ArgumentParser( prog="patatools cache", - description=SUBCOMMAND_DESCRIPTION, + description="Convert between song formats.", formatter_class=argparse.RawTextHelpFormatter, ) diff --git a/patacrep/tools/convert/__main__.py b/patacrep/tools/convert/__main__.py index a7b25455..65b82d8b 100644 --- a/patacrep/tools/convert/__main__.py +++ b/patacrep/tools/convert/__main__.py @@ -1,4 +1,4 @@ -"""`patatools.convert` command: convert between song formats""" +"""Convert between song formats.""" import os import logging @@ -10,7 +10,6 @@ from patacrep.utils import yesno from patacrep.build import config_model LOGGER = logging.getLogger("patatools.convert") -SUBCOMMAND_DESCRIPTION = "Convert between song formats" def _usage(): return "patatools convert INPUTFORMAT OUTPUTFORMAT FILES" diff --git a/setup.py b/setup.py index 4d0c74c5..aacfe910 100755 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ setup( packages=find_packages(exclude=["test*"]), license="GPLv2 or any later version", install_requires=[ - "unidecode", "jinja2", "ply", "pyyaml", + "argdispatch", "unidecode", "jinja2", "ply", "pyyaml", ], entry_points={ 'console_scripts': [