|
1 # Copyright 2000-2004 Michael Hudson mwh@python.net |
|
2 # |
|
3 # All Rights Reserved |
|
4 # |
|
5 # Portions Copyright (c) 2005 Nokia Corporation |
|
6 # |
|
7 # Permission to use, copy, modify, and distribute this software and |
|
8 # its documentation for any purpose is hereby granted without fee, |
|
9 # provided that the above copyright notice appear in all copies and |
|
10 # that both that copyright notice and this permission notice appear in |
|
11 # supporting documentation. |
|
12 # |
|
13 # THE AUTHOR MICHAEL HUDSON DISCLAIMS ALL WARRANTIES WITH REGARD TO |
|
14 # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
|
15 # AND FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, |
|
16 # INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER |
|
17 # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF |
|
18 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|
19 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
20 |
|
21 # one impressive collections of imports: |
|
22 from pyrepl.completing_reader import CompletingReader |
|
23 from pyrepl.historical_reader import HistoricalReader |
|
24 from pyrepl import completing_reader, reader |
|
25 from pyrepl import commands, completer |
|
26 from pyrepl import module_lister |
|
27 import sys, os, re, code, atexit |
|
28 import warnings |
|
29 |
|
30 HISTORYFILE=u"c:\\pythonhistory.txt" |
|
31 |
|
32 try: |
|
33 import cPickle as pickle |
|
34 except ImportError: |
|
35 try: |
|
36 import pickle |
|
37 except ImportError: |
|
38 try: |
|
39 import dumbpickle as pickle |
|
40 except ImportError: |
|
41 print "No pickle module -> command history will not be saved." |
|
42 pickle=None |
|
43 |
|
44 CommandCompiler = code.CommandCompiler |
|
45 |
|
46 def eat_it(*args): |
|
47 """this function eats warnings, if you were wondering""" |
|
48 pass |
|
49 |
|
50 class maybe_accept(commands.Command): |
|
51 def do(self): |
|
52 r = self.reader |
|
53 text = r.get_unicode() |
|
54 try: |
|
55 # ooh, look at the hack: |
|
56 code = r.compiler("#coding:utf-8\n"+text.encode('utf-8')) |
|
57 except (OverflowError, SyntaxError, ValueError): |
|
58 self.finish = 1 |
|
59 else: |
|
60 if code is None: |
|
61 r.insert("\n") |
|
62 else: |
|
63 self.finish = 1 |
|
64 |
|
65 from_line_prog = re.compile( |
|
66 "^from\s+(?P<mod>[A-Za-z_.0-9]*)\s+import\s+(?P<name>[A-Za-z_.0-9]*)") |
|
67 import_line_prog = re.compile( |
|
68 "^(?:import|from)\s+(?P<mod>[A-Za-z_.0-9]*)\s*$") |
|
69 |
|
70 def mk_saver(reader): |
|
71 def saver(reader=reader): |
|
72 try: |
|
73 file = open(HISTORYFILE, "w") |
|
74 except IOError: |
|
75 pass |
|
76 else: |
|
77 pickle.dump(reader.history, file) |
|
78 file.close() |
|
79 return saver |
|
80 |
|
81 def prhelp(): |
|
82 print ''' |
|
83 This is pyrepl, a simple multiline editor with command history, tab |
|
84 completion and emacs-like key bindings. |
|
85 |
|
86 Most useful commands: |
|
87 (C-x means Control-x, M-x means Alt-x or Esc x) |
|
88 |
|
89 C-p, C-n previous/next history item |
|
90 C-o accept line and take next line from history |
|
91 (handy for repeating sequences of commands) |
|
92 M-r restore line to what it was when retrieved |
|
93 from history |
|
94 C-r, C-s incremental search back/forward |
|
95 C-a, C-e beginning/end of line |
|
96 M-f, M-b next/previous word |
|
97 C-k cut to end of line |
|
98 C-w, M-d cut word to left/right of cursor |
|
99 C-y paste |
|
100 M-y paste older (use after C-y) |
|
101 M-return insert literal Return |
|
102 C-d delete character/quit interpreter |
|
103 C-xC-s save history to file (also saved on graceful exit) |
|
104 tab complete item (attribute, method, variable...) |
|
105 ''' |
|
106 |
|
107 class PythonicReader(CompletingReader, HistoricalReader): |
|
108 def collect_keymap(self): |
|
109 return super(PythonicReader, self).collect_keymap() + ( |
|
110 (r'\r', 'maybe-accept'), |
|
111 (r'\M-\r', 'insert-nl'), |
|
112 (r'\n', 'maybe-accept'), |
|
113 (r'\M-\n', 'insert-nl')) |
|
114 |
|
115 def __init__(self, console, locals, |
|
116 compiler=None): |
|
117 super(PythonicReader, self).__init__(console) |
|
118 self.completer = completer.Completer(locals) |
|
119 st = self.syntax_table |
|
120 for c in "._0123456789": |
|
121 st[c] = reader.SYNTAX_WORD |
|
122 self.locals = locals |
|
123 self.locals['prhelp']=prhelp |
|
124 if compiler is None: |
|
125 self.compiler = CommandCompiler() |
|
126 else: |
|
127 self.compiler = compiler |
|
128 try: |
|
129 file = open(HISTORYFILE) |
|
130 except IOError: |
|
131 pass |
|
132 else: |
|
133 try: |
|
134 self.history = pickle.load(file) |
|
135 except: |
|
136 self.history = [] |
|
137 self.historyi = len(self.history) |
|
138 file.close() |
|
139 self.save_history=mk_saver(self) |
|
140 atexit.register(self.save_history) |
|
141 for c in [maybe_accept]: |
|
142 self.commands[c.__name__] = c |
|
143 self.commands[c.__name__.replace('_', '-')] = c |
|
144 |
|
145 def get_completions(self, stem): |
|
146 b = self.get_unicode() |
|
147 m = import_line_prog.match(b) |
|
148 if m: |
|
149 mod = m.group("mod") |
|
150 try: |
|
151 return module_lister.find_modules(mod) |
|
152 except ImportError: |
|
153 pass |
|
154 m = from_line_prog.match(b) |
|
155 if m: |
|
156 mod, name = m.group("mod", "name") |
|
157 try: |
|
158 l = module_lister._packages[mod] |
|
159 except KeyError: |
|
160 try: |
|
161 mod = __import__(mod, self.locals, self.locals, ['']) |
|
162 return [x for x in dir(mod) if x.startswith(name)] |
|
163 except ImportError: |
|
164 pass |
|
165 else: |
|
166 return [x[len(mod) + 1:] |
|
167 for x in l if x.startswith(mod + '.' + name)] |
|
168 try: |
|
169 l = completing_reader.uniqify(self.completer.complete(stem)) |
|
170 return l |
|
171 except (NameError, AttributeError): |
|
172 return [] |
|
173 |
|
174 class ReaderConsole(code.InteractiveInterpreter): |
|
175 II_init = code.InteractiveInterpreter.__init__ |
|
176 def __init__(self, console, locals=None): |
|
177 self.II_init(locals) |
|
178 self.compiler = CommandCompiler() |
|
179 self.compile = self.compiler.compiler |
|
180 self.reader = PythonicReader(console, locals, self.compiler) |
|
181 self.locals['Reader'] = self.reader |
|
182 |
|
183 def run_user_init_file(self): |
|
184 for key in "PYREPLSTARTUP", "PYTHONSTARTUP": |
|
185 initfile = os.environ.get(key) |
|
186 if initfile is not None and os.path.exists(initfile): |
|
187 break |
|
188 else: |
|
189 return |
|
190 try: |
|
191 execfile(initfile, self.locals, self.locals) |
|
192 except: |
|
193 etype, value, tb = sys.exc_info() |
|
194 import traceback |
|
195 traceback.print_exception(etype, value, tb.tb_next) |
|
196 |
|
197 def execute(self, text): |
|
198 try: |
|
199 # ooh, look at the hack: |
|
200 code = self.compile("# coding:utf8\n"+text.encode('utf-8'), |
|
201 '<input>', 'single') |
|
202 except (OverflowError, SyntaxError, ValueError): |
|
203 self.showsyntaxerror("<input>") |
|
204 else: |
|
205 self.runcode(code) |
|
206 sys.stdout.flush() |
|
207 |
|
208 def interact(self): |
|
209 while 1: |
|
210 try: |
|
211 try: # catches EOFError's and KeyboardInterrupts during execution |
|
212 try: # catches KeyboardInterrupts during editing |
|
213 try: # warning saver |
|
214 # can't have warnings spewed onto terminal |
|
215 sv = warnings.showwarning |
|
216 warnings.showwarning = eat_it |
|
217 l = unicode(self.reader.readline(), 'utf-8') |
|
218 finally: |
|
219 warnings.showwarning = sv |
|
220 except KeyboardInterrupt: |
|
221 print "KeyboardInterrupt" |
|
222 else: |
|
223 if l: |
|
224 self.execute(l) |
|
225 except EOFError: |
|
226 break |
|
227 except KeyboardInterrupt: |
|
228 continue |
|
229 finally: |
|
230 self.reader.save_history() |
|
231 |
|
232 def prepare(self): |
|
233 self.sv_sw = warnings.showwarning |
|
234 warnings.showwarning = eat_it |
|
235 self.reader.prepare() |
|
236 self.reader.refresh() # we want :after methods... |
|
237 |
|
238 def restore(self): |
|
239 self.reader.restore() |
|
240 warnings.showwarning = self.sv_sw |
|
241 |
|
242 def handle1(self, block=1): |
|
243 try: |
|
244 r = 1 |
|
245 r = self.reader.handle1(block) |
|
246 except KeyboardInterrupt: |
|
247 self.restore() |
|
248 print "KeyboardInterrupt" |
|
249 self.prepare() |
|
250 else: |
|
251 if self.reader.finished: |
|
252 text = self.reader.get_unicode() |
|
253 self.restore() |
|
254 if text: |
|
255 self.execute(text) |
|
256 self.prepare() |
|
257 return r |
|
258 |
|
259 |