diff --git a/CHANGELOG.md b/CHANGELOG.md index c77b9fc..0ac96b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ ## 0.13.0 -+ Added module `argparse` with class `LetterSubparsers()` ++ Added module `argparse` with class `LetterSubparsers()`, which allows pacman-style args by preprocessing them + before feeding them to parse_args + Module `sqlalchemy`: * removed deprecated alias `entity_base()`. use `declarative_base()` instead. * fix imports. diff --git a/src/suou/__init__.py b/src/suou/__init__.py index fadb067..aa24d72 100644 --- a/src/suou/__init__.py +++ b/src/suou/__init__.py @@ -41,7 +41,7 @@ from .color import OKLabColor, chalk, WebColor, RGBColor, LinearRGBColor, \ from .mat import Matrix from .argparse import LetterSubparsers -__version__ = "0.13.0a4" +__version__ = "0.13.0a5" __all__ = ( 'ColorFormatter', diff --git a/src/suou/argparse.py b/src/suou/argparse.py index 563d6be..39df63d 100644 --- a/src/suou/argparse.py +++ b/src/suou/argparse.py @@ -33,12 +33,22 @@ class LetterSubparsers(object): _parser: argparse.ArgumentParser _letters: dict[str, str] _subparsers: argparse._SubParsersAction[argparse.ArgumentParser] + _has_verbose: bool = False def __init__(self, parser : argparse.ArgumentParser, *, dest: str = 'action', **kwargs): self._parser = parser self._letters = {} self._subparsers = parser.add_subparsers(dest = dest, **kwargs) + def add_verbose(self, *, help: str = "show more logs (e.g. debug)"): + """ + Add a "-v" / "--verbose" argument to the main parser. + + This allows the argument to be everywhere in the argv. + """ + self._parser.add_argument('-v', '--verbose', action='count', help=help) + self._has_verbose = True + def action(self, /, letter: str, name: str | None = None, **kwargs): """ Decorator which adds a subparser of an argument parser's subparsers, and specifies a letter to make a shorthand in pacman style. @@ -65,11 +75,11 @@ class LetterSubparsers(object): return func return decorator - def parse_args(self, argv = None, system_exit: bool = True): + def parse_args(self, argv: list[str] = None, system_exit: bool = True): """ Variation of ArgumentParser.parse_args() that takes shortcut letters into account. - Best used together with letter_action(). + Best used together with .action(). """ if argv is None: @@ -77,15 +87,21 @@ class LetterSubparsers(object): else: argv = list(argv) + opt_start = 0 + + # preprocess the letters if len(argv) > 0: first_arg = argv.pop(0) - + if first_arg.startswith('-') and len(first_arg) >= 2: - letter, rest = first_arg[1], first_arg[2:] - if letter in self._letters: - argv.insert(0, self._letters[letter]) - if rest: - argv.insert(1, "-" + rest) + for idx, letter in enumerate(first_arg): + rest = first_arg[1:idx] + first_arg[idx+1:] + if letter in self._letters: + argv.insert(0, self._letters[letter]) + if rest: + argv.insert(1, "-" + rest) + opt_start = 1 + break else: # put it back argv.insert(0, first_arg) @@ -93,12 +109,31 @@ class LetterSubparsers(object): # put it back argv.insert(0, first_arg) + # preprocess the verbose + if self._has_verbose and len(argv) > opt_start: + nargv = argv[:opt_start] + vc = 0 + for arg in argv[opt_start:]: + if arg.startswith('-') and not arg.startswith('--'): + arg_vc = sum(1 for l in arg[1:] if l == 'v') + arg_vless = arg.replace('v', '') + if arg_vless != '-': + nargv.append(arg_vless) + vc += arg_vc + elif arg == '--verbose': + vc += 1 + else: + nargv.append(arg) + argv = ["--verbose"] * vc + nargv + try: - return self._parser.parse_args(argv) + args = self._parser.parse_args(argv) except SystemExit: # prevent SystemExit at parse fail if system_exit: raise + else: + return args __all__ = ('LetterSubparsers',)