def test_mw_unres(): def unres_cmd(unresolved_arg): return unresolved_arg cmd = Command(unres_cmd) assert cmd.func is unres_cmd with pytest.raises( NameError, match= "unresolved middleware or handler arguments: .*unresolved_arg.*"): cmd.run(['unres_cmd']) def inner_mw(next_, arg): return next_() @face_middleware(provides='arg', flags=[Flag('--verbose', parse_as=True)]) def outer_mw(next_): return next_(arg=1) def ok_cmd(arg): return None cmd = Command(ok_cmd, middlewares=[outer_mw]) cmd.add_middleware(inner_mw) with pytest.raises( NameError, match= "unresolved middleware or handler arguments: .*arg.* check middleware order." ): cmd.run(['ok_cmd']) return
def test_cmd_name(): def handler(): return 0 Command(handler, name='ok_cmd') name_err_map = { '': 'non-zero length string', 5: 'non-zero length string', 'name_': 'without trailing dashes or underscores', 'name--': 'without trailing dashes or underscores', 'n?me': ('valid subcommand name must begin with a letter, and' ' consist only of letters, digits, underscores, and' ' dashes') } for name, err in name_err_map.items(): with pytest.raises(ValueError, match=err): Command(handler, name=name) return
def test_next_reserved(): def bad_cmd(next_): return cmd = Command(bad_cmd) with pytest.raises(NameError): cmd.run(['bad_cmd'])
def test_help_subcmd(): hhandler = HelpHandler(flag=False, subcmd='help') cmd = Command(None, 'cmd', help=hhandler) try: cmd.run(['cmd', 'help']) except SystemExit as se: assert se.code == 0 with pytest.raises(ValueError, match='requires a handler function or help handler'): Command(None, help=None)
def test_post_posargs(): cmd = Command(lambda posargs, post_posargs: None, name='cmd') res = cmd.parse(['cmd']) assert res.posargs == () assert res.post_posargs == None # TODO: if this ^ isn't a useful signal, it would be more convenient to have the # behavior be the same as below res = cmd.parse(['cmd', '--']) assert res.posargs == () assert res.post_posargs == ()
def test_err_subcmd_prog_name(): cmd = Command(lambda: print("foo"), "foo") subcmd = Command(lambda: print("bar"), "bar") subcmd.add(Command(lambda: print("baz"), "baz")) cmd.add(subcmd) cc = CommandChecker(cmd) res = cc.fail('fred.py bar ba') assert 'fred.py' in res.stderr assert 'foo' not in res.stderr
def get_subcmd_cmd(): subcmd = Command(None, name='subcmd', doc='the subcmd help') subcmd.add(_subsubcmd, name='subsubcmd', posargs={'count': 2, 'name': 'posarg_item'}) cmd = Command(None, 'halp', doc='halp help') cmd.add(subcmd) return cmd
def test_bad_posargspec(): # issue #11 assert PosArgSpec(name=None).display.name is not None assert PosArgDisplay(name=None).name is not None posargs_args = [{ 'name': None }, { 'provides': 'x' }, { 'display': { 'doc': 'wee' } }, { 'display': { 'name': 'better_name' } }] for arg in posargs_args: cmd = Command(lambda targs: None, name='cmd', posargs=arg) cmd_chk = CommandChecker(cmd, mix_stderr=True) res = cmd_chk.run(['cmd', '-h']) assert res.stdout.startswith('Usage') return
def test_flag_hidden(): # TODO: is display='' sufficient for hiding (do we need hidden=True) cmd = Command(lambda tiger, dragon: None, 'cmd') cmd.add('--tiger', display='') flags = cmd.get_flags(with_hidden=False) assert 'tiger' not in [f.name for f in flags] cmd.add('--dragon', display={'label': ''}) flags = cmd.get_flags(with_hidden=False) assert 'dragon' not in [f.name for f in flags]
def test_flag_init(): cmd = Command(lambda flag, part: None, name='cmd') with pytest.raises(ValueError, match='cannot make an argument-less flag required'): cmd.add('--flag', missing=ERROR, parse_as=True) # test custom callable multi cmd.add(Flag('--part', multi=lambda flag, vals: ''.join(vals))) res = cmd.parse(['cmd', '--part', 'a', '--part', 'b']) assert res.flags['part'] == 'ab' with pytest.raises(ValueError, match='multi expected callable, bool, or one of.*'): cmd.add('--badflag', multi='nope')
def get_vcs_cmd(as_parser=False): cmd = Command(None, 'vcs') cmd.add(_add_cmd, name='add', posargs={'min_count': 1}, post_posargs=True) cmd.add(_checkout_cmd, name='checkout', posargs={'max_count': 1}) if as_parser: cmd.__class__ = Parser return cmd
def test_char_missing_error(): # testing required flags cmd = Command(lambda req_flag: None, name='cmd') cmd.add('--req-flag', char='-R', missing=ERROR) res = cmd.parse(['cmd', '--req-flag', 'val']) assert res.flags['req_flag'] == 'val' res = cmd.parse(['cmd', '-R', 'val']) assert res.flags['req_flag'] == 'val' with pytest.raises(ArgumentParseError, match='--req-flag'): cmd.parse(['cmd']) return
def test_posargspec_init(): with pytest.raises(TypeError, match='expected callable or ERROR'): PosArgSpec(parse_as=object()) with pytest.raises(TypeError, match='unexpected keyword'): PosArgSpec(badkw='val') with pytest.raises(ValueError, match='expected min_count >= 0'): PosArgSpec(min_count=-1) with pytest.raises(ValueError, match='expected max_count > 0'): PosArgSpec(max_count=-1) with pytest.raises(ValueError, match='expected min_count > max_count'): PosArgSpec(max_count=3, min_count=4) with pytest.raises(TypeError, match='.*PosArgDisplay instance.*'): PosArgSpec(display=object()) with pytest.raises(TypeError, match='unexpected keyword'): PosArgSpec(display={'badkw': 'val'}) # cmd = Command(lambda posargs_: posargs_, posargs=PosArgSpec(display=False)) assert PosArgSpec(display=False).display.hidden == True assert PosArgSpec(display=PosArgDisplay(name='posargs')) cmd = Command(lambda: None, name='cmd', posargs=1) assert cmd.posargs.min_count == 1 assert cmd.posargs.max_count == 1 cmd = Command(lambda targs: None, name='cmd', posargs='targs') assert cmd.posargs.display.name == 'targs' assert cmd.posargs.provides == 'targs' cmd = Command(lambda targs: None, name='cmd', posargs=int) assert cmd.posargs.parse_as == int with pytest.raises(TypeError, match='.*instance of PosArgSpec.*'): Command(lambda targs: None, name='cmd', posargs=object()) return
def test_multi_extend(): cmd = Command(lambda override: None, name='cmd') cmd.add('--override', char='o', multi=True) res = cmd.parse(['cmd', '-o', 'x=y', '-o', 'a=b']) assert res.flags['override'] == ['x=y', 'a=b'] res = cmd.parse(['cmd']) assert res.flags['override'] == [] res = cmd.parse(['cmd', '-o=x']) assert res.flags['override'] == ['x']
def _get_cmd(prepare=False): cmd = Command(name='pocket_protector', func=None, doc=__doc__) # func=None means output help # add flags cmd.add('--file', missing='protected.yaml', doc='path to the PocketProtector-managed file, defaults to protected.yaml in the working directory') cmd.add('--confirm', parse_as=True, doc='show diff and prompt for confirmation before modifying the file') cmd.add('--non-interactive', parse_as=True, doc='disable falling back to interactive authentication, useful for automation') cmd.add('--ignore-env', parse_as=True, display=False, # TODO: keep? doc='ignore environment variables like PPROTECT_PASSPHRASE') cmd.add('--user', char='-u', doc="the acting user's email credential") cmd.add('--passphrase-file', doc='path to a file containing only the passphrase, likely provided by a deployment system') # add middlewares, outermost first ("first added, first called") cmd.add(mw_verify_creds) cmd.add(mw_write_kf) cmd.add(mw_ensure_kf) cmd.add(mw_exit_handler) # add subcommands cmd.add(add_key_custodian, name='init', doc='create a new protected') cmd.add(add_key_custodian) cmd.add(add_domain) cmd.add(rm_domain) cmd.add(add_owner) cmd.add(rm_owner) cmd.add(add_secret) cmd.add(update_secret) cmd.add(rm_secret) cmd.add(set_key_custodian_passphrase) cmd.add(rotate_domain_keys) cmd.add(decrypt_domain, posargs={'count': 1, 'provides': 'domain_name'}) cmd.add(list_domains) cmd.add(list_domain_secrets, posargs={'count': 1, 'provides': 'domain_name'}) cmd.add(list_all_secrets) cmd.add(list_audit_log) cmd.add(print_version, name='version') if prepare: cmd.prepare() # an optional check on all subcommands, not just the one being executed return cmd
def test_bad_subprs(): with pytest.raises( ValueError, match= 'commands accepting positional arguments cannot take subcommands'): posarg_cmd = Command(lambda: None, 'pa', posargs=True) posarg_cmd.add(lambda: None, 'bad_subcmd') cmd = Command(lambda: None, 'base') cmd.add(lambda: None, 'twin') with pytest.raises(ValueError, match='conflicting subcommand name'): cmd.add(lambda: None, 'twin') with pytest.raises(TypeError, match='expected Command instance'): cmd.add_command(object())
def main(): """ The main entrypoint, setting up a face application, with middleware, common flags, and subcommands. """ cmd = Command(name='montage-admin', func=None, doc="CLI tools for administrating Montage.") # middleware cmd.add(_admin_dao_mw) cmd.add(_rdb_session_mw) cmd.add('--username', missing=ERROR) cmd.add('--debug', parse_as=True, doc='get extra output, enable debug console before db commit') cmd.add('--force', parse_as=True, doc='skip some confirmations, use with caution') cmd.add('--campaign-id', parse_as=int, missing=ERROR) cmd.add('--round-id', parse_as=int, missing=ERROR) cmd.add('--csv-path', missing=ERROR) cmd.add('--url', missing=ERROR) cmd.add( add_organizer ) # , posargs={'count': 1, 'name': 'username'}) # TODO: figure out if we want posarg/flag overriding ser_cmd = Command(name='series', func=None, doc='tools for administrating Montage series') ser_cmd.add(add_series, name='add') cmd.add(ser_cmd) cmp_cmd = Command(name='campaign', func=None, doc='tools for administrating Montage campaigns') cmp_cmd.add(list_campaigns, name='list') cmp_cmd.add(create_campaign, name='create') cmp_cmd.add(add_coordinator, name='add-coordinator') cmp_cmd.add(cancel_campaign, name='cancel') cmp_cmd.add(backfill_series) cmd.add(cmp_cmd) rnd_cmd = Command(name='round', func=None, doc='tools for administrating Montage rounds') rnd_cmd.add(create_round, name='create') rnd_cmd.add(import_gist, name='import-gist') rnd_cmd.add(activate_round, name='activate') rnd_cmd.add(pause_round, name='pause') rnd_cmd.add(advance_round, name='advance') rnd_cmd.add(edit_round_quorum, name='edit-quorum') rnd_cmd.add(check_round_dupes, name='check-dupes') rnd_cmd.add(apply_round_ratings, name='apply-ratings') rnd_cmd.add(retask_duplicate_ratings, name='retask-dupes') rnd_cmd.add(shuffle_round_assignments, name='shuffle-tasks') rnd_cmd.add(cancel_round, name='cancel') cmd.add(rnd_cmd) cmd.add(rdb_console) cmd.prepare() return cmd.run()
def test_command_misc_api(): with pytest.raises(TypeError, match='unexpected keyword'): Command(lambda: None, name='ok', bad_kwarg=True)
def get_search_command(as_parser=False): """A command which provides various subcommands mimicking popular command-line text search tools to test power, compatiblity, and flexibility. """ cmd = Command(None, 'search') cmd.add('--verbose', char='-V', parse_as=True) rg_subcmd = Command(_rg, 'rg') rg_subcmd.add('--glob', char='-g', multi=True, parse_as=str, doc='Include or exclude files/directories for searching' ' that match the given glob. Precede with ! to exclude.') rg_subcmd.add('--max-count', char='-m', parse_as=int, doc='Limit the number of matching lines per file.') rg_subcmd.add('--filetype', ChoicesParam(['py', 'js', 'html'])) rg_subcmd.add('--extensions', ListParam(strip=True)) cmd.add(rg_subcmd) cmd.add(_timestamp_mw) if as_parser: cmd.__class__ = Parser return cmd
def main(argv): posargs = PosArgSpec(str, max_count=2, display={'label': '[spec [target]]'}) cmd = Command(glom_cli, posargs=posargs, middlewares=[mw_get_target]) cmd.add('--target-file', str, missing=None, doc='path to target data source') cmd.add('--target-format', str, missing='json', doc='format of the source data (json or python)') cmd.add('--spec-file', str, missing=None, doc='path to glom spec definition') cmd.add('--spec-format', str, missing='python', doc='format of the glom spec definition (json, python, python-full)') cmd.add('--indent', int, missing=2, doc='number of spaces to indent the result, 0 to disable pretty-printing') cmd.add('--debug', parse_as=True, doc='interactively debug any errors that come up') cmd.add('--inspect', parse_as=True, doc='interactively explore the data') return cmd.run(argv) or 0
def main(argv=None): """\ automation and analytics for curated lists of awesome software. Normal analysis workflow: * apatite pull-repos (can take 3-4 hours, 25GB on the full APA, use --targets to limit) * apatite collect-metrics * apatite export-metrics * apatite analyze # TODO """ cmd = Command(name='apatite', func=None, doc=main.__doc__) # func=None means output help # add flags cmd.add('--file', missing='projects.yaml', doc='path to the project listing YAML file') cmd.add( '--confirm', parse_as=True, doc='show diff and prompt for confirmation before modifying the file') cmd.add( '--non-interactive', parse_as=True, doc= 'disable falling back to interactive authentication, useful for automation' ) cmd.add('--targets', parse_as=ListParam(str), missing=[], doc='specific target projects') cmd.add('--metrics', parse_as=ListParam(str), missing=[], doc='specific metrics to collect') cmd.add('--dry-run', parse_as=True, doc='do not save results') two_weeks_ago = _date_param('-2w') cmd.add( '--earliest', parse_as=_date_param, missing=two_weeks_ago, doc=( 'minimum datetime value to accept (isodate or negative timedelta).' ' defaults to two weeks ago (-2w)')) cmd.add('--no-progress', parse_as=True) # add middlewares, outermost first ("first added, first called") cmd.add(mw_exit_handler) cmd.add(mw_ensure_project_listing) cmd.add(mw_ensure_work_dir) # add subcommands cmd.add(render) cmd.add(normalize) cmd.add(pull_repos) cmd.add(collect_metrics) cmd.add(show_recent_metrics) cmd.add(export_metrics) cmd.add(show_exportable_metrics) cmd.add(set_repo_added_dates) cmd.add(console) cmd.add(print_version, name='version') cmd.prepare( ) # an optional check on all subcommands, not just the one being executed try: cmd.run(argv=argv) # exit behavior is handled by mw_exit_handler except Exception: if os.getenv('APATITE_DEBUG'): import pdb pdb.post_mortem() raise return
def main(): cmd = Command(cut_mp4) cmd.add('--input', missing=ERROR, doc='path to the input mp4 file') cmd.add('--output', missing=ERROR, doc='path to write the output mp4 file') cmd.add('--start', doc='starting timestamp in hh:mm:ss format') cmd.add('--end', doc='ending timestamp in hh:mm:ss format') cmd.add('--no-align-keyframes', parse_as=True, doc="don't align to the nearest keyframe, potentially" " creating an unclean cut with video artifacts") cmd.run()
def get_calc_cmd(as_parser=False): cmd = Command(None, 'calc') cmd.add(_add_cmd, name='add', posargs={'min_count': 2, 'parse_as': float}) cmd.add(_add_two_ints, name='add_two_ints', posargs={'count': 2, 'parse_as': int, 'provides': 'ints'}) cmd.add(_is_odd, name='is_odd', posargs={'count': 1, 'parse_as': int, 'provides': 'target_int'}) cmd.add(_ask_halve, name='halve', posargs=False) cmd.add(_ask_blackjack, name='blackjack') if as_parser: cmd.__class__ = Parser return cmd
def main(argv): posargs = PosArgSpec(str, max_count=2, display={'label': '[spec [target]]'}) cmd = Command(glom_cli, posargs=posargs, middlewares=[mw_get_target]) cmd.add('--target-file', str, missing=None, doc='path to target data source') cmd.add('--target-format', str, missing='json', doc='format of the source data (json or python)') cmd.add('--spec-file', str, missing=None, doc='path to glom spec definition') cmd.add( '--spec-format', str, missing='python', doc='format of the glom spec definition (json, python, python-full)') cmd.add( '--indent', int, missing=2, doc= 'number of spaces to indent the result, 0 to disable pretty-printing') cmd.add('--debug', parse_as=True, doc='interactively debug any errors that come up') cmd.add('--inspect', parse_as=True, doc='interactively explore the data') return cmd.run(argv) or 0
def main(): cmd = Command(cut_mp4) cmd.add('--input', missing=ERROR, doc='path to the input mp4 file') cmd.add('--output', missing=ERROR, doc='path to write the output mp4 file') cmd.add('--start', doc='starting timestamp in hh:mm:ss format') cmd.add('--end', doc='ending timestamp in hh:mm:ss format') cmd.add('--filter-audio', parse_as=True, doc='do high-pass/low-pass noise filtration of audio.' ' good for noisy meetup recordings.') cmd.add('--no-align-keyframes', parse_as=True, doc="don't align to the nearest keyframe, potentially" " creating an unclean cut with video artifacts") cmd.run()
def main(): cmd = Command(name='chert', func=None) cmd.add(init, posargs={'min_count': 1, 'max_count': 1, 'display': 'target_dir', 'provides': 'target_dir'}) cmd.add(serve) cmd.add(render) cmd.add(publish) cmd.add(clean) cmd.add(version) # cmd.add('--target-dir', doc='path to generate new chert site') cmd.add(_cur_input_path_mw) cmd.run()
def main(argv=None): cmd = Command(name='pacetrack', func=None) # subcommands update_subcmd = Command(update, posargs={'min_count': 1, 'display': 'campaign_id', 'provides': 'campaign_ids'}) # update_subcmd.add('campaign_name') cmd.add(update_subcmd) cmd.add(update_all) cmd.add(render_all) cmd.add(list_campaigns) cmd.add(print_version, name='version') # cmd.add(prune) # mostly for testing cmd.add('--jsub', parse_as=True, doc='run commands through the WMF Labs job grid (for production use only)') cmd.add('--force', parse_as=True, doc='ignore configured fetch frequency and force updates') cmd.add('--dry-run', parse_as=True, doc='log actions without performing them (e.g., do not remove files)') # flags cmd.add('--debug', doc='increase logging level', parse_as=True, missing=DEBUG) # middlewares cmd.add(mw_cli_log) try: cmd.run() except Exception: # TODO: once face is stable, this can become part of the middleware if os.getenv('PACETRACK_ENABLE_DEBUG'): import pdb;pdb.post_mortem() raise
def main(): cmd = Command(name='chert', func=None) cmd.add(init, posargs={'count': 1, 'name': 'target_dir'}) cmd.add(serve) cmd.add(render) cmd.add(publish) cmd.add(clean) cmd.add(version) # cmd.add('--target-dir', doc='path to generate new chert site') cmd.add(_cur_input_path_mw) cmd.run()
def main(): cmd = Command(busy_loop, 'cmd', middlewares=[output_streams_mw]) sum_subcmd = Command(sum_func, 'sum') sum_subcmd.add( '--num', parse_as=ListParam(int), missing=(0, ), doc='a number to include in the sum, expects integers at the moment' ' because it is fun to change things later') sum_subcmd.add( '--grummmmmmmmmmmmmmmmmmm', parse_as=int, multi=True, missing=0, doc='a bizarre creature, shrek-like, does nothing, but is here to' ' make the help longer and less helpful but still good for wraps.') cmd.add(sum_subcmd) cmd.add(verbose_mw) cmd.add(subtract, doc='', posargs=float) cmd.add(print_args, 'print', '', posargs=True) cmd.add('--loop-count', parse_as=int) return cmd.run() # execute