summaryrefslogtreecommitdiff
path: root/commands.py
blob: 61f5170599ff1979dde7aa2155b63f1b3d8d3d18 (plain)
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
'''
This file should contain all the built-in `do_*` commands of Cmd2.
'''


def do__relative_load(self, arg=None):
	'''
	Runs commands in script at file or URL; if this is called from within an
	already-running script, the filename will be interpreted relative to the
	already-running script's directory.
	'''
	if arg:
		arg = arg.split(None, 1)
		targetname, args = arg[0], (arg[1:] or [''])[0]
		targetname = os.path.join(self.current_script_dir or '', targetname)
		self.do__load('%s %s' % (targetname, args))

urlre = re.compile('(https?://[-\\w\\./]+)')

def do_cmdenvironment(self, args):
	'''
	Summary report of interactive parameters.
	'''

	self.stdout.write("""
	Commands are %(casesensitive)scase-sensitive.
	Commands may be terminated with: %(terminators)s

	Settable parameters: %(settable)s\n""" % \
	{   'casesensitive' : (self.case_insensitive and 'not ') or '',
		'terminators'   : str(self.terminators),
		'settable'      : ' '.join(self.settable)
	})


def do_help(self, arg):
	if arg:
		funcname = self.func_named(arg)
		if funcname:
			fn = getattr(self, funcname)
			try:
				fn.optionParser.print_help(file=self.stdout)
			except AttributeError:
				cmd.Cmd.do_help(self, funcname[3:])
	else:
		cmd.Cmd.do_help(self, arg)


def do_shortcuts(self, args):
	'''
	Lists single-key shortcuts available.
	'''
	result = "\n".join('%s: %s' % (sc[0], sc[1]) for sc in sorted(self.shortcuts))
	self.stdout.write("Single-key shortcuts for other commands:\n%s\n" % (result))


def do_EOF(self, arg):
	return self._STOP_SCRIPT_NO_EXIT # End of script; should not exit app

do_eof = do_EOF


def do_quit(self, arg):
	return self._STOP_AND_EXIT

do_exit = do_quit
do_q    = do_quit


@options(
	[make_option(
		'-l', '--long',
		action  ="store_true",
		help    ="describe function of parameter")])
def do_show(self, arg, opts):
	'''
	Show the value of a parameter.
	'''
	param = arg.strip().lower()
	result = {}
	maxlen = 0
	for p in self.settable:
		if (not param) or p.startswith(param):
			result[p] = '%s: %s' % (p, str(getattr(self, p)))
			maxlen = max(maxlen, len(result[p]))
	if result:
		for p in sorted(result):
			if opts.long:
				self.poutput('%s # %s' % (result[p].ljust(maxlen), self.settable[p]))
			else:
				self.poutput(result[p])
	else:
		raise NotImplementedError("Parameter '%s' not supported (type 'show' for list of parameters)." % param)


def do_set(self, arg):
	'''
	Set a cmd2 parameter.  Accepts abbreviated parameter names
	if there is no ambiguity.  Lists settable parameters and values if
	called with no arguments.
	'''
	try:
		statement, paramName, val = arg.parsed.raw.split(None, 2)
		val = val.strip()
		paramName = paramName.strip().lower()

		if paramName not in self.settable:
			hits = [p for p in self.settable if p.startswith(paramName)]
			if len(hits) == 1:
				paramName = hits[0]
			else:
				return self.do_show(paramName)
		currentVal = getattr(self, paramName)

		if (val[0] == val[-1]) and val[0] in ("'", '"'):
			val = val[1:-1]
		else:
			val = cast(currentVal, val)

		setattr(self, paramName, val)
		self.stdout.write('%s - was: %s\nnow: %s\n' % (paramName, currentVal, val))

		if currentVal != val:
			try:
				onchange_hook = getattr(self, '_onchange_%s' % paramName)
				onchange_hook(old=currentVal, new=val)
			except AttributeError:
				pass
	except (ValueError, AttributeError, NotSettableError), e:
		self.do_show(arg)


def do_pause(self, arg):
	'''Display the provided text, then wait for the user to press RETURN.'''
	raw_input(arg + '\n')


def do_shell(self, arg):
	'''
	Executes a command as if at the OS prompt.
	'''
	os.system(arg)


def do_py(self, arg):
	'''
	py <command>: Executes a Python command.
	py: Enters interactive Python mode.

	End with ``Ctrl-D`` (Unix) / ``Ctrl-Z`` (Windows), ``quit()``, '`exit()``.

	Non-python commands can be issued with ``cmd("your command")``.
	Run python code from external files with ``run("filename.py")``
	'''
	self.pystate['self'] = self
	arg = arg.parsed.raw[2:].strip()
	localvars = (self.locals_in_py and self.pystate) or {}
	interp = InteractiveConsole(locals=localvars)
	interp.runcode('import sys, os;sys.path.insert(0, os.getcwd())')

	if arg.strip():
		interp.runcode(arg)
	else:
		def quit():
			raise EmbeddedConsoleExit
		def onecmd_plus_hooks(arg):
			return self.onecmd_plus_hooks(arg + '\n')
		def run(arg):
			try:
				file = open(arg)
				interp.runcode(file.read())
				file.close()
			except IOError, e:
				self.perror(e)
		self.pystate['quit'] = quit
		self.pystate['exit'] = quit
		self.pystate['cmd'] = onecmd_plus_hooks
		self.pystate['run'] = run

		try:
			cprt        = 'Type "help", "copyright", "credits" or "license" for more information.'
			keepstate   = Statekeeper(sys, ('stdin','stdout'))
			sys.stdout  = self.stdout
			sys.stdin   = self.stdin
			interp.interact(banner= "Python %s on %s\n%s\n(%s)\n%s" %
				   (sys.version, sys.platform, cprt, self.__class__.__name__, self.do_py.__doc__))
		except EmbeddedConsoleExit:
			pass
		keepstate.restore()


@options(
	[make_option(
		'-s',   '--script',
		action  =   'store_true',
		help    =   'Script format; no separation lines'),],
	arg_desc = '(limit on which commands to include)')
def do_history(self, arg, opts):
	'''
	history [arg]: lists past commands issued

	| no arg:           list all
	| arg is integer:   list one history item, by index
	| arg is string:    string search
	| /arg enclosed slashes/: regular expression search
	'''
	if arg:
		history = self.history.get(arg)
	else:
		history = self.history
	for hi in history:
		if opts.script:
			self.poutput(hi)
		else:
			self.stdout.write(hi.pr())


def do_list(self, arg):
	'''list [arg]: lists last command issued

	no arg                      ->  list most recent command
	arg is integer              ->  list one history item, by index
	a..b, a:b, a:, ..b          ->  list spans from a (or start) to b (or end)
	arg is string               ->  list all commands matching string search
	/arg enclosed in slashes/   ->  regular expression search
	'''
	try:
		history = self.history.span(arg or '-1')
	except IndexError:
		history = self.history.search(arg)
	for hi in history:
		self.poutput(hi.pr())

do_hi   = do_history
do_l    = do_list
do_li   = do_list


def do_ed(self, arg):
	'''
	ed:             edit most recent command in text editor
	ed [N]:         edit numbered command from history
	ed [filename]:  edit specified file name

	Commands are run after editor is closed.

	The editor defaults to the EDITOR environment variable.  You
	can also use `set edit <program-name>` to choose an editor.
	'''
	if not self.editor:
		raise EnvironmentError("Please use 'set editor' to specify your text editing program of choice.")
	filename = self.default_file_name
	if arg:
		try:
			buffer = self.last_matching(int(arg))
		except ValueError:
			filename    = arg
			buffer      = ''
	else:
		buffer = self.history[-1]

	if buffer:
		f = open(os.path.expanduser(filename), 'w')
		f.write(buffer or '')
		f.close()
	os.system('%s %s' % (self.editor, filename))
	self.do__load(filename)

do_edit = do_ed

saveparser = (pyparsing.Optional(pyparsing.Word(pyparsing.nums)^'*')("idx")   +
			  pyparsing.Optional(pyparsing.Word(legalChars + '/\\'))("fname") +
			  pyparsing.stringEnd)


def do_save(self, arg):
	'''
	`save [N] [filename.ext]`

	Saves command from history to file.

	| N     =>  Number of command (from history), or `*`;
	|           most recent command if omitted
	'''
	try:
		args = self.saveparser.parseString(arg)
	except pyparsing.ParseException:
		self.perror('Could not understand save target %s' % arg)
		raise SyntaxError(self.do_save.__doc__)

	fname = args.fname or self.default_file_name

	if args.idx == '*':
		saveme = '\n\n'.join(self.history[:])
	elif args.idx:
		saveme = self.history[int(args.idx)-1]
	else:
		saveme = self.history[-1]

	try:
		f = open(os.path.expanduser(fname), 'w')
		f.write(saveme)
		f.close()
		self.pfeedback('Saved to %s' % (fname))
	except Exception, e:
		self.perror('Error saving %s' % (fname))
		raise


def do_load(self, arg=None):
	'''
	Runs script of command(s) from a file or URL.
	'''
	if arg is None:
		targetname = self.default_file_name
	else:
		arg = arg.split(None, 1)
		targetname, args = arg[0], (arg[1:] or [''])[0].strip()
	try:
		target = self.read_file_or_url(targetname)
	except IOError, e:
		self.perror('Problem accessing script from %s: \n%s' % (targetname, e))
		return
	keepstate = Statekeeper(self, ('stdin','use_rawinput','prompt',
								   'continuation_prompt','current_script_dir'))
	self.stdin = target
	self.use_rawinput = False
	self.prompt = self.continuation_prompt = ''
	self.current_script_dir = os.path.split(targetname)[0]
	stop = self._cmdloop()
	self.stdin.close()
	keepstate.restore()
	self.lastcmd = ''
	return stop and (stop != self._STOP_SCRIPT_NO_EXIT)

do__load = do_load  # avoid an unfortunate legacy use of do_load from sqlpython

def do_run(self, arg):
	'''
	run [arg]: re-runs an earlier command

	no arg                      -> run most recent command
	arg is integer              -> run one history item, by index
	arg is string               -> run most recent command by string search
	/arg enclosed in slashes/   -> run most recent by regex
	'''
	'run [N]: runs the SQL that was run N commands ago'
	runme = self.last_matching(arg)
	self.pfeedback(runme)
	if runme:
		stop = self.onecmd_plus_hooks(runme)

do_r = do_run