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