forked from emmettbutler/candela
-
Notifications
You must be signed in to change notification settings - Fork 0
/
shell_example.py
220 lines (187 loc) · 7.87 KB
/
shell_example.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
import getpass
from candela.shell import Shell
from candela.menu import Menu
from candela.command import Command, QuitCommand, RunScriptCommand, BackCommand
from candela import constants
class MyShell(Shell):
def __init__(self):
Shell.__init__(self)
self.name = "My Shell"
# set the header that appears in the top left of the shell
self.header = """
___ _ _
/ __\__ _ _ __ __| | ___| | __ _
/ / / _` | '_ \ / _` |/ _ \ |/ _` |
/ /__| (_| | | | | (_| | __/ | (_| |
\____/\__,_|_| |_|\__,_|\___|_|\__,_|
"""
# place sticky text on the top right side of the shell
# you can change this text by calling sticker() from a command
self.sticker("Welcome, %s" % getpass.getuser())
# define commands
hello_world_com = self.build_hello_command()
named_com = self.build_named_args_command()
complex_com = self.build_complex_command()
invalid_com = self.build_invalid_command()
quit_com = self.build_quit_command()
sticker_com = self.build_sticker_command()
builtins_com = self.build_builtin_command()
# define menus
main_menu = Menu('main')
#menu display title
main_menu.title = "Main menu"
# list of Command objects making up menu
main_menu.commands = [hello_world_com, named_com, complex_com, sticker_com,
invalid_com, builtins_com, quit_com]
script_com = self.build_script_command()
back_com = self.build_back_command()
builtins_menu = Menu('builtins')
builtins_menu.title = "Built-in commands menu"
builtins_menu.commands = [script_com, hello_world_com, back_com, quit_com]
# list of all menus in the app
self.menus = [main_menu, builtins_menu]
# name of default menu
self.menu = 'main'
self.put("""
Welcome to Candela, the simple python shell builder.
You can use Candela to build your own shell-based interfaces.
This is especially good for custom offline tools for games or web production.
For example, if your game requires lots of data in a custom XML format, you can
use Candela to easily set up an editor for that data.
This is an instructional shell app built in Candela that demonstrates its functionality.
Try any of the commands listed in the menu bar to continue.
""")
def build_hello_command(self):
com = Command('first_command', 'Intro to commands')
def _run(*args, **kwargs):
self.put("""
Congratulations, you just invoked your first command in Candela.
This text is being printed from inside a callback function passed to the library
via a Command object.
You can print text to your shell from anywhere in your Shell subclass by calling
self.put().
""")
return constants.CHOICE_VALID
com.run = _run
return com
def build_named_args_command(self):
com = Command('named my_arg <-f filename> [-g othername]', 'Demonstrate arguments')
def _val(*args, **kwargs):
success,message = com.default_validate(*args, **kwargs)
if not success:
if len(args) == 0 and len(kwargs) == 0:
self.put("""
Some commands accept command line arguments.
When creating a command, you specify these arguments with a special (simple) syntax.
For example, this command is called "%s". It accepts both positional and named arguments.
Positional arguments are indicated by a bare word following the command's name
in the command definition.
Named arguments are wrapped in either <> or [], and contain both the argument name
(starting with '-') and a helpful tip about the argument function.
Named arguments with <> are required, those with [] are optional.
Try passing arguments to this command!
""" % com.name)
else:
self.put("""
This command requires one unnamed argument followed by a named argument (-f).
Try this:
%s helloworld -f data.txt
""" % com.name)
return (False, message)
return (success, message)
com.validate = _val
def _run(*args, **kwargs):
self.put("Got arguments:")
self.put(args)
self.put(kwargs)
self.put("Arguments are passed around in a format quite familiar to python:")
self.put("Positional arguments in a list, named arguments in a dictionary")
return constants.CHOICE_VALID
com.run = _run
return com
def build_invalid_command(self):
com = Command('broken', 'Demonstrate invalid command')
def _run(*args, **kwargs):
self.put("I will never print")
return constants.CHOICE_VALID
com.run = _run
def _val(*args, **kwargs):
message = """
You can write custom validation functions for your commands.
A validation function will run before execution of the command.
If the validation returns False, the command is not run.
The default validation function checks for the presence of all required
arguments, but you can override this behavior by setting the Command's validate member.
This is a command that always fails to validate.
"""
return (False, message)
com.validate = _val
return com
def build_complex_command(self):
com = Command('cat <-f filename>', 'Demonstrate arbitrary python running')
def _run(*args, **kwargs):
self.put("""
Commands can run arbitrary python via a callback. Here's a callback that reads a
file from your local drive and prints it to the shell.
""")
try:
with open(kwargs['f'], 'r') as f:
self.put(f.read())
except IOError:
self.put("%s : No such file!" % kwargs['f'])
return constants.CHOICE_VALID
com.run = _run
return com
def build_sticker_command(self):
com = Command('make_sticker text', 'Make a new sticker')
def _run(*args, **kwargs):
self.put("""
This command places a 'sticker' on the terminal. Use these to present persistent
data to the user.
""")
self.sticker(" ".join(args))
return constants.CHOICE_VALID
com.run = _run
return com
def build_builtin_command(self):
com = Command('builtins', 'Go to builtin commands menu')
def _run(*args, **kwargs):
self.put("""
Commands can conditionally lead the user to other menus.
This demo app has two menus defined: the main menu and the built-in commands menu.
The command you just ran has defined the attribute self.new_menu to point to the
builtins menu.
Notice that the options menu has changed to reflect the new commands available in this menu.
""")
return constants.CHOICE_VALID
com.run = _run
com.new_menu = 'builtins'
return com
def build_script_command(self):
com = RunScriptCommand(self)
def _run(*args, **kwargs):
self.put("""
This command runs a script.
A Candela script is simply a text file containing one command per line.
""")
com.default_run(*args, **kwargs)
return constants.CHOICE_VALID
com.run = _run
def _val(*args, **kwargs):
success, message = com.default_validate(*args, **kwargs)
if not success:
self.put("""
Try running\nrun script_example.txt
""")
return (False, message)
return (success, message)
com.validate = _val
return com
def build_back_command(self):
return BackCommand('main')
def build_quit_command(self):
quit_com = QuitCommand(self.name)
quit_com.alias('q')
return quit_com
if __name__ == "__main__":
MyShell().main_loop().end()