Skip to content

Commit fdbc73b

Browse files
authored
Merge pull request #478 from python-cmd2/hook_example
Add hook example
2 parents 3f737b4 + 58e2d8b commit fdbc73b

File tree

1 file changed

+115
-0
lines changed

1 file changed

+115
-0
lines changed

examples/hooks.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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

Comments
 (0)