def __init__(self, name='xargs', topic='shell', brief='build and execute command lines from stdin', **kwargs): self.parser = PypsiArgParser(prog=name, description=brief, usage=XArgsUsage.format(name=name)) self.parser.add_argument('-I', default='{}', action='store', metavar='REPSTR', help='string token to replace', dest='token') self.parser.add_argument('command', nargs=argparse.REMAINDER, help="command to execute", metavar='COMMAND') super(XArgsCommand, self).__init__(name=name, topic=topic, usage=self.parser.format_help(), brief=brief, **kwargs)
def __init__(self, name='var', brief='manage local variables', topic='shell', **kwargs): self.parser = PypsiArgParser(prog=name, description=brief, usage=VariableCommand.Usage) self.parser.add_argument('-l', '--list', help='list variables', action='store_true') self.parser.add_argument('-d', '--delete', help='delete variable', metavar='VARIABLE') self.parser.add_argument( 'exp', metavar='EXPRESSION', help='expression defining variable, in the form of NAME = [VALUE]', nargs=argparse.REMAINDER) super(VariableCommand, self).__init__(name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs)
def __init__(self, name='alias', brief='manage command aliases', topic='shell', **kwargs): super(AliasCommand, self).__init__(name=name, brief=brief, topic=topic, **kwargs) self.parser = PypsiArgParser(prog=name, description=brief) self.parser.add_argument('-l', '--list', action='store_true', help='list registered aliases') self.parser.add_argument('exp', metavar='EXP', help='alias expression', nargs=argparse.REMAINDER) self.parser.add_argument('-d', '--delete', help='delete alias', nargs=1, metavar='ALIAS')
def __init__(self): super().__init__(name='vote', brief='manage votes') self.parser = PypsiArgParser() subcmd = self.parser.add_subparsers(help='subcmd', dest='subcmd', required=True) list_cmd = subcmd.add_parser('list', help='list all stocks in an exchange') list_cmd.add_argument('-e', '--exchange-id', action='store', help='exchange id') create_cmd = subcmd.add_parser('new', help='create new stock') create_cmd.add_argument('name', action='store', help='stock name') create_cmd.add_argument('-e', '--exchange-id', action='store', help='exchange id', required=True) create_cmd.add_argument('-v', '--value', action='store', type=int, default=1000, help='initial stock value')
def __init__(self, name, topic, brief, **kwargs): self.parser = PypsiArgParser(prog=name, description=brief) super(SaveLoadCommand, self).__init__(name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs)
def __init__(self, name, topic, brief, **kwargs): self.parser = PypsiArgParser(prog=name, description=brief) # TODO: start using args from ArgParser, now they only added to show pretty help self.parser = PypsiArgParser(prog=name, description=brief) subparsers = self.parser.add_subparsers(dest='subparser_name', help="Commands") add_arg_parser = subparsers.add_parser("add", help="add connection") edit_arg_parser = subparsers.add_parser("edit", help="edit connection") edit_arg_parser.add_argument("index", metavar="INDEX", action="store", help="connection index to edit") delete_arg_parser = subparsers.add_parser("delete", help="delete connection") delete_arg_parser.add_argument("index", metavar="INDEX", action="store", help="connection index to delete") super(InputOutputCommand, self).__init__(name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs) self._handlers = { self.SUBCOMMAND_ADD: self._add_handler, self.SUBCOMMAND_DELETE: self._delete_handler, self.SUBCOMMAND_EDIT: self._edit_handler } self._print_header = True
def __init__(self, name='tail', topic='shell', brief='display the last lines of a file', **kwargs): self.parser = PypsiArgParser(prog=name, description=brief, usage=TailCmdUsage) # Add a callback function that will be called when the # argument is tab-completed self.parser.add_argument('input_file', help='file to display', metavar="FILE", completer=self.complete_path) self.parser.add_argument('-n', '--lines', metavar="N", type=int, default=10, help="number of lines to display") self.parser.add_argument('-f', '--follow', help="continue to output as file grows", action='store_true') super(TailCommand, self).__init__(name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs)
def __init__(self, name='tip', brief='print shell tips', topic='shell', tips=None, motd=None, vars=None, **kwargs): self.tips = tips or [] self.motd = motd or '' self.vars = vars or {} self.rand = random.Random() self.rand.seed() self.parser = PypsiArgParser(prog=name, description=brief) self.parser.add_argument('-m', '--motd', action='store_true', help="print the message of the day") super(TipCommand, self).__init__(name=name, brief=brief, topic=topic, usage=self.parser.format_help(), **kwargs)
def __init__(self, topic='proxy', name='minion', brief=HelpTopic, **kwargs): self.parser = PypsiArgParser(prog=name, description=brief, usage=MinionCmdUsage) subcmd = self.parser.add_subparsers(prog='minion', dest='subcmd', metavar='subcmd') subcmd.required = True shutdown = subcmd.add_parser('shutdown', help='shutdown the connected minion') restart = subcmd.add_parser('restart', help='restart the connected minion') load = subcmd.add_parser('load', help='loads a plugin into minion') load.add_argument('plugin', metavar='PLUGIN', help='name of plugin to load into minion') log = subcmd.add_parser('log', help='print the log of a minion') log.add_argument('lines', metavar='LINES', help='range of line number to print <start:finish>') super(MinionCommand, self).__init__(name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs)
class CompCommand(Command): def __init__(self, name, topic, brief, **kwargs): self.parser = PypsiArgParser(prog=name, description=brief) super(CompCommand, self).__init__(name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs) def run(self, shell, args): try: self.parser.parse_args(args) except CommandShortCircuit as e: return e.code component_info = shell._component_info # read print_header from args ns = MainSectionWizard(component_info).run(shell, print_header=True) if ns: out_dict = Helper.filter_non_zero_string_items(ns.__dict__) component_info.load_from_json(out_dict) else: return EXIT_CODE return 0
def __init__(self, name, topic, brief, **kwargs): # TODO: start using args from ArgParser, now they only added to show pretty help self.parser = PypsiArgParser(prog=name, description=brief) subparsers = self.parser.add_subparsers(dest='subparser_name', help="Commands") add_arg_parser = subparsers.add_parser("add", help="add argument") edit_arg_parser = subparsers.add_parser("edit", help="edit argument") edit_arg_parser.add_argument("key", metavar="KEY", action="store", help="argument key to edit") delete_arg_parser = subparsers.add_parser("delete", help="delete argument") delete_arg_parser.add_argument("key", metavar="KEY", action="store", help="argument key to delete") super(ArgumentCommand, self).__init__(name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs) self._handlers = { self.SUBCOMMAND_ADD: self._add_handler, self.SUBCOMMAND_DELETE: self._delete_handler, self.SUBCOMMAND_EDIT: self._edit_handler }
class ExitCommand(Command): ''' Exit the active shell. ''' def __init__(self, name='exit', topic='shell', brief='exit the shell', **kwargs): self.parser = PypsiArgParser(prog=name, description=brief) self.parser.add_argument('code', metavar='CODE', action='store', default=0, type=int, nargs='?', help='exit code to return to parent process') super(ExitCommand, self).__init__(name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs) def run(self, shell, args): try: ns = self.parser.parse_args(args) except CommandShortCircuit as e: return e.code raise SystemExit(ns.code)
def __init__(self, name='help', topic='shell', brief='print information on a topic or command', topics=None, vars=None, **kwargs): self.parser = PypsiArgParser(prog=name, description=brief) # Add a callback to self.complete_topics so tab-complete can # return the possible topics you may get help on self.parser.add_argument("topic", metavar="TOPIC", help="command or topic to print", nargs='?', completer=self.complete_topics) super(HelpCommand, self).__init__(name=name, brief=brief, usage=self.parser.format_help(), topic=topic, **kwargs) self.vars = vars or {} self.topics = topics
def __init__(self, name='cd', brief='change current working directory', topic='shell', **kwargs): super(ChdirCommand, self).__init__(name=name, brief=brief, topic=topic, **kwargs) self.parser = PypsiArgParser(prog=name, description=brief) self.parser.add_argument('path', help='path', metavar="PATH")
class ExitCommand(Command): ''' Exit the active shell. ''' def __init__(self, name='exit', topic='shell', brief='exit the shell', **kwargs): self.parser = PypsiArgParser( prog=name, description=brief ) self.parser.add_argument( 'code', metavar='CODE', action='store', default=0, type=int, nargs='?', help='exit code to return to parent process' ) super(ExitCommand, self).__init__( name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs ) def run(self, shell, args): try: ns = self.parser.parse_args(args) except CommandShortCircuit as e: return e.code raise SystemExit(ns.code)
def __init__(self, topic='proxy', brief=HelpTopic, name='list', **kwargs): self.parser = PypsiArgParser(prog=name, description=brief, usage=ListCmdUsage) super(ListCommand, self).__init__(name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs)
class VoteLabelCommand(Command): def __init__(self): super().__init__(name='vlabel', brief='manage vote labels') self.parser = PypsiArgParser() self.subcmd = self.parser.add_subparsers(help='subcmd', dest='subcmd', required=True) self.subcmd.add_parser('list', help='list all vote labels') create_cmd = self.subcmd.add_parser('new', help='create new vote label') create_cmd.add_argument('name', action='store', help='label name') def complete(self, shell, args, prefix): if len(args) == 1 and args[0].startswith('-'): completions = command_completer(self.parser, shell, args, prefix) else: completions = command_completer(self.subcmd, shell, args, prefix) return completions def run(self, shell, args): try: args = self.parser.parse_args(args) except CommandShortCircuit as err: return err.code if args.subcmd == 'list': return self.print_vote_labels(shell) if args.subcmd == 'new': return self.create_vote_label(shell, args.name) self.error(shell, f'unknown sub-command: {args.subcmd}') def print_vote_labels(self, shell): cursor = shell.ctx.db.vote_labels.find() table = Table( [Column('Id'), Column('Name'), Column('Symbol')], spacing=4) for row in cursor: table.append(row['_id'], row['name'], row['symbol']) table.write(sys.stdout) return 0 def create_vote_label(self, shell, name: str): vlabel = shell.ctx.db.create_vote_label(name) print('created new user successfully') print() print('Id: ', vlabel['_id']) print('Name: ', vlabel['name']) print('Symbol:', vlabel['symbol']) return 0
class ChdirCommand(Command): ''' Change the current working directory. This accepts one of the following: * <path> - a relative or absolute path * ~ - the current user's home directory * ~<user> - <user>'s home directory * - - the previous directory ''' def __init__(self, name='cd', brief='change current working directory', topic='shell', **kwargs): super(ChdirCommand, self).__init__(name=name, brief=brief, topic=topic, **kwargs) self.parser = PypsiArgParser(prog=name, description=brief) self.parser.add_argument('path', help='path', metavar="PATH") def setup(self, shell): shell.ctx.chdir_last_dir = os.getcwd() def chdir(self, shell, path, print_cwd=False): prev = os.getcwd() try: os.chdir(path) if print_cwd: print(os.getcwd()) except OSError as e: self.error(shell, path, ": ", e.strerror) return -1 except Exception as e: self.error(shell, path, ": ", str(e)) return -1 shell.ctx.chdir_last_dir = prev return 0 def run(self, shell, args): try: ns = self.parser.parse_args(args) except CommandShortCircuit as e: return e.code if ns.path == '-': return self.chdir(shell, shell.ctx.chdir_last_dir, True) if ns.path.startswith('~'): return self.chdir(shell, os.path.expanduser(ns.path)) return self.chdir(shell, ns.path)
def __init__(self): super().__init__(name='vlabel', brief='manage vote labels') self.parser = PypsiArgParser() self.subcmd = self.parser.add_subparsers(help='subcmd', dest='subcmd', required=True) self.subcmd.add_parser('list', help='list all vote labels') create_cmd = self.subcmd.add_parser('new', help='create new vote label') create_cmd.add_argument('name', action='store', help='label name')
class SaveLoadCommand(Command): def __init__(self, name, topic, brief, **kwargs): self.parser = PypsiArgParser(prog=name, description=brief) super(SaveLoadCommand, self).__init__(name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs) def run(self, shell, args): try: self.parser.parse_args(args) except CommandShortCircuit as e: return e.code if self.name == CommandName.SAVE: w = SaveLoadWizard("Save Component", shell._filename) ns = w.run(shell, True) if ns: if ns.path is not None: component_info = shell._component_info with io.open(ns.path, mode='w', encoding="utf-8") as f: json.dump(component_info, f, indent=4, cls=ComponentInfoEncoder) print("Component was successfully saved: {}".format( ns.path)) shell._filename = ns.path else: print("Abort saving component") elif self.name == CommandName.LOAD: w = SaveLoadWizard("Load Component", None) ns = w.run(shell, True) if ns: with io.open(ns.path, encoding="utf-8") as f: try: json_dict = json.load(f) shell._component_info.load_from_json(json_dict) except Exception as e: print( "Can not load file {}. Not a json file?. Error: {}" .format(ns.path, str(e))) return 1 shell._filename = ns.path print("Component was successfully loaded: {}".format(ns.path)) else: pass return 0
def __init__(self, topic='proxy', name='disconnect', brief=HelpTopic, **kwargs): self.parser = PypsiArgParser( prog=name, description=brief, usage=DisconnectCmdUsage ) self.parser.add_argument( 'name', help='name of minion', metavar="NAME", completer=self.complete_minions ) super(DisconnectCommand, self).__init__( name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs )
def setup_parser(self, brief): self.parser = PypsiArgParser( prog='history', description=brief, usage=CmdUsage ) subcmd = self.parser.add_subparsers(prog='history', dest='subcmd', metavar='subcmd') subcmd.required = True ls = subcmd.add_parser('list', help='list history events') ls.add_argument( 'count', metavar='N', type=int, help='number of events to display', nargs='?' ) subcmd.add_parser('clear', help='remove all history events') delete = subcmd.add_parser( 'delete', help='delete single history event' ) delete.add_argument( 'index', metavar='N', type=int, help='remove item at index N', ) save = subcmd.add_parser('save', help='save history to a file') save.add_argument( 'path', metavar='PATH', help='save history to file located at PATH' ) load = subcmd.add_parser('load', help='load history from a file') load.add_argument( 'path', metavar='PATH', help='load history from file located at PATH' )
def __init__(self, name='tail', topic='shell', brief='display the last lines of a file', **kwargs): self.parser = PypsiArgParser( prog=name, description=brief, usage=TailCmdUsage ) self.parser.add_argument( 'input_file', help='file to display', metavar="FILE" ) self.parser.add_argument( '-n', '--lines', metavar="N", type=int, default=10, help="number of lines to display" ) self.parser.add_argument( '-f', '--follow', help="continue to output as file grows", action='store_true' ) super(TailCommand, self).__init__( name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs )
def __init__(self, name='macro', topic='shell', brief="manage registered macros", macros=None, **kwargs): self.parser = PypsiArgParser( prog=name, description=brief, usage=MacroCmdUsage ) self.parser.add_argument( '-l', '--list', help='list all macros', action='store_true' ) self.parser.add_argument( '-d', '--delete', help='delete macro', metavar='NAME', completer=self.complete_macros ) self.parser.add_argument( '-s', '--show', help='print macro body', metavar='NAME', completer=self.complete_macros ) self.parser.add_argument( 'name', help='macro name', nargs='?', metavar='NAME' ) super(MacroCommand, self).__init__( name=name, usage=self.parser.format_help(), brief=brief, topic=topic, **kwargs ) self.base_macros = macros or {} self.macro_name = None
def __init__(self, name='include', topic='shell', brief='execute a script file', **kwargs): self.parser = PypsiArgParser( prog=name, description=brief ) self.parser.add_argument( 'path', metavar='PATH', action='store', help='file to execute' ) super(IncludeCommand, self).__init__( name=name, topic=topic, brief=brief, usage=self.parser.format_help(), **kwargs ) self.stack = []
class ShellCommand(Command): def __init__(self, topic='proxy', name='shell', brief=HelpTopic, **kwargs): self.parser = PypsiArgParser( prog=name, description=brief, usage=ShellCmdUsage ) super(ShellCommand, self).__init__( name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs ) def run(self, shell, args): if not self.check_connected(shell): self.error("shell", "Not currently connected to minion") return 0 print(f'Starting interactive shell with: {shell.connect}') minionshell = MinionShell(shell.connect, shell) minionshell.cmdloop() print(f'Closed interactive shell with: {shell.connect}') def check_connected(self, shell): minion = shell.connect if minion is not None: return True
def __init__(self): super().__init__(name='exchange', brief='manage exchanges') self.parser = PypsiArgParser() self.subcmd = self.parser.add_subparsers(help='subcmd', dest='subcmd') self.subcmd.add_parser('list', help='list all exchanges') create_cmd = self.subcmd.add_parser('new', help='create new exchange') create_cmd.add_argument('-s', '--select', action='store_true', help='select the exchange after creating it') create_cmd.add_argument('name', action='store', help='exchange name') select_cmd = self.subcmd.add_parser('select', help='select an active exhcnage') select_cmd.add_argument('id', help='exchange id') deselect_cmd = self.subcmd.add_parser('deselect', help='deselect active exchange')
class AliasCommand(Command): def __init__(self, name='alias', brief='manage command aliases', topic='shell', **kwargs): super(AliasCommand, self).__init__(name=name, brief=brief, topic=topic, **kwargs) self.parser = PypsiArgParser(prog=name, description=brief) self.parser.add_argument('-l', '--list', action='store_true', help='list registered aliases') self.parser.add_argument('exp', metavar='EXP', help='alias expression', nargs=argparse.REMAINDER) self.parser.add_argument('-d', '--delete', help='delete alias', nargs=1, metavar='ALIAS') def run(self, shell, args): try: ns = self.parser.parse_args(args) except CommandShortCircuit as e: return e.code rc = None if ns.list: for name in shell.ctx.aliases: print(name, "=", shell.ctx.aliases[name]) rc = 0 elif ns.delete: for name in ns.delete: if name in shell.ctx.aliases: del shell.ctx.aliases[name] rc = 0 else: self.error(shell, "alias does not exist: ", name) rc = 1 break else: (remainder, exp) = Expression.parse(args) if remainder or not exp or exp.operator != '=': self.error(shell, "invalid expression") rc = 1 else: shell.ctx.aliases[exp.operand] = exp.value rc = 0 return rc
class ShowCommand(Command): def __init__(self, name, topic, brief, **kwargs): self.parser = PypsiArgParser(prog=name, description=brief) super(ShowCommand, self).__init__(name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs) def run(self, shell, args): try: self.parser.parse_args(args) except CommandShortCircuit as e: return e.code component_info = shell._component_info print(json.dumps(component_info, indent=4, cls=ComponentInfoEncoder)) return 0
def __init__(self, name='help', topic='shell', brief='print information on a topic or command', topics=None, vars=None, **kwargs): self.parser = PypsiArgParser( prog=name, description=brief ) self.parser.add_argument( "topic", metavar="TOPIC", help="command or topic to print", nargs='?' ) super(HelpCommand, self).__init__( name=name, brief=brief, usage=self.parser.format_help(), topic=topic, **kwargs ) self.vars = vars or {} self.topics = topics
def __init__(self, name='exit', topic='shell', brief='exit the shell', **kwargs): self.parser = PypsiArgParser(prog=name, description=brief) self.parser.add_argument('code', metavar='CODE', action='store', default=0, type=int, nargs='?', help='exit code to return to parent process') super(ExitCommand, self).__init__(name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs)
class DisconnectCommand(Command): def __init__(self, topic='proxy', name='disconnect', brief=HelpTopic, **kwargs): self.parser = PypsiArgParser( prog=name, description=brief, usage=DisconnectCmdUsage ) self.parser.add_argument( 'name', help='name of minion', metavar="NAME", completer=self.complete_minions ) super(DisconnectCommand, self).__init__( name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs ) def run(self, shell, args): try: ns = self.parser.parse_args(args) except CommandShortCircuit as e: return e.code if not ns.name: pass elif ns.name == shell.connect: shell.connect = None shell.remove_prompt_connect() print(f"Successfully disconnected from minion: {ns.name}") else: self.error(shell,f"master is not currently connect to {ns.name}") def complete(self, shell, args, prefix): if len(args) == 1: return self.complete_minions(shell, args, prefix) def complete_minions(self, shell, args, prefix): return sorted( [name for name in shell.minions if name.startswith(prefix)] )
def __init__(self, topic='proxy', name='log', brief=HelpTopic, **kwargs): self.parser = PypsiArgParser(prog=name, description=brief, usage=LogCmdUsage) self.parser.add_argument('-a', '--all', help="prints out the entire log file", action='store_true') self.parser.add_argument( '-l', '--lines', help='prints out a range of lines <start_line>:<end_line>', metavar="LINES") super(LogCommand, self).__init__(name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs)
def __init__(self): super().__init__(name='stock', brief='manage stocks') self.parser = PypsiArgParser() self.subcmd = self.parser.add_subparsers(help='subcmd', dest='subcmd', required=True) list_cmd = self.subcmd.add_parser( 'list', help='list all stocks in an exchange') list_cmd.add_argument('-e', '--exchange-id', action='store', help='exchange id') create_cmd = self.subcmd.add_parser('new', help='create new stock') create_cmd.add_argument('name', action='store', help='stock name') create_cmd.add_argument('-s', '--symbol', action='store', help='stock symbol') create_cmd.add_argument('-e', '--exchange-id', action='store', help='exchange id')
def __init__(self, name='exit', topic='shell', brief='exit the shell', **kwargs): self.parser = PypsiArgParser( prog=name, description=brief ) self.parser.add_argument( 'code', metavar='CODE', action='store', default=0, type=int, nargs='?', help='exit code to return to parent process' ) super(ExitCommand, self).__init__( name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs )
def setup_parser(self, brief): self.parser = PypsiArgParser(prog="history", description=brief, usage=CmdUsage) subcmd = self.parser.add_subparsers(prog="history", dest="subcmd", metavar="subcmd") subcmd.required = True ls = subcmd.add_parser("list", help="list history events") ls.add_argument("count", metavar="N", type=int, help="number of events to display", nargs="?") subcmd.add_parser("clear", help="remove all history events") delete = subcmd.add_parser("delete", help="delete single history event") delete.add_argument("index", metavar="N", type=int, help="remove item at index N") save = subcmd.add_parser("save", help="save history to a file") save.add_argument("path", metavar="PATH", help="save history to file located at PATH") load = subcmd.add_parser("load", help="load history from a file") load.add_argument("path", metavar="PATH", help="load history from file located at PATH")
class XArgsCommand(Command): ''' Execute a command for each line of input from :data:`sys.stdin`. ''' def __init__(self, name='xargs', topic='shell', brief='build and execute command lines from stdin', **kwargs): self.parser = PypsiArgParser( prog=name, description=brief, usage=XArgsUsage.format(name=name) ) self.parser.add_argument( '-I', default='{}', action='store', metavar='REPSTR', help='string token to replace', dest='token' ) self.parser.add_argument( 'command', nargs=argparse.REMAINDER, help="command to execute", metavar='COMMAND' ) super(XArgsCommand, self).__init__( name=name, topic=topic, usage=self.parser.format_help(), brief=brief, **kwargs ) def run(self, shell, args): try: ns = self.parser.parse_args(args) except CommandShortCircuit as e: return e.code if not ns.command: self.error(shell, "missing command") return 1 base = ' '.join([ '"{}"'.format(c.replace('"', '\\"')) for c in ns.command ]) for line in sys.stdin: cmd = base.replace(ns.token, line.strip()) shell.execute(cmd) return 0
def __init__(self, name='echo', topic='shell', brief='print a line of text', **kwargs): self.parser = PypsiArgParser( prog=name, description=brief, usage=EchoCmdUsage ) self.parser.add_argument( 'message', help='message to print', nargs=argparse.REMAINDER, metavar="MESSAGE" ) self.parser.add_argument( '-n', '--nolf', help="don't print newline character", action='store_true' ) super(EchoCommand, self).__init__( name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs )
def __init__(self, name='tip', brief='print shell tips', topic='shell', tips=None, motd=None, vars=None, **kwargs): self.tips = tips or [] self.motd = motd or '' self.vars = vars or {} self.rand = random.Random() self.rand.seed() self.parser = PypsiArgParser( prog=name, description=brief ) self.parser.add_argument( '-m', '--motd', action='store_true', help="print the message of the day" ) super(TipCommand, self).__init__( name=name, brief=brief, topic=topic, usage=self.parser.format_help(), **kwargs )
def __init__(self, name='help', topic='shell', brief='print information on a topic or command', topics=None, vars=None, **kwargs): self.parser = PypsiArgParser( prog=name, description=brief ) # Add a callback to self.complete_topics so tab-complete can # return the possible topics you may get help on self.parser.add_argument( "topic", metavar="TOPIC", help="command or topic to print", nargs='?', completer=self.complete_topics ) super(HelpCommand, self).__init__( name=name, brief=brief, usage=self.parser.format_help(), topic=topic, **kwargs ) self.vars = vars or {} self.topics = topics
def __init__(self, name='xargs', topic='shell', brief='build and execute command lines from stdin', **kwargs): self.parser = PypsiArgParser( prog=name, description=brief, usage=XArgsUsage.format(name=name) ) self.parser.add_argument( '-I', default='{}', action='store', metavar='REPSTR', help='string token to replace', dest='token' ) self.parser.add_argument( 'command', nargs=argparse.REMAINDER, help="command to execute", metavar='COMMAND' ) super(XArgsCommand, self).__init__( name=name, topic=topic, usage=self.parser.format_help(), brief=brief, **kwargs )
def __init__(self, name='var', brief='manage local variables', topic='shell', **kwargs): self.parser = PypsiArgParser( prog=name, description=brief, usage=VariableCommand.Usage ) self.parser.add_argument( '-l', '--list', help='list variables', action='store_true' ) self.parser.add_argument( '-d', '--delete', help='delete variable', metavar='VARIABLE' ) self.parser.add_argument( 'exp', metavar='EXPRESSION', help='expression defining variable, in the form of NAME = [VALUE]', nargs=argparse.REMAINDER ) super(VariableCommand, self).__init__( name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs )
class EchoCommand(Command): ''' Prints text to the screen. ''' def __init__(self, name='echo', topic='shell', brief='print a line of text', **kwargs): self.parser = PypsiArgParser( prog=name, description=brief, usage=EchoCmdUsage ) self.parser.add_argument( 'message', help='message to print', nargs=argparse.REMAINDER, metavar="MESSAGE" ) self.parser.add_argument( '-n', '--nolf', help="don't print newline character", action='store_true' ) super(EchoCommand, self).__init__( name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs ) def run(self, shell, args): try: ns = self.parser.parse_args(args) except CommandShortCircuit as e: return e.code tail = '' if ns.nolf else '\n' print(' '.join(ns.message), sep='', end=tail) return 0
class HistoryCommand(Command): """ Interact with and manage the shell's history. """ def __init__(self, name="history", brief="manage shell history", topic="shell", **kwargs): self.setup_parser(brief) super(HistoryCommand, self).__init__( name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs ) def complete(self, shell, args, prefix): if len(args) == 1: return [x for x in ("clear", "delete", "list", "load", "save") if x.startswith(prefix)] if len(args) == 2: if args[0] == "save" or args[0] == "load": return path_completer(args[-1]) return [] def setup_parser(self, brief): self.parser = PypsiArgParser(prog="history", description=brief, usage=CmdUsage) subcmd = self.parser.add_subparsers(prog="history", dest="subcmd", metavar="subcmd") subcmd.required = True ls = subcmd.add_parser("list", help="list history events") ls.add_argument("count", metavar="N", type=int, help="number of events to display", nargs="?") subcmd.add_parser("clear", help="remove all history events") delete = subcmd.add_parser("delete", help="delete single history event") delete.add_argument("index", metavar="N", type=int, help="remove item at index N") save = subcmd.add_parser("save", help="save history to a file") save.add_argument("path", metavar="PATH", help="save history to file located at PATH") load = subcmd.add_parser("load", help="load history from a file") load.add_argument("path", metavar="PATH", help="load history from file located at PATH") def run(self, shell, args): try: ns = self.parser.parse_args(args) except CommandShortCircuit as e: return e.code rc = 0 if ns.subcmd == "list": start = 0 if ns.count: start = len(shell.ctx.history) - ns.count if start < 0: start = 0 i = start + 1 for event in shell.ctx.history[start:]: print(i, " ", event, sep="") i += 1 elif ns.subcmd == "clear": shell.ctx.history.clear() elif ns.subcmd == "delete": try: del shell.ctx.history[ns.index - 1] except: self.error(shell, "invalid event index\n") rc = -1 elif ns.subcmd == "save": try: with open(ns.path, "w") as fp: for event in shell.ctx.history: fp.write(event) fp.write("\n") except IOError as e: self.error(shell, "error saving history to file: ", os.strerror(e.errno), "\n") rc = -1 elif ns.subcmd == "load": try: lines = [] with safe_open(ns.path, "r") as fp: for event in fp: lines.append(str(event)) shell.ctx.history.clear() for line in lines: shell.ctx.history.append(line.strip()) except IOError as e: self.error(shell, "error saving history to file: ", os.strerror(e.errno), "\n") rc = -1 except UnicodeEncodeError: self.error(shell, "error: file contains invalid unicode characters\n") return rc
class TipCommand(Command): def __init__(self, name='tip', brief='print shell tips', topic='shell', tips=None, motd=None, vars=None, **kwargs): self.tips = tips or [] self.motd = motd or '' self.vars = vars or {} self.rand = random.Random() self.rand.seed() self.parser = PypsiArgParser( prog=name, description=brief ) self.parser.add_argument( '-m', '--motd', action='store_true', help="print the message of the day" ) super(TipCommand, self).__init__( name=name, brief=brief, topic=topic, usage=self.parser.format_help(), **kwargs ) def load_tips(self, path): fp = safe_open(path, 'r') tip = [] for line in fp.readlines(): line = line.rstrip() if line: tip.append(line) elif tip: self.tips.append(' '.join(tip)) tip = [] if tip: self.tips.append(' '.join(tip)) fp.close() def load_motd(self, path): fp = safe_open(path, 'r') self.motd = fp.read().rstrip() fp.close() def run(self, shell, args): try: ns = self.parser.parse_args(args) except CommandShortCircuit as e: return e.code rc = None if ns.motd: rc = self.print_motd(shell) else: rc = self.print_random_tip(shell) return rc def print_random_tip(self, shell, header=True): if not self.tips: self.error(shell, "no tips available") return -1 i = self.rand.randrange(len(self.tips)) if header: title = "Tip #{}\n".format(i + 1) title += '-' * len(title) print(AnsiCodes.green, title, AnsiCodes.reset, sep='') try: cnt = sys.stdout.ansi_format(self.tips[i], **self.vars) except: cnt = self.tips[i] print(cnt) return 0 def print_motd(self, shell): if not self.motd: self.error(shell, "no motd available") return -1 try: cnt = sys.stdout.ansi_format(self.motd, **self.vars) except: cnt = self.motd print( AnsiCodes.green, "Message of the Day".center(shell.width), '\n', AnsiCodes.green, '>' * shell.width, "\n", AnsiCodes.reset, cnt, '\n', AnsiCodes.green, "<" * shell.width, "\n", AnsiCodes.reset, sep='' ) return 0
class TailCommand(Command): ''' Displays the last N lines of a file to the screen. ''' def __init__(self, name='tail', topic='shell', brief='display the last lines of a file', **kwargs): self.parser = PypsiArgParser( prog=name, description=brief, usage=TailCmdUsage ) self.parser.add_argument( 'input_file', help='file to display', metavar="FILE" ) self.parser.add_argument( '-n', '--lines', metavar="N", type=int, default=10, help="number of lines to display" ) self.parser.add_argument( '-f', '--follow', help="continue to output as file grows", action='store_true' ) super(TailCommand, self).__init__( name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs ) def run(self, shell, args): try: ns = self.parser.parse_args(args) except CommandShortCircuit as e: return e.code # check for valid input file if not os.path.isfile(ns.input_file): self.error(shell, "invalid file path: ", ns.input_file, "\n") return -1 # print the last N lines last_lines = self.tail(ns.input_file, ns.lines) for line in last_lines: print(line) print() # continue to follow the file and display new content if ns.follow: self.follow_file(ns.input_file) return 0 def complete(self, shell, args, prefix): return path_completer(args[-1]) def tail(self, fname, lines=10, block_size=1024): data = [] blocks = -1 num_lines = 0 with open(fname) as fp: # seek to the end and get the number of bytes in the file fp.seek(0, 2) num_bytes = fp.tell() bytes_left = num_bytes while num_lines < lines and bytes_left > 0: if bytes_left - block_size > 0: # Seek back a block_size fp.seek(num_bytes - (blocks * block_size)) # read data from file data.insert(0, fp.read(block_size)) else: # jump back to the beginning fp.seek(0, 0) # read data data.insert(0, fp.read(num_bytes)) num_lines = data[0].count('\n') bytes_left -= block_size blocks -= 1 return ''.join(data).splitlines()[-lines:] def follow_file(self, fname): with open(fname) as fp: # jump to the end of the file fp.seek(0, 2) try: while 1: where = fp.tell() line = fp.readline() if not line: time.sleep(1) fp.seek(where) else: print(line, end='', flush=True) except (KeyboardInterrupt): print() return 0 return 0
class HistoryCommand(Command): ''' Interact with and manage the shell's history. ''' def __init__(self, name='history', brief='manage shell history', topic='shell', **kwargs): self.setup_parser(brief) super(HistoryCommand, self).__init__( name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs ) def complete(self, shell, args, prefix): if len(args) == 1: return [x for x in ('clear', 'delete', 'list', 'load', 'save') if x.startswith(prefix)] if len(args) == 2: if args[0] == 'save' or args[0] == 'load': return path_completer(args[-1], prefix=prefix) return [] def setup_parser(self, brief): self.parser = PypsiArgParser( prog='history', description=brief, usage=CmdUsage ) subcmd = self.parser.add_subparsers(prog='history', dest='subcmd', metavar='subcmd') subcmd.required = True ls = subcmd.add_parser('list', help='list history events') ls.add_argument( 'count', metavar='N', type=int, help='number of events to display', nargs='?' ) subcmd.add_parser('clear', help='remove all history events') delete = subcmd.add_parser( 'delete', help='delete single history event' ) delete.add_argument( 'index', metavar='N', type=int, help='remove item at index N', ) save = subcmd.add_parser('save', help='save history to a file') save.add_argument( 'path', metavar='PATH', help='save history to file located at PATH' ) load = subcmd.add_parser('load', help='load history from a file') load.add_argument( 'path', metavar='PATH', help='load history from file located at PATH' ) def run(self, shell, args): try: ns = self.parser.parse_args(args) except CommandShortCircuit as e: return e.code rc = 0 if ns.subcmd == 'list': start = 0 if ns.count and ns.count > 0: start = len(shell.ctx.history) - ns.count if start < 0: start = 0 i = start + 1 for event in shell.ctx.history[start:]: print(i, ' ', event, sep='') i += 1 elif ns.subcmd == 'clear': shell.ctx.history.clear() elif ns.subcmd == 'delete': try: del shell.ctx.history[ns.index - 1] except: self.error(shell, "invalid event index\n") rc = -1 elif ns.subcmd == 'save': try: with open(ns.path, 'w') as fp: for event in shell.ctx.history: fp.write(event) fp.write('\n') except IOError as e: self.error(shell, "error saving history to file: ", os.strerror(e.errno), '\n') rc = -1 elif ns.subcmd == 'load': try: lines = [] with safe_open(ns.path, 'r') as fp: for event in fp: lines.append(str(event)) shell.ctx.history.clear() for line in lines: shell.ctx.history.append(line.strip()) except IOError as e: self.error(shell, "error saving history to file: ", os.strerror(e.errno), '\n') rc = -1 except UnicodeEncodeError: self.error( shell, "error: file contains invalid unicode characters\n" ) return rc
class MacroCommand(BlockCommand): ''' Record and execute command macros. Macros can consist of one or more command statements. Macros are comparable to Bash functions. Once a macro has been recorded, a new command is registered in the shell as an instance of a :class:`Macro`. This command requires the :class:`pypsi.plugins.block.BlockPlugin` plugin. ''' def __init__(self, name='macro', topic='shell', brief="manage registered macros", macros=None, **kwargs): self.parser = PypsiArgParser( prog=name, description=brief, usage=MacroCmdUsage ) self.parser.add_argument( '-l', '--list', help='list all macros', action='store_true' ) self.parser.add_argument( '-d', '--delete', help='delete macro', metavar='NAME', completer=self.complete_macros ) self.parser.add_argument( '-s', '--show', help='print macro body', metavar='NAME', completer=self.complete_macros ) self.parser.add_argument( 'name', help='macro name', nargs='?', metavar='NAME' ) super(MacroCommand, self).__init__( name=name, usage=self.parser.format_help(), brief=brief, topic=topic, **kwargs ) self.base_macros = macros or {} self.macro_name = None def complete_macros(self, shell, args, prefix): # pylint: disable=unused-argument # returns a list of macro names in the current shell return list(shell.ctx.macros.keys()) def complete(self, shell, args, prefix): # The command_completer function takes in the parser, automatically # completes optional arguments (ex, '-v'/'--verbose') or sub-commands, # and complete any arguments' values by calling a callback function # with the same arguments as complete if the callback was defined # when the parser was created. return command_completer(self.parser, shell, args, prefix) def setup(self, shell): rc = 0 if 'macros' not in shell.ctx: shell.ctx.macros = {} for name in self.base_macros: rc = self.add_macro(shell, name, self.base_macros[name]) return rc def run(self, shell, args): try: ns = self.parser.parse_args(args) except CommandShortCircuit as e: return e.code rc = 0 if ns.show: if ns.delete or ns.name: self.usage_error(shell, 'incompatible arguments: -s/--show and ', '-d/--delete' if ns.delete else 'NAME') return -1 if ns.list or ns.name: self.usage_error(shell, 'incompatible arguments: -s/--show and ', '-l/--list' if ns.list else 'NAME') return -1 if ns.show in shell.ctx.macros: print("macro ", ns.show, sep='') for line in shell.ctx.macros[ns.show]: print(" ", line, sep='') print("end") else: self.error(shell, "unknown macro ", ns.show) rc = -1 elif ns.delete: if ns.list or ns.name: self.usage_error(shell, 'incompatible arguments: -d/--delete and ', '-l/--list' if ns.list else 'NAME') return -1 if ns.delete in shell.ctx.macros: del shell.ctx.macros[ns.delete] # It gets registered as a command too. See line 230 in this # file and register() in shell.py del shell.commands[ns.delete] else: self.error(shell, "unknown macro ", ns.delete) rc = -1 elif ns.name: if ns.list: self.usage_error(shell, "list option does not take an argument") else: if (ns.name in shell.commands.keys() and ns.name not in shell.ctx.macros): self.error( shell, "A macro cannot have the same name as an ", "existing command." ) return -1 self.macro_name = ns.name self.begin_block(shell) if sys.stdin.isatty(): print("Beginning macro, use the '", shell.ctx.block.end_cmd, "' command to save.", sep='') shell.ctx.macro_orig_eof_is_sigint = shell.eof_is_sigint shell.eof_is_sigint = True elif ns.list: # Left justified table print(title_str("Registered Macros", shell.width)) chunk_size = 3 tbl = Table( columns=(Column(''), Column(''), Column('', Column.Grow)), spacing=4, header=False, width=shell.width ) macro_names = list(shell.ctx.macros.keys()) for i in range(0, len(macro_names), chunk_size): chunk = macro_names[i:i + chunk_size] tbl.extend(chunk) tbl.write(sys.stdout) else: self.usage_error(shell, "missing required argument: NAME") rc = 1 return rc def end_block(self, shell, lines): self.add_macro(shell, self.macro_name, lines) self.macro_name = None shell.eof_is_sigint = shell.ctx.macro_orig_eof_is_sigint return 0 def cancel_block(self, shell): self.macro_name = None shell.eof_is_sigint = shell.ctx.macro_orig_eof_is_sigint def add_macro(self, shell, name, lines): shell.register( Macro(lines=lines, name=name, topic='__hidden__') ) shell.ctx.macros[name] = lines return 0
class IncludeCommand(Command): ''' Execute a script file. This entails reading each line of the given file and processing it as if it were typed into the shell. ''' def __init__(self, name='include', topic='shell', brief='execute a script file', **kwargs): self.parser = PypsiArgParser( prog=name, description=brief ) self.parser.add_argument( 'path', metavar='PATH', action='store', help='file to execute' ) super(IncludeCommand, self).__init__( name=name, topic=topic, brief=brief, usage=self.parser.format_help(), **kwargs ) self.stack = [] def complete(self, shell, args, prefix): return path_completer(args[-1], prefix=prefix) def run(self, shell, args): try: ns = self.parser.parse_args(args) except CommandShortCircuit as e: return e.code return self.include_file(shell, ns.path) def include_file(self, shell, path): fp = None ifile = IncludeFile(path) if self.stack: for i in self.stack: if i.abspath == ifile.abspath: self.error(shell, "recursive include for file ", ifile.abspath, '\n') return -1 self.stack.append(ifile) try: fp = safe_open(path, 'r') except (OSError, IOError) as e: self.error(shell, "error opening file {}: {}".format(path, str(e))) return -1 try: # Execute the lines in the file shell.include(fp) except Exception as e: self.error(shell, "error executing file ", path, ": ", str(e)) self.stack.pop() fp.close() return 0
class VariableCommand(Command): ''' Manage variables. ''' Usage = """var name or: var EXPRESSION or: var -l or: var -d name""" def __init__(self, name='var', brief='manage local variables', topic='shell', **kwargs): self.parser = PypsiArgParser( prog=name, description=brief, usage=VariableCommand.Usage ) self.parser.add_argument( '-l', '--list', help='list variables', action='store_true' ) self.parser.add_argument( '-d', '--delete', help='delete variable', metavar='VARIABLE' ) self.parser.add_argument( 'exp', metavar='EXPRESSION', help='expression defining variable, in the form of NAME = [VALUE]', nargs=argparse.REMAINDER ) super(VariableCommand, self).__init__( name=name, usage=self.parser.format_help(), topic=topic, brief=brief, **kwargs ) def run(self, shell, args): try: ns = self.parser.parse_args(args) except CommandShortCircuit as e: return e.code rc = 0 if ns.list: tbl = Table( columns=(Column("Variable"), Column("Value", Column.Grow)), width=shell.width, spacing=4, ) for name in shell.ctx.vars: if name == '_': continue s = shell.ctx.vars[name] if callable(s): s = s() elif isinstance(s, ManagedVariable): s = s.get(shell) tbl.append(name, obj_str(s)) tbl.write(sys.stdout) elif ns.delete: if ns.delete in shell.ctx.vars: s = shell.ctx.vars[ns.delete] if isinstance(s, ManagedVariable): self.error(shell, "variable is managed and cannot be deleted") rc = -1 else: del shell.ctx.vars[ns.delete] else: self.error(shell, "unknown variable: ", ns.delete) elif ns.exp and '=' in ''.join(args): (remainder, exp) = Expression.parse(args) if remainder or not exp: self.error(shell, "invalid expression") return 1 if isinstance(shell.ctx.vars[exp.operand], ManagedVariable): try: shell.ctx.vars[exp.operand].set(shell, exp.value) except ValueError as e: self.error(shell, "could not set variable: ", str(e)) else: shell.ctx.vars[exp.operand] = exp.value elif ns.exp: if len(args) == 1: if args[0] in shell.ctx.vars: s = shell.ctx.vars[args[0]] if callable(s): s = s() elif isinstance(s, ManagedVariable): s = s.getter(shell) print(obj_str(s)) else: self.error(shell, "unknown variable: ", args[0]) return 1 else: self.error(shell, "invalid expression") return 1 else: self.usage_error(shell, "missing required EXPRESSION") rc = 1 return rc
class HelpCommand(Command): ''' Provides access to manpage-esque topics and command usage information. ''' def __init__(self, name='help', topic='shell', brief='print information on a topic or command', topics=None, vars=None, **kwargs): self.parser = PypsiArgParser( prog=name, description=brief ) self.parser.add_argument( "topic", metavar="TOPIC", help="command or topic to print", nargs='?' ) super(HelpCommand, self).__init__( name=name, brief=brief, usage=self.parser.format_help(), topic=topic, **kwargs ) self.vars = vars or {} self.topics = topics def setup(self, shell): shell.ctx.topics = list(self.topics or []) shell.ctx.uncat_topic = Topic('uncat', 'Uncategorized Commands & Topics') shell.ctx.topic_lookup = {t.id: t for t in shell.ctx.topics} shell.ctx.topics_dirty = True def complete(self, shell, args, prefix): if shell.ctx.topics_dirty: self.reload(shell) completions = [ x.id for x in shell.ctx.topics if x.id.startswith(prefix) or not prefix ] completions.extend([ x for x in shell.commands if x.startswith(prefix) or not prefix ]) completions = sorted(completions) return completions def reload(self, shell): shell.ctx.uncat_topic.commands = [] for id in shell.ctx.topic_lookup: shell.ctx.topic_lookup[id].commands = [] for (name, cmd) in shell.commands.items(): if cmd.topic == '__hidden__': continue if cmd.topic: if cmd.topic in shell.ctx.topic_lookup: shell.ctx.topic_lookup[cmd.topic].commands.append(cmd) else: self.add_topic(shell, Topic(cmd.topic, commands=[cmd])) else: shell.ctx.uncat_topic.commands.append(cmd) shell.ctx.topics_dirty = False for topic in shell.ctx.topics: if topic.commands: topic.commands = sorted(topic.commands, key=lambda x: x.name) def add_topic(self, shell, topic): shell.ctx.topics_dirty = True shell.ctx.topic_lookup[topic.id] = topic shell.ctx.topics.append(topic) def print_topic_commands(self, shell, topic, title=None, name_col_width=20): print( AnsiCodes.yellow, title_str(title or topic.name or topic.id, shell.width), AnsiCodes.reset, sep='' ) print(AnsiCodes.yellow, end='') Table( columns=(Column(''), Column('', Column.Grow)), spacing=4, header=False, width=shell.width ).extend(*[ (' ' + c.name.ljust(name_col_width - 1), c.brief or '') for c in topic.commands ]).write(sys.stdout) print(AnsiCodes.reset, end='') def print_topics(self, shell): max_name_width = 0 for topic in shell.ctx.topics: for c in topic.commands: max_name_width = max(len(c.name), max_name_width) for c in shell.ctx.uncat_topic.commands: max_name_width = max(len(c.name), max_name_width) addl = [] for topic in shell.ctx.topics: if topic.content or not topic.commands: addl.append(topic) if topic.commands: self.print_topic_commands(shell, topic, name_col_width=max_name_width) print() if shell.ctx.uncat_topic.commands: self.print_topic_commands(shell, shell.ctx.uncat_topic, name_col_width=max_name_width) print() if addl: addl = sorted(addl, key=lambda x: x.id) print( AnsiCodes.yellow, title_str("Additional Topics", shell.width), sep='' ) Table( columns=(Column(''), Column('', Column.Grow)), spacing=4, header=False, width=shell.width ).extend(*[ (' ' + topic.id.ljust(max_name_width - 1), topic.name or '') for topic in addl ]).write(sys.stdout) print(AnsiCodes.reset) def print_topic(self, shell, id): if id not in shell.ctx.topic_lookup: if id in shell.commands: cmd = shell.commands[id] print(AnsiCodes.yellow, cmd.usage, AnsiCodes.reset, sep='') return 0 self.error(shell, "unknown topic: ", id) return -1 topic = shell.ctx.topic_lookup[id] if topic.content: print(title_str(topic.name or topic.id, shell.width)) try: cnt = topic.content.format(**self.vars) except: cnt = topic.content print(cnt) print() if topic.commands: self.print_topic_commands(shell, topic, "Commands") return 0 def run(self, shell, args): if shell.ctx.topics_dirty: self.reload(shell) try: ns = self.parser.parse_args(args) except CommandShortCircuit as e: return e.code rc = 0 if not ns.topic: self.print_topics(shell) else: rc = self.print_topic(shell, ns.topic) return rc