|
| 1 | +#!/usr/bin/env python |
| 2 | +# coding=utf-8 |
| 3 | +""" |
| 4 | +A sample application for cmd2 demonstrating how to use hooks. |
| 5 | +
|
| 6 | +This application shows how to use postparsing hooks to allow case insensitive |
| 7 | +command names, abbreviated commands, as well as allowing numeric arguments to |
| 8 | +follow a command without any intervening whitespace. |
| 9 | +
|
| 10 | +""" |
| 11 | + |
| 12 | +import re |
| 13 | + |
| 14 | +from typing import List |
| 15 | + |
| 16 | +import cmd2 |
| 17 | + |
| 18 | + |
| 19 | +class CmdLineApp(cmd2.Cmd): |
| 20 | + """Example cmd2 application demonstrating the use of hooks. |
| 21 | +
|
| 22 | + This simple application has one command, `list` which generates a list |
| 23 | + of 10 numbers. This command takes one optional argument, which is the |
| 24 | + number to start on. |
| 25 | +
|
| 26 | + We have three postparsing hooks, which allow the user to enter: |
| 27 | +
|
| 28 | + (Cmd) list 5 |
| 29 | + (Cmd) L 5 |
| 30 | + (Cmd) l 5 |
| 31 | + (Cmd) L5 |
| 32 | + (Cmd) LI5 |
| 33 | +
|
| 34 | + and have them all treated as valid input which prints a list of 10 numbers |
| 35 | + starting with the number 5. |
| 36 | + """ |
| 37 | + |
| 38 | + # Setting this true makes it run a shell command if a cmd2/cmd command doesn't exist |
| 39 | + # default_to_shell = True |
| 40 | + def __init__(self, *args, **kwargs): |
| 41 | + # sneakily remove the cmd2.Cmd command called load |
| 42 | + # this lets a user enter a command like "l5" and allows it to |
| 43 | + # be unambiguous |
| 44 | + delattr(cmd2.Cmd, "do_load") |
| 45 | + |
| 46 | + super().__init__(*args, **kwargs) |
| 47 | + |
| 48 | + # register three hooks |
| 49 | + self.register_postparsing_hook(self.add_whitespace_hook) |
| 50 | + self.register_postparsing_hook(self.downcase_hook) |
| 51 | + self.register_postparsing_hook(self.abbrev_hook) |
| 52 | + |
| 53 | + def add_whitespace_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData: |
| 54 | + """A hook to split alphabetic command names immediately followed by a number. |
| 55 | +
|
| 56 | + l24 -> l 24 |
| 57 | + list24 -> list 24 |
| 58 | + list 24 -> list 24 |
| 59 | +
|
| 60 | + """ |
| 61 | + command = data.statement.command |
| 62 | + # regular expression with looks for: |
| 63 | + # ^ - the beginning of the string |
| 64 | + # ([^\s\d]+) - one or more non-whitespace non-digit characters, set as capture group 1 |
| 65 | + # (\d+) - one or more digit characters, set as capture group 2 |
| 66 | + command_pattern = re.compile(r'^([^\s\d]+)(\d+)') |
| 67 | + match = command_pattern.search(command) |
| 68 | + if match: |
| 69 | + data.statement = self.statement_parser.parse("{} {} {}".format( |
| 70 | + match.group(1), |
| 71 | + match.group(2), |
| 72 | + '' if data.statement.args is None else data.statement.args |
| 73 | + )) |
| 74 | + return data |
| 75 | + |
| 76 | + def downcase_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData: |
| 77 | + """A hook to make uppercase commands lowercase.""" |
| 78 | + command = data.statement.command.lower() |
| 79 | + data.statement = self.statement_parser.parse("{} {}".format( |
| 80 | + command, |
| 81 | + '' if data.statement.args is None else data.statement.args |
| 82 | + )) |
| 83 | + return data |
| 84 | + |
| 85 | + def abbrev_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData: |
| 86 | + """Accept unique abbreviated commands""" |
| 87 | + target = 'do_' + data.statement.command |
| 88 | + if target not in dir(self): |
| 89 | + # check if the entered command might be an abbreviation |
| 90 | + funcs = [func for func in self.keywords if func.startswith(data.statement.command)] |
| 91 | + if len(funcs) == 1: |
| 92 | + raw = data.statement.raw.replace(data.statement.command, funcs[0], 1) |
| 93 | + data.statement = self.statement_parser.parse(raw) |
| 94 | + return data |
| 95 | + |
| 96 | + @cmd2.with_argument_list |
| 97 | + def do_list(self, arglist: List[str]) -> None: |
| 98 | + """Generate a list of 10 numbers.""" |
| 99 | + if arglist: |
| 100 | + first = arglist[0] |
| 101 | + try: |
| 102 | + first = int(first) |
| 103 | + except ValueError: |
| 104 | + first = 1 |
| 105 | + else: |
| 106 | + first = 1 |
| 107 | + last = first + 10 |
| 108 | + |
| 109 | + for x in range(first, last): |
| 110 | + self.poutput(str(x)) |
| 111 | + |
| 112 | + |
| 113 | +if __name__ == '__main__': |
| 114 | + c = CmdLineApp() |
| 115 | + c.cmdloop() |
0 commit comments