Example #1
0
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, ctx):
        try:
            ns = self.parser.parse_args(args)
        except CommandShortCircuit as e:
            return e.code

        raise SystemExit(ns.code)
Example #2
0
class EchoCommand(Command):
    '''
    Prints text to the screen.
    '''

    def __init__(self, name='echo', topic='shell', **kwargs):
        self.parser = PypsiArgParser(
            prog=name,
            description='display a line of text',
            usage=EchoCmdUsage
        )

        subcmd = self.parser.add_argument_group(title='Stream')

        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='print a line of text', **kwargs)

    def run(self, shell, args, ctx):
        ns = self.parser.parse_args(shell, args)
        if self.parser.rc is not None:
            return self.parser.rc

        tail = '' if ns.nolf else '\n'

        print(' '.join(ns.message), sep='', end=tail)

        return 0
Example #3
0
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, ctx):
        try:
            ns = self.parser.parse_args(args)
        except CommandShortCircuit as e:
            return e.code

        raise SystemExit(ns.code)
Example #4
0
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, ctx):
        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
        ])

        child = ctx.fork()
        for line in sys.stdin:
            cmd = base.replace(ns.token, line.strip())
            shell.execute(cmd, child)

        return 0
Example #5
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, ctx):
        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(shell, args, prefix)

    def tail(self, fname, lines=10, block_size=1024):
        data = []
        blocks = -1
        num_lines = 0
        where = 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
Example #6
0
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', action='store_true'
        )
        self.parser.add_argument(
            '-s', '--show', help='print macro body', action='store_true'
        )
        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 {}

    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, shell.ctx.macros[name])
                
        return rc

    def run(self, shell, args, ctx):
        try:
            ns = self.parser.parse_args(args)
        except CommandShortCircuit as e:
            return e.code

        rc = 0
        if ns.name:
            if ns.delete:
                if ns.name in shell.ctx.macros:
                    del shell.ctx.macros[ns.name]
                    #It gets registered as a command too. See line 202 in this file and register() in shell.py
                    del shell.commands[ns.name]
                else:
                    self.error(shell, "unknown macro ", ns.name)
                    rc = -1
            elif ns.show:
                if ns.name in shell.ctx.macros:
                    print("macro ", ns.name, sep='')
                    for line in shell.ctx.macros[ns.name]:
                        print("    ", line, sep='')
                    print("end")
                else:
                    self.error(shell, "unknown macro ", ns.name)
                    rc = -1
            elif 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:
            tbl = FixedColumnTable(widths=[shell.width//3]*3)
            print(title_str("Registered Macros", shell.width))
            for name in shell.ctx.macros:
                tbl.add_cell(sys.stdout, name)
            tbl.flush(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
Example #7
0
class VariableCommand(Command):
    '''
    Manage variables.
    '''

    Usage = """usage: var name = value
   or: var -l
   or: var -d name
Manage local variables."""

    def __init__(self, name='var', brief='manage variables', topic='shell', **kwargs):
        self.parser = PypsiArgParser(
            prog=name,
            description=brief
        )

        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',
            nargs=argparse.REMAINDER
        )
        super(VariableCommand, self).__init__(
            name=name, usage=self.parser.format_help(), topic=topic,
            brief=brief, **kwargs
        )

    def run(self, shell, args, ctx):
        ns = self.parser.parse_args(shell, args)
        if self.parser.rc is not None:
            return self.parser.rc

        rc = 0
        if ns.list:
            tbl = Table(
                columns=(Column("Variable"), Column("Value", Column.Grow)),
                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.getter(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
            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
Example #8
0
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, **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.topics = list(topics or [])
        self.uncat = Topic('uncat', 'Uncategorized Commands & Features')
        self.lookup = {t.id: t for t in self.topics}
        self.dirty = True

    def reload(self, shell):
        self.uncat.commands = []
        for id in self.lookup:
            self.lookup[id].commands = []

        for (name, cmd) in shell.commands.items():
            if cmd.topic:
                if cmd.topic in self.lookup:
                    self.lookup[cmd.topic].commands.append(cmd)
                else:
                    self.add_topic(Topic(cmd.topic, commands=[cmd]))
            else:
                self.uncat.commands.append(cmd)
        self.dirty = False


    def add_topic(self, topic):
        self.dirty = True
        self.lookup[topic.id] = topic
        self.topics.append(topic)

    def print_topic_commands(self, shell, topic, title=None):
        print(
            AnsiStderr.yellow,
            title_str(title or topic.name or topic.id, shell.width),
            AnsiStderr.reset,
            sep=''
        )
        print(AnsiStderr.yellow, end='')
        Table(
            columns=(Column(''), Column('', Column.Grow)),
            spacing=4,
            header=False,
            width=shell.width
        ).extend(
            *[
                (' Name', 'Description'),
                (' ----', '-----------')
            ]
        ).extend(
            *[(' '+c.name, c.brief or '') for c in topic.commands]
        ).write(sys.stdout)
        print(AnsiStderr.reset, end='')

    def print_topics(self, shell):
        addl = []
        for topic in self.topics:
            if topic.content or not topic.commands:
                addl.append(topic)

            if topic.commands:
                self.print_topic_commands(shell, topic)
                print()

        if self.uncat.commands:
            self.print_topic_commands(shell, self.uncat)
            print()

        if addl:
            print(
                AnsiStderr.yellow,
                title_str("Additional Topics", shell.width),
                AnsiStderr.reset,
                sep=''
            )
            tbl = FixedColumnTable([shell.width // 3] * 3)
            for topic in addl:
                tbl.add_cell(sys.stdout, topic.id)
            tbl.flush(sys.stdout)
            print()

    def print_topic(self, shell, id):
        if id not in self.lookup:
            if id in shell.commands:
                cmd = shell.commands[id]
                print(AnsiStderr.yellow, cmd.usage, AnsiStderr.reset, sep='')
                return 0

            self.error(shell, "unknown topic: ", id)
            return -1

        topic = self.lookup[id]
        if topic.content:
            print(title_str(topic.name or topic.id, shell.width))
            print(word_wrap(topic.content, shell.width))
            print()

        if topic.commands:
            self.print_topic_commands(shell, topic, "Commands")
        return 0

    def run(self, shell, args, ctx):
        if self.dirty:
            self.reload(shell)

        ns = self.parser.parse_args(shell, args)
        if self.parser.rc is not None:
            return self.parser.rc

        rc = 0
        if not ns.topic:
            self.print_topics(shell)
        else:
            rc = self.print_topic(shell, ns.topic)

        return rc
Example #9
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,
            #usage=IncludeCmdUsage
        )

        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(shell, args, prefix)

    def run(self, shell, args, ctx):
        try:
            ns = self.parser.parse_args(args)
        except CommandShortCircuit as e:
            return e.code

        return self.include_file(shell, ns.path, ctx)

    def include_file(self, shell, path, ctx):
        fp = None
        ifile = IncludeFile(path)

        top = False
        templ = ''
        if self.stack:
            #templ = shell.error.prefix
            for i in self.stack:
                if i.abspath == ifile.abspath:
                    self.error(shell, "recursive include for file ",
                               ifile.abspath, '\n')
                    return -1
        else:
            #templ = shell.error.prefix + "error in file {file} on line {line}: "
            top = True

        self.stack.append(ifile)

        try:
            fp = safe_open(path, 'r')
        except (OSError, IOError) as e:
            self.error(shell, "error opening file ", path, ": ", str(e), '\n')
            return -1

        #orig_prefix = shell.error.prefix
        next = ctx.fork()
        for line in fp:
            #shell.error.prefix = templ.format(file=ifile.name, line=ifile.line)
            shell.execute(line.strip(), next)
            ifile.line += 1

        #if top:
        #    shell.error.prefix = orig_prefix

        self.stack.pop()
        fp.close()

        return 0
Example #10
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, ctx):
        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(shell, args, prefix)

    def tail(self, fname, lines=10, block_size=1024):
        data = []
        blocks = -1
        num_lines = 0
        where = 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
Example #11
0
class VariableCommand(Command):
    '''
    Manage variables.
    '''

    Usage = """var name
   or: var name = value
   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',
            nargs=argparse.REMAINDER
        )
        super(VariableCommand, self).__init__(
            name=name, usage=self.parser.format_help(), topic=topic,
            brief=brief, **kwargs
        )

    def run(self, shell, args, ctx):
        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
Example #12
0
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.topics = list(topics or [])
        self.uncat = Topic('uncat', 'Uncategorized Commands & Topics')
        self.lookup = {t.id: t for t in self.topics}
        self.dirty = True
        self.vars = vars or {}

    def complete(self, shell, args, prefix):
        '''
        args = [arg for arg in args if not arg.startswith('-')]

        completions = []
        base = []
        for topic in self.topics:
            base.append(topic.name or topic.id)
            base.extend([command.name for command in topic.commands])

        if len(args) <= 1:
            completions.extend([x for x in base if x.startswith(prefix) or not prefix])

        return sorted(completions)
        '''
        #pre = args[-1] if args else prefix
        if self.dirty:
            self.reload(shell)

        completions = sorted([x.id for x in self.topics if x.id.startswith(prefix) or not prefix])
        return completions

    def reload(self, shell):
        self.uncat.commands = []
        for id in self.lookup:
            self.lookup[id].commands = []

        for (name, cmd) in shell.commands.items():
            if cmd.topic == '__hidden__':
                continue

            if cmd.topic:
                if cmd.topic in self.lookup:
                    self.lookup[cmd.topic].commands.append(cmd)
                else:
                    self.add_topic(Topic(cmd.topic, commands=[cmd]))
            else:
                self.uncat.commands.append(cmd)
        self.dirty = False
        
        for topic in self.topics:
            if topic.commands:
                topic.commands = sorted(topic.commands, key=lambda x: x.name)

    def add_topic(self, topic):
        self.dirty = True
        self.lookup[topic.id] = topic
        self.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(
        #    *[
        #        (' Name', 'Description'),
        #        (' ----', '-----------')
        #    ]
        ).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 self.topics:
            for c in topic.commands:
                max_name_width = max(len(c.name), max_name_width)

        for c in self.uncat.commands:
            max_name_width = max(len(c.name), max_name_width)

        addl = []
        for topic in self.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 self.uncat.commands:
            self.print_topic_commands(shell, self.uncat, 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 self.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 = self.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(word_wrap(cnt, shell.width))
            print()

        if topic.commands:
            self.print_topic_commands(shell, topic, "Commands")
        return 0

    def run(self, shell, args, ctx):
        if self.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
Example #13
0
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, ctx):
        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))  # int(self.rand.random() * len(self.tips)

        if header:
            title = "Tip #{}".format(i + 1)
            title += "\n" + ("-" * len(title))
            print(AnsiCodes.green, title, AnsiCodes.reset, sep="")

        try:
            cnt = self.tips[i].format(**self.vars)
        except:
            cnt = self.tips[i]

        print(word_wrap(cnt, shell.width))

    def print_motd(self, shell):
        if not self.motd:
            self.error(shell, "no motd available")
            return -1

        try:
            cnt = self.motd.format(**self.vars)
        except:
            cnt = self.motd

        print(
            AnsiCodes.green,
            "Message of the Day".center(shell.width),
            "\n",
            ">" * shell.width,
            "\n",
            AnsiCodes.reset,
            word_wrap(cnt, shell.width),
            "\n",
            AnsiCodes.green,
            "<" * shell.width,
            "\n",
            AnsiCodes.reset,
            sep="",
        )
Example #14
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,
            #usage=IncludeCmdUsage
        )

        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(shell, args, prefix)

    def run(self, shell, args, ctx):
        try:
            ns = self.parser.parse_args(args)
        except CommandShortCircuit as e:
            return e.code

        return self.include_file(shell, ns.path, ctx)

    def include_file(self, shell, path, ctx):
        fp = None
        ifile = IncludeFile(path)

        top = False
        templ = ''
        if self.stack:
            #templ = shell.error.prefix
            for i in self.stack:
                if i.abspath == ifile.abspath:
                    self.error(shell, "recursive include for file ", ifile.abspath, '\n')
                    return -1
        else:
            #templ = shell.error.prefix + "error in file {file} on line {line}: "
            top = True

        self.stack.append(ifile)

        try:
            fp = safe_open(path, 'r')
        except (OSError, IOError) as e:
            self.error(shell, "error opening file ", path, ": ", str(e), '\n')
            return -1

        #orig_prefix = shell.error.prefix
        next = ctx.fork()
        for line in fp:
            #shell.error.prefix = templ.format(file=ifile.name, line=ifile.line)
            shell.execute(line.strip(), next)
            ifile.line += 1

        #if top:
        #    shell.error.prefix = orig_prefix

        self.stack.pop()
        fp.close()

        return 0
Example #15
0
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,
                 **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.topics = list(topics or [])
        self.uncat = Topic('uncat', 'Uncategorized Commands & Features')
        self.lookup = {t.id: t for t in self.topics}
        self.dirty = True

    def reload(self, shell):
        self.uncat.commands = []
        for id in self.lookup:
            self.lookup[id].commands = []

        for (name, cmd) in shell.commands.items():
            if cmd.topic:
                if cmd.topic in self.lookup:
                    self.lookup[cmd.topic].commands.append(cmd)
                else:
                    self.add_topic(Topic(cmd.topic, commands=[cmd]))
            else:
                self.uncat.commands.append(cmd)
        self.dirty = False

    def add_topic(self, topic):
        self.dirty = True
        self.lookup[topic.id] = topic
        self.topics.append(topic)

    def print_topic_commands(self, shell, topic, title=None):
        print(AnsiStderr.yellow,
              title_str(title or topic.name or topic.id, shell.width),
              AnsiStderr.reset,
              sep='')
        print(AnsiStderr.yellow, end='')
        Table(columns=(Column(''), Column('', Column.Grow)),
              spacing=4,
              header=False,
              width=shell.width).extend(
                  *[(' Name',
                     'Description'), (' ----', '-----------')]).extend(
                         *[(' ' + c.name, c.brief or '')
                           for c in topic.commands]).write(sys.stdout)
        print(AnsiStderr.reset, end='')

    def print_topics(self, shell):
        addl = []
        for topic in self.topics:
            if topic.content or not topic.commands:
                addl.append(topic)

            if topic.commands:
                self.print_topic_commands(shell, topic)
                print()

        if self.uncat.commands:
            self.print_topic_commands(shell, self.uncat)
            print()

        if addl:
            print(AnsiStderr.yellow,
                  title_str("Additional Topics", shell.width),
                  AnsiStderr.reset,
                  sep='')
            tbl = FixedColumnTable([shell.width // 3] * 3)
            for topic in addl:
                tbl.add_cell(sys.stdout, topic.id)
            tbl.flush(sys.stdout)
            print()

    def print_topic(self, shell, id):
        if id not in self.lookup:
            if id in shell.commands:
                cmd = shell.commands[id]
                print(AnsiStderr.yellow, cmd.usage, AnsiStderr.reset, sep='')
                return 0

            self.error(shell, "unknown topic: ", id)
            return -1

        topic = self.lookup[id]
        if topic.content:
            print(title_str(topic.name or topic.id, shell.width))
            print(word_wrap(topic.content, shell.width))
            print()

        if topic.commands:
            self.print_topic_commands(shell, topic, "Commands")
        return 0

    def run(self, shell, args, ctx):
        if self.dirty:
            self.reload(shell)

        ns = self.parser.parse_args(shell, args)
        if self.parser.rc is not None:
            return self.parser.rc

        rc = 0
        if not ns.topic:
            self.print_topics(shell)
        else:
            rc = self.print_topic(shell, ns.topic)

        return rc
Example #16
0
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, ctx):
        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)) #int(self.rand.random() * len(self.tips)
        
        if header:
            title = "Tip #{}".format(i+1)
            title += '\n' + ('-'*len(title))
            print(AnsiCodes.green, title, AnsiCodes.reset, sep='')

        try:
            cnt = self.tips[i].format(**self.vars)
        except:
            cnt = self.tips[i]

        print(word_wrap(cnt, shell.width))

    def print_motd(self, shell):
        if not self.motd:
            self.error(shell, "no motd available")
            return -1

        try:
            cnt = self.motd.format(**self.vars)
        except:
            cnt = self.motd

        print(
            AnsiCodes.green,
            "Message of the Day".center(shell.width), '\n',
            '>' * shell.width, "\n",
            AnsiCodes.reset,
            word_wrap(cnt, shell.width), '\n',
            AnsiCodes.green,
            "<" * shell.width, "\n",
            AnsiCodes.reset,
            sep=''
        )
Example #17
0
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.topics = list(topics or [])
        self.uncat = Topic('uncat', 'Uncategorized Commands & Topics')
        self.lookup = {t.id: t for t in self.topics}
        self.dirty = True
        self.vars = vars or {}

    def complete(self, shell, args, prefix):
        '''
        args = [arg for arg in args if not arg.startswith('-')]

        completions = []
        base = []
        for topic in self.topics:
            base.append(topic.name or topic.id)
            base.extend([command.name for command in topic.commands])

        if len(args) <= 1:
            completions.extend([x for x in base if x.startswith(prefix) or not prefix])

        return sorted(completions)
        '''
        #pre = args[-1] if args else prefix
        if self.dirty:
            self.reload(shell)

        completions = sorted([
            x.id for x in self.topics if x.id.startswith(prefix) or not prefix
        ])
        return completions

    def reload(self, shell):
        self.uncat.commands = []
        for id in self.lookup:
            self.lookup[id].commands = []

        for (name, cmd) in shell.commands.items():
            if cmd.topic == '__hidden__':
                continue

            if cmd.topic:
                if cmd.topic in self.lookup:
                    self.lookup[cmd.topic].commands.append(cmd)
                else:
                    self.add_topic(Topic(cmd.topic, commands=[cmd]))
            else:
                self.uncat.commands.append(cmd)
        self.dirty = False

        for topic in self.topics:
            if topic.commands:
                topic.commands = sorted(topic.commands, key=lambda x: x.name)

    def add_topic(self, topic):
        self.dirty = True
        self.lookup[topic.id] = topic
        self.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(
            #    *[
            #        (' Name', 'Description'),
            #        (' ----', '-----------')
            #    ]
        ).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 self.topics:
            for c in topic.commands:
                max_name_width = max(len(c.name), max_name_width)

        for c in self.uncat.commands:
            max_name_width = max(len(c.name), max_name_width)

        addl = []
        for topic in self.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 self.uncat.commands:
            self.print_topic_commands(shell,
                                      self.uncat,
                                      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 self.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 = self.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(word_wrap(cnt, shell.width))
            print()

        if topic.commands:
            self.print_topic_commands(shell, topic, "Commands")
        return 0

    def run(self, shell, args, ctx):
        if self.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
Example #18
0
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)

        self.parser.add_argument('-l',
                                 '--list',
                                 help='list all macros',
                                 action='store_true')
        self.parser.add_argument('-d',
                                 '--delete',
                                 help='delete macro',
                                 action='store_true')
        self.parser.add_argument('-s',
                                 '--show',
                                 help='print macro body',
                                 action='store_true')
        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 {}

    def setup(self, shell):
        if 'macros' not in shell.ctx:
            shell.ctx.macros = {}

        for name in self.base_macros:
            self.add_macro(shell, name, shell.ctx.macros[name])
        return 0

    def run(self, shell, args, ctx):
        ns = self.parser.parse_args(shell, args)
        if self.parser.rc is not None:
            return self.parser.rc

        rc = 0
        if ns.name:
            if ns.delete:
                if ns.name in shell.ctx.macros:
                    del shell.ctx.macros[ns.name]
                else:
                    self.error(shell, "unknown macro ", ns.name)
                    rc = -1
            elif ns.show:
                if ns.name in shell.ctx.macros:
                    print("macro ", ns.name, sep='')
                    for line in shell.ctx.macros[ns.name]:
                        print("    ", line, sep='')
                    print("end")
                else:
                    self.error(shell, "unknown macro ", ns.name)
                    rc = -1
            elif ns.list:
                self.usage_error(shell,
                                 "list option does not take an argument")
            else:
                self.macro_name = ns.name
                self.begin_block(shell)
        elif ns.list:
            tbl = FixedColumnTable(widths=[shell.width // 3] * 3)
            print(title_str("Registered Macros", shell.width))
            for name in shell.ctx.macros:
                tbl.add_cell(sys.stdout, name)
            tbl.flush(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
        return 0

    def cancel_block(self, shell):
        self.macro_name = None

    def add_macro(self, shell, name, lines):
        shell.register(Macro(lines=lines, name=name))
        shell.ctx.macros[name] = lines
        return 0
Example #19
0
class TipCommand(Command):

    def __init__(self, name='tip', brief='print shell tips', topic='shell', tips=None, motd=None, **kwargs):
        self.tips = tips or []
        self.motd = motd 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, ctx):
        ns = self.parser.parse_args(shell, args)
        if self.parser.rc is not None:
            return self.parser.rc

        if ns.motd:
            self.print_motd(shell)
        else:
            self.print_random_tip(shell)

        return 0

    def print_random_tip(self, shell, header=True):
        i = int(self.rand.random() * 10) % len(self.tips)
        
        if header:
            title = "Tip #{}".format(i+1)
            title += '\n' + ('-'*len(title))
            print(AnsiStdout.green, title, AnsiStdout.reset, sep='')

        print(word_wrap(self.tips[i], shell.width))

    def print_motd(self, shell):
        print(
            AnsiStdout.green,
            "Message of the Day".center(shell.width), '\n',
            '>' * shell.width, "\n",
            AnsiStdout.reset,
            word_wrap(self.motd, shell.width), '\n',
            AnsiStdout.green,
            "<" * shell.width, "\n",
            AnsiStdout.reset,
            sep=''
        )