1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 """The prompt based relax user interface (UI)."""
25
26
27 import dep_check
28
29
30 from code import InteractiveConsole
31 from lib import ansi
32 from math import pi
33 from os import chdir, getcwd, path
34 from pydoc import pager
35 from re import search
36 if dep_check.readline_module:
37 import readline
38 if dep_check.runpy_module:
39 import runpy
40 import sys
41
42
43 from info import Info_box
44 from prompt.command import Ls, Lh, Ll, system
45 from prompt.help import _Helper, _Helper_python
46 if dep_check.readline_module:
47 from prompt.tab_completion import Tab_completion
48 from prompt.uf_objects import Class_container, Uf_object
49 from lib.errors import AllRelaxErrors, RelaxError
50 from status import Status; status = Status()
51 from user_functions.data import Uf_info; uf_info = Uf_info()
52
53
54
55
56
57
58 PS1_ORIG = 'relax> '
59 PS2_ORIG = 'relax| '
60 PS3_ORIG = '\n%s' % PS1_ORIG
61
62
63 PS1_COLOUR = "%s%s%s" % (ansi.relax_prompt, PS1_ORIG, ansi.end)
64 PS2_COLOUR = "%s%s%s" % (ansi.relax_prompt, PS2_ORIG, ansi.end)
65 PS3_COLOUR = "\n%s%s%s" % (ansi.relax_prompt, PS1_ORIG, ansi.end)
66
67
69 - def __init__(self, show_script=True, raise_relax_error=False):
70 """The interpreter class.
71
72 @param show_script: If true, the relax will print the script contents prior to
73 executing the script.
74 @type show_script: bool
75 @param raise_relax_error: If false, the default, then relax will print a nice error
76 message to STDERR, without a traceback, when a RelaxError
77 occurs. This is to make things nicer for the user.
78 @type raise_relax_error: bool
79 """
80
81
82 self.__show_script = show_script
83 self.__raise_relax_error = raise_relax_error
84
85
86 info = Info_box()
87 self.__intro_string = info.intro_text()
88
89
90 if ansi.enable_control_chars(stream=1):
91 self.prompt_colour_on()
92 else:
93 self.prompt_colour_off()
94
95
96 self._locals = self._setup()
97
98
100 """Private method for executing the given user function.
101
102 @keyword uf_name: The name of the user function.
103 @type uf_name: str
104 """
105
106
107 if 'uf_name' not in kargs:
108 raise RelaxError("The user function name argument 'uf_name' has not been supplied.")
109
110
111 uf_name = kargs.pop('uf_name')
112
113
114 if search('\.', uf_name):
115 class_name, uf_name = uf_name.split('.')
116 else:
117 class_name = None
118
119
120 if class_name:
121 class_obj = self._locals[class_name]
122
123
124 if class_name:
125 uf = getattr(class_obj, uf_name)
126 else:
127 uf = self._locals[uf_name]
128
129
130 uf(*args, **kargs)
131
132
134 """Set up all the interpreter objects.
135
136 All objects are initialised and placed in a dictionary. These will be later placed in different namespaces such as the run() method local namespace. All the user functions and classes will be auto-generated.
137
138 @return: The dictionary of interpreter objects.
139 @rtype: dict
140 """
141
142
143 objects = {}
144
145
146 objects['pi'] = pi
147
148
149 objects['lh'] = Lh()
150 objects['ll'] = Ll()
151 objects['ls'] = Ls()
152 objects['system'] = system
153
154
155 objects['gpl'] = objects['GPL'] = GPL()
156
157
158 objects['intro_off'] = self.off
159 objects['intro_on'] = self.on
160 objects['exit'] = objects['bye'] = objects['quit'] = objects['q'] = _Exit()
161
162
163 objects['help_python'] = _Helper_python()
164 objects['help'] = _Helper()
165
166
167 for name, data in uf_info.class_loop():
168
169 obj = Class_container(name, data.title)
170
171
172 objects[name] = obj
173
174
175 self._uf_dict = {}
176 for name, data in uf_info.uf_loop():
177
178 if search('\.', name):
179 class_name, uf_name = name.split('.')
180 else:
181 class_name = None
182
183
184 obj = Uf_object(name, title=data.title, kargs=data.kargs, backend=data.backend, desc=data.desc)
185
186
187 if class_name:
188 class_obj = objects[class_name]
189
190
191 if class_name:
192 setattr(class_obj, uf_name, obj)
193 else:
194 objects[name] = obj
195
196
197 self._uf_dict[name] = obj
198
199
200 return objects
201
202
203 - def off(self, verbose=True):
204 """Turn the user function introductions off."""
205
206 status.uf_intro = False
207
208
209 if verbose:
210 print("Echoing of user function calls has been disabled.")
211
212
213 - def on(self, verbose=True):
214 """Turn the user function introductions on."""
215
216 status.uf_intro = True
217
218
219 if verbose:
220 print("Echoing of user function calls has been enabled.")
221
222
224 """Place all special objects into self."""
225
226
227 for name in self._locals.keys():
228 setattr(self, name, self._locals[name])
229
230
237
238
245
246
247 - def run(self, script_file=None):
248 """Run the python interpreter.
249
250 The namespace of this function is the namespace seen inside the interpreter. All user
251 accessible functions, classes, etc, should be placed in this namespace.
252
253
254 @param script_file: The script file to be executed. For the interpreter mode, this
255 should be left as None.
256 @type script_file: None or str
257 """
258
259
260 for name in self._locals.keys():
261 locals()[name] = self._locals[name]
262
263
264 if dep_check.readline_module:
265 readline.set_completer(Tab_completion(name_space=locals()).finish)
266 readline.set_completer_delims(' \t\n`~!@#$%^&*()=+{}\\|;:",<>/?')
267 readline.parse_and_bind("tab: complete")
268
269
270 if script_file and not status.prompt:
271
272 status.uf_intro = True
273
274
275 return run_script(intro=self.__intro_string, local=locals(), script_file=script_file, show_script=self.__show_script, raise_relax_error=self.__raise_relax_error)
276
277
278 if script_file and status.prompt:
279
280 status.uf_intro = True
281
282
283 run_script(intro=self.__intro_string, local=locals(), script_file=script_file, show_script=self.__show_script, raise_relax_error=self.__raise_relax_error)
284
285
286 status.uf_intro = False
287
288
289 prompt(intro=None, local=locals())
290
291
292 else:
293 prompt(intro=self.__intro_string, local=locals())
294
295
296
299 """Exit the program."""
300
301 print("Exiting the program.")
302 sys.exit()
303
304
306 """A special object for displaying the GPL license."""
307
309 """Replacement representation."""
310
311
312 file = open('docs/COPYING')
313 pager(file.read())
314
315
316 return "The GNU General Public License."
317
318
319
321 """Execute the script."""
322
323
324 status.exec_lock.acquire('script UI', mode='script')
325
326
327 head, tail = path.split(name)
328 script_path = path.join(getcwd(), head)
329 sys.path.append(script_path)
330
331
332 if head:
333 orig_dir = getcwd()
334 chdir(head)
335
336
337 module, ext = path.splitext(tail)
338
339
340 if search('\.', module):
341 raise RelaxError("The relax script must not contain the '.' character (except before the extension '*.py').")
342 if ext != '.py':
343 raise RelaxError("The script must have the extension *.py.")
344
345
346 try:
347
348 sys.path.reverse()
349
350
351 if dep_check.runpy_module:
352 runpy.run_module(module, globals)
353
354
355 else:
356 exec(compile(open(name).read(), name, 'exec'), globals)
357
358 finally:
359
360 if head:
361 chdir(orig_dir)
362
363
364 sys.path.reverse()
365 sys.path.pop(sys.path.index(script_path))
366
367
368 status.exec_lock.release()
369
370
372 """Replacement function for 'code.InteractiveConsole.interact'.
373
374 This will enter into the prompt.
375
376 @param intro: The string to print prior to jumping to the prompt mode.
377 @type intro: str
378 @param local: A namespace which will become that of the prompt (i.e. the namespace visible to
379 the user when in the prompt mode). This should be the output of a function such
380 as locals().
381 @type local: dict
382 """
383
384
385 if intro:
386 sys.stdout.write("%s\n" % intro)
387
388
389
390
391
392 more = False
393 while True:
394 try:
395 if more:
396 prompt = sys.ps2
397 else:
398 prompt = sys.ps1
399 try:
400 line = self.raw_input(prompt)
401 except EOFError:
402 self.write("\n")
403 break
404 else:
405 more = self.push(line)
406 except KeyboardInterrupt:
407 self.write("\nKeyboardInterrupt\n")
408 self.resetbuffer()
409 more = False
410
411
412 -def interact_script(self, intro=None, local={}, script_file=None, show_script=True, raise_relax_error=False):
413 """Replacement function for 'code.InteractiveConsole.interact'.
414
415 This will execute the script file.
416
417
418 @param intro: The string to print prior to jumping to the prompt mode.
419 @type intro: str
420 @param local: A namespace which will become that of the prompt (i.e. the namespace
421 visible to the user when in the prompt mode). This should be the
422 output of a function such as locals().
423 @type local: dict
424 @param script_file: The script file to be executed.
425 @type script_file: None or str
426 @param show_script: If true, the relax will print the script contents prior to executing
427 the script.
428 @type show_script: bool
429 @param raise_relax_error: If false, the default, then a nice error message will be sent to
430 STDERR, without a traceback, when a RelaxError occurs. This is to
431 make things nicer for the user.
432 @type raise_relax_error: bool
433 """
434
435
436 if intro:
437 sys.stdout.write("%s\n" % intro)
438
439
440 if show_script:
441 try:
442 file = open(script_file, 'r')
443 except IOError:
444 try:
445 raise RelaxError("The script file '" + script_file + "' does not exist.")
446 except AllRelaxErrors:
447 instance = sys.exc_info()[1]
448 sys.stdout.write(instance.__str__())
449 sys.stdout.write("\n")
450 return
451
452
453 if ansi.enable_control_chars(stream=1):
454 sys.stdout.write(ansi.script)
455
456
457 sys.stdout.write("script = " + repr(script_file) + "\n")
458 sys.stdout.write("----------------------------------------------------------------------------------------------------\n")
459 sys.stdout.write(file.read())
460 sys.stdout.write("----------------------------------------------------------------------------------------------------")
461
462
463 if ansi.enable_control_chars(stream=1):
464 sys.stdout.write(ansi.end)
465
466
467 sys.stdout.write("\n")
468
469
470 file.close()
471
472
473 exec_pass = True
474
475
476 try:
477 exec_script(script_file, local)
478
479
480 except KeyboardInterrupt:
481
482 if status.debug:
483 raise
484
485
486 else:
487 sys.stderr.write("\nScript execution cancelled.\n")
488
489
490 exec_pass = False
491
492
493 except AllRelaxErrors:
494 instance = sys.exc_info()[1]
495
496
497 if raise_relax_error:
498 raise
499
500
501 else:
502
503 if status.debug:
504 self.showtraceback()
505
506
507 else:
508 sys.stderr.write(instance.__str__())
509
510
511 exec_pass = False
512
513
514 except:
515
516 raise
517
518
519 if show_script:
520 sys.stdout.write("\n")
521
522
523 return exec_pass
524
525
526 -def prompt(intro=None, local=None):
527 """Python interpreter emulation.
528
529 This function replaces 'code.interact'.
530
531
532 @param intro: The string to print prior to jumping to the prompt mode.
533 @type intro: str
534 @param local: A namespace which will become that of the prompt (i.e. the namespace visible to
535 the user when in the prompt mode). This should be the output of a function such
536 as locals().
537 @type local: dict
538 """
539
540
541 InteractiveConsole.interact = interact_prompt
542 InteractiveConsole.runcode = runcode
543
544
545 console = InteractiveConsole(local)
546 console.interact(intro, local)
547
548
549 -def run_script(intro=None, local=None, script_file=None, show_script=True, raise_relax_error=False):
550 """Python interpreter emulation.
551
552 This function replaces 'code.interact'.
553
554
555 @param intro: The string to print prior to jumping to the prompt mode.
556 @type intro: str
557 @param local: A namespace which will become that of the prompt (i.e. the namespace
558 visible to the user when in the prompt mode). This should be the
559 output of a function such as locals().
560 @type local: dict
561 @param script_file: The script file to be executed.
562 @type script_file: None or str
563 @param show_script: If true, the relax will print the script contents prior to executing
564 the script.
565 @type show_script: bool
566 @param raise_relax_error: If false, the default, then a nice error message will be sent to
567 STDERR, without a traceback, when a RelaxError occurs. This is to
568 make things nicer for the user.
569 @type raise_relax_error: bool
570 """
571
572
573 InteractiveConsole.interact = interact_script
574 InteractiveConsole.runcode = runcode
575
576
577 console = InteractiveConsole(local)
578 return console.interact(intro, local, script_file, show_script=show_script, raise_relax_error=raise_relax_error)
579
580
582 """Replacement code for code.InteractiveInterpreter.runcode.
583
584 @param code: The code to execute.
585 @type code: str
586 """
587
588 try:
589 exec(code, self.locals)
590 except SystemExit:
591 raise
592 except AllRelaxErrors:
593 instance = sys.exc_info()[1]
594 self.write(instance.__str__())
595 self.write("\n")
596 except:
597 self.showtraceback()
598