def main(*, flag1: Argument(aliases=('f', 'flagone')) = False, flag2: Argument(aliases='Ft') = True): ''' Aliases are sequences, so a normal string will be split into characters for each alias, while multi-character aliases should use tuples. ''' print('flag1={} flag2={}'.format(*map(repr, (flag1, flag2))))
def main( *, arg: Argument( completer=lambda **_: ('app', 'apk', 'exe')) = 'one', file: Argument( aliases=('f',), completer=lambda **_: ('/usr/bin/env', '/usr/bin/python'), completion_validator=fuzzy_path_validator) = None): print('arg={}'.format(repr(arg)))
def main(subcommand: Argument(choices=('pairs', 'kwargs', 'auto')), *_REMAINDER_): if subcommand == 'auto': if sys.version_info >= (3, 6): return kwargs.execute(_REMAINDER_) else: return pairs.execute(_REMAINDER_) elif subcommand == 'pairs': return pairs.execute(_REMAINDER_) elif subcommand == 'kwargs': return kwargs.execute(_REMAINDER_) else: raise NoMatchingDelegate()
#!/usr/bin/env python3 ''' $ decorator_arguments.py --<TAB> --arg\x0b--file $ decorator_arguments.py --arg <TAB> app\x0bapk\x0bexe $ decorator_arguments.py --arg a<TAB> app\x0bapk $ decorator_arguments.py -f /u/b/e<TAB> /usr/bin/env ''' from hashbang import command, Argument from hashbang.completion import fuzzy_path_validator @command(Argument('arg', completer=lambda **_: ('app', 'apk', 'exe')), Argument('file', aliases=('f', ), completer=lambda **_: ('/usr/bin/env', '/usr/bin/python'), completion_validator=fuzzy_path_validator)) def main(*, arg='one', file=None): print('arg={}'.format(repr(arg))) if __name__ == '__main__': main.execute()
def main(arg1: Argument(remainder=True), flag1=False): print('arg1={} _REMAINDER_={} flag1={}'.format( *map(repr, (arg1, _REMAINDER_, flag1))))
#!/usr/bin/env python3 ''' $ append.py --help > usage: append.py [--arg ARG] [-h] > > optional arguments: > --arg ARG > -h, --help show this help message and exit $ append.py arg=[] $ append.py --arg chocolate arg=['chocolate'] $ append.py --arg chocolate --arg hazelnut --arg nutella arg=['chocolate', 'hazelnut', 'nutella'] ''' from hashbang import command, Argument @command(Argument('arg', append=True)) def main(*, arg=()): print('arg={}'.format(*map(repr, (arg, )))) if __name__ == '__main__': main.execute()
def main(*, arg: Argument(help='The argument') = 'one'): print('arg={}'.format(repr(arg)))
def main(arg1, *rem: Argument(remainder=True), flag1=False): print('arg1={} rem={} flag1={}'.format(*map(repr, (arg1, rem, flag1))))
def main(*, arg: Argument(choices=('one', 'two', 'three')) = 'one'): print('arg={}'.format(repr(arg)))
> optional arguments: > --arg ARG > --flag > -h, --help show this help message and exit $ required.py # returncode=2 stderr=True usage: required.py --arg ARG --flag [-h] required.py: error: the following arguments are required: --arg $ required.py --arg chocolate # returncode=2 stderr=True usage: required.py --arg ARG --flag [-h] required.py: error: one of the arguments --flag is required $ required.py --arg chocolate --flag arg='chocolate' flag=True $ required.py --arg chocolate --noflag arg='chocolate' flag=False ''' from hashbang import command, Argument @command(Argument('arg', required=True), Argument('flag', required=True)) def main(*, arg=None, flag=False): print('arg={} flag={}'.format(*map(repr, (arg, flag)))) if __name__ == '__main__': main.execute()
#!/usr/bin/env python3 ''' $ required_invalid.py # returncode=1 stderr=True Error: "required" does not apply to positional arguments. Specify a default \ value if you want optional positional args. e.g. def func(foo=123) ''' from hashbang import command, Argument @command(Argument('arg', required=True)) def main(arg='one'): print('arg={}'.format(repr(arg))) if __name__ == '__main__': main.execute()
Run pol from another Python script. This is designed to ease the transition from the one-liner to a more full-fledged script file. By running pol in Python, you get the results in Python list or objects, while still keeping the input parsing and output formatting capabilities of pol. Serious scripts should migrate away from those as well, perhaps outputting json and then using pol as a data-formatting pass-through. ''' result, _ = _execute_internal(*args, **kwargs) if isinstance(result, (str, bytes)): return result if isinstance(result, collections.abc.Iterable): return list(result) else: return result @command(Argument('field_separator', aliases='F'), Argument('input_format', choices=list(PARSERS)), Argument('output_format', choices=list(PRINTERS)), formatter_class=argparse.RawDescriptionHelpFormatter) def _command_line(prog, *_REMAINDER_, input=None, field_separator=None, record_separator='\n', input_format='awk', output_format='auto'): ''' pol - Python one liners to easily parse and process data in Python. Pol processes text information from stdin or a given file and evaluates the given input `prog` and prints the result.
def main(*args: Argument(type=int), port: Argument(type=int) = 0): print('args={} port={}'.format(*map(repr, (args, port)))) print('{}'.format(type(port)))
def main(*, flag1: Argument(choices=('a', 'b')) = False): print('flag1={}'.format(repr(flag1)))
This example uses nargs=argparse.REMAINDER, to capture everything after an optional argument ('-c'). argparse.REMAINDER should typically used for optional arguments, whereas *_REMAINDER_ should be used for positional arguments (capturing without a '-c' flag). ''' def add_argument(self, cmd, arg_container, param): argument = arg_container.add_argument( *self.get_flag_names(param.name.rstrip('_')), action='store', nargs=argparse.REMAINDER, # For append arguments, argparse calls append() directly # on the default, so make sure we don't reuse the default # value, and convert to list to allow tuples as defaults default=param.default, dest=param.name, choices=self.choices, help=self.help, type=self.type, required=self.required) @command(Argument('interactive', aliases=('i', )), RemainderArgument('command', aliases=('c', ))) def bash(scriptfile=None, *, interactive=False, command=()): print('scriptfile={} interactive={} command={}'.format( *map(repr, (scriptfile, interactive, command)))) if __name__ == '__main__': bash.execute()
> > optional arguments: > --flag1 > --verbose $ context.py subcommand2 123 456 --verbose Executing subcommand2 with [Context]... subcommand2 arg='123' remaining=('456',) flag2=False verbose=True \ context=[Context] ''' from hashbang import command, Argument, NoMatchingDelegate @command( Argument('context_', py_only=True)) def subcommand1(arg, *remaining, flag1=False, verbose=False, context_=None): print( 'subcommand1 arg={} remaining={} flag1={} verbose={} context={}' .format(*map(repr, (arg, remaining, flag1, verbose, context_)))) @command( Argument('context', py_only=True)) def subcommand2(arg, *remaining, flag2=False, verbose=False, context=None): print( 'subcommand2 arg={} remaining={} flag2={} verbose={} context={}' .format(*map(repr, (arg, remaining, flag2, verbose, context)))) class Context:
import sys from hashbang import command, Argument def exception_handler(exception): try: raise exception except CustomError as e: print('CustomError') sys.exit(e.exitcode()) class CustomError(Exception): def __init__(self, code): super().__init__() self.code = code def exitcode(self): return self.code @command(Argument('exitcode', type=int), exception_handler=exception_handler) def main(*, exitcode=1): raise CustomError(exitcode) if __name__ == '__main__': main.execute()
import sys from hashbang import command, Argument from pathlib import Path DIR = Path(__file__).parent class ConfigFile: def __init__(self, filepath): self.filepath = filepath def apply_hashbang_extension(self, cmd): with self.filepath.open('r') as configfile: lines = list(configfile.readlines()) for line in lines: key, value, *_ = line.rstrip('\n').split('=', 1) + [None] cmd.default_values[key] = value @command(ConfigFile(DIR / 'configfile'), Argument('after', type=int), Argument('before', type=int), Argument('context', type=int)) def grep(file=None, *, after=0, before=0, context=0, color=False): print('file={} after={} before={} context={} color={}'.format( *[repr(i) for i in (file, after, before, context, color)])) if __name__ == '__main__': grep.execute()
def version_bump(current_version, bump): if bump == 'nobump': return current_version parts = current_version.split('.') bump_index = VERSION_COMPONENTS.index(bump) int_parts = (int(p) for p in parts) int_parts_bumped = ( p if i < bump_index # part before bump, keep unchanged else p + 1 if i == bump_index # part to bump else 0 # part after bump, zero out for i, p in enumerate(int_parts)) parts = (str(p) for p in int_parts_bumped) return '.'.join(parts) @command(Argument('bump', choices=VERSION_COMPONENTS + ('nobump', )), Argument('upload', help='Whether to upload the created package to PyPI'), Argument('version', help='Specify the version name explicitly instead of ' 'bumping'), Argument('git_status_check', help='Whether to check and return a failure ' 'if the current git repo has unstaged, ' 'untracked, or uncommitted files')) def main(bump=None, *, upload=True, version=None, git_status_check=True, test=False,