Mercurial > cpython-lru_cache-weakref
annotate Demo/stdwin/python.py @ 867:cb146aa987c9 legacy-trunk
Make it at least legal grammar!
| author | Guido van Rossum <guido@python.org> |
|---|---|
| date | Fri, 15 May 1992 15:40:30 +0000 |
| parents | 8f7b31a8a678 |
| children | 56636395724d |
| rev | line source |
|---|---|
| 752 | 1 #! /usr/local/python |
| 2 | |
| 3 # A STDWIN-based front end for the Python interpreter. | |
| 4 # | |
| 5 # This is useful if you want to avoid console I/O and instead | |
| 6 # use text windows to issue commands to the interpreter. | |
| 7 # | |
| 8 # It supports multiple interpreter windows, each with its own context. | |
| 9 # | |
| 10 # BUGS AND CAVEATS: | |
| 11 # | |
| 12 # Although this supports multiple windows, the whole application | |
| 13 # is deaf and dumb when a command is running in one window. | |
| 14 # | |
| 15 # Everything written to stdout or stderr is saved on a file which | |
| 16 # is inserted in the window at the next input request. | |
| 17 # | |
| 18 # On UNIX (using X11), interrupts typed in the window will not be | |
| 19 # seen until the next input request. (On the Mac, interrupts work.) | |
| 20 # | |
| 21 # Direct input from stdin should not be attempted. | |
| 22 | |
| 23 | |
| 24 import sys | |
| 25 import builtin | |
| 26 import stdwin | |
| 27 from stdwinevents import * | |
| 28 import rand | |
| 29 import mainloop | |
|
753
8f7b31a8a678
Adapt it to modern times (0.9.6)
Guido van Rossum <guido@python.org>
parents:
752
diff
changeset
|
30 import os |
| 752 | 31 |
| 32 | |
| 33 # Filename used to capture output from commands; change to suit your taste | |
| 34 # | |
| 35 OUTFILE = '@python.stdout.tmp' | |
| 36 | |
| 37 | |
| 38 # Stack of windows waiting for [raw_]input(). | |
| 39 # Element [0] is the top. | |
| 40 # If there are multiple windows waiting for input, only the | |
| 41 # one on top of the stack can accept input, because the way | |
| 42 # raw_input() is implemented (using recursive mainloop() calls). | |
| 43 # | |
| 44 inputwindows = [] | |
| 45 | |
| 46 | |
| 47 # Exception raised when input is available. | |
| 48 # | |
| 49 InputAvailable = 'input available for raw_input (not an error)' | |
| 50 | |
| 51 | |
| 52 # Main program. Create the window and call the mainloop. | |
| 53 # | |
| 54 def main(): | |
| 55 # Hack so 'import python' won't load another copy | |
| 56 # of this if we were loaded though 'python python.py'. | |
| 57 # (Should really look at sys.argv[0]...) | |
| 58 if 'inputwindows' in dir(sys.modules['__main__']) and \ | |
| 59 sys.modules['__main__'].inputwindows is inputwindows: | |
| 60 sys.modules['python'] = sys.modules['__main__'] | |
| 61 # | |
| 62 win = makewindow() | |
| 63 mainloop.mainloop() | |
| 64 | |
| 65 | |
| 66 # Create a new window. | |
| 67 # | |
| 68 def makewindow(): | |
| 69 # stdwin.setdefscrollbars(0, 1) # Not in Python 0.9.1 | |
| 70 # stdwin.setfont('monaco') # Not on UNIX! and not Python 0.9.1 | |
| 71 # stdwin.setdefwinsize(stdwin.textwidth('in')*40, stdwin.lineheight() * 24) | |
| 72 win = stdwin.open('Python interpreter ready') | |
| 73 win.editor = win.textcreate((0,0), win.getwinsize()) | |
| 74 win.outfile = OUTFILE + `rand.rand()` | |
| 75 win.globals = {} # Dictionary for user's global variables | |
| 76 win.command = '' # Partially read command | |
| 77 win.busy = 0 # Ready to accept a command | |
| 78 win.auto = 1 # [CR] executes command | |
| 79 win.insertOutput = 1 # Insert output at focus. | |
| 80 win.insertError = 1 # Insert error output at focus. | |
| 81 win.setwincursor('ibeam') | |
| 82 win.filename = '' # Empty if no file associated with this window | |
| 83 makefilemenu(win) | |
| 84 makeeditmenu(win) | |
| 85 win.dispatch = pdispatch # Event dispatch function | |
| 86 mainloop.register(win) | |
| 87 return win | |
| 88 | |
| 89 | |
| 90 # Make a 'File' menu | |
| 91 # | |
| 92 def makefilemenu(win): | |
| 93 win.filemenu = mp = win.menucreate('File') | |
| 94 mp.callback = [] | |
| 95 additem(mp, 'New', 'N', do_new) | |
| 96 additem(mp, 'Open...', 'O', do_open) | |
| 97 additem(mp, '', '', None) | |
| 98 additem(mp, 'Close', 'W', do_close) | |
| 99 additem(mp, 'Save', 'S', do_save) | |
| 100 additem(mp, 'Save as...', '', do_saveas) | |
| 101 additem(mp, '', '', None) | |
| 102 additem(mp, 'Quit', 'Q', do_quit) | |
| 103 | |
| 104 | |
| 105 # Make an 'Edit' menu | |
| 106 # | |
| 107 def makeeditmenu(win): | |
| 108 win.editmenu = mp = win.menucreate('Edit') | |
| 109 mp.callback = [] | |
| 110 additem(mp, 'Cut', 'X', do_cut) | |
| 111 additem(mp, 'Copy', 'C', do_copy) | |
| 112 additem(mp, 'Paste', 'V', do_paste) | |
| 113 additem(mp, 'Clear', '', do_clear) | |
| 114 additem(mp, '', '', None) | |
| 115 win.iauto = len(mp.callback) | |
| 116 additem(mp, 'Autoexecute', '', do_auto) | |
| 117 mp.check(win.iauto, win.auto) | |
| 118 win.insertOutputNum = len(mp.callback) | |
| 119 additem(mp, 'Insert Output', '', do_insertOutputOption) | |
| 120 win.insertErrorNum = len(mp.callback) | |
| 121 additem(mp, 'Insert Error', '', do_insertErrorOption) | |
| 122 additem(mp, 'Exec', '\r', do_exec) | |
| 123 | |
| 124 | |
| 125 # Helper to add a menu item and callback function | |
| 126 # | |
| 127 def additem(mp, text, shortcut, handler): | |
| 128 if shortcut: | |
| 129 mp.additem(text, shortcut) | |
| 130 else: | |
| 131 mp.additem(text) | |
| 132 mp.callback.append(handler) | |
| 133 | |
| 134 | |
| 135 # Dispatch a single event to the interpreter. | |
| 136 # Resize events cause a resize of the editor. | |
| 137 # Other events are directly sent to the editor. | |
| 138 # | |
| 139 # Exception: WE_COMMAND/WC_RETURN causes the current selection | |
| 140 # (if not empty) or current line (if empty) to be sent to the | |
| 141 # interpreter. (In the future, there should be a way to insert | |
| 142 # newlines in the text; or perhaps Enter or Meta-RETURN should be | |
| 143 # used to trigger execution, like in MPW, though personally I prefer | |
| 144 # using a plain Return to trigger execution, as this is what I want | |
| 145 # in the majority of cases.) | |
| 146 # | |
| 147 # Also, WE_COMMAND/WC_CANCEL cancels any command in progress. | |
| 148 # | |
| 149 def pdispatch(event): | |
| 150 type, win, detail = event | |
| 151 if type == WE_CLOSE: | |
| 152 do_close(win) | |
| 153 elif type == WE_SIZE: | |
| 154 win.editor.move((0, 0), win.getwinsize()) | |
| 155 elif type == WE_COMMAND and detail == WC_RETURN: | |
| 156 if win.auto: | |
| 157 do_exec(win) | |
| 158 else: | |
| 159 void = win.editor.event(event) | |
| 160 elif type == WE_COMMAND and detail == WC_CANCEL: | |
| 161 if win.busy: | |
| 162 raise InputAvailable, (EOFError, None) | |
| 163 else: | |
| 164 win.command = '' | |
| 165 settitle(win) | |
| 166 elif type == WE_MENU: | |
| 167 mp, item = detail | |
| 168 mp.callback[item](win) | |
| 169 else: | |
| 170 void = win.editor.event(event) | |
| 171 if win.editor: | |
| 172 # May have been deleted by close... | |
| 173 win.setdocsize(0, win.editor.getrect()[1][1]) | |
| 174 if type in (WE_CHAR, WE_COMMAND): | |
| 175 win.editor.setfocus(win.editor.getfocus()) | |
| 176 | |
| 177 | |
| 178 # Helper to set the title of the window. | |
| 179 # | |
| 180 def settitle(win): | |
| 181 if win.filename == '': | |
| 182 win.settitle('Python interpreter ready') | |
| 183 else: | |
| 184 win.settitle(win.filename) | |
| 185 | |
| 186 | |
| 187 # Helper to replace the text of the focus. | |
| 188 # | |
| 189 def replace(win, text): | |
| 190 win.editor.replace(text) | |
| 191 # Resize the window to display the text | |
| 192 win.setdocsize(0, win.editor.getrect()[1][1]) # update the size before.. | |
| 193 win.editor.setfocus(win.editor.getfocus()) # move focus to the change - dml | |
| 194 | |
| 195 | |
| 196 # File menu handlers | |
| 197 # | |
| 198 def do_new(win): | |
| 199 win = makewindow() | |
| 200 # | |
| 201 def do_open(win): | |
| 202 try: | |
| 203 filename = stdwin.askfile('Open file', '', 0) | |
| 204 win = makewindow() | |
| 205 win.filename = filename | |
|
753
8f7b31a8a678
Adapt it to modern times (0.9.6)
Guido van Rossum <guido@python.org>
parents:
752
diff
changeset
|
206 win.editor.replace(open(filename, 'r').read()) |
| 752 | 207 win.editor.setfocus(0, 0) |
| 208 win.settitle(win.filename) | |
| 209 # | |
| 210 except KeyboardInterrupt: | |
| 211 pass # Don't give an error on cancel. | |
| 212 # | |
| 213 def do_save(win): | |
| 214 try: | |
| 215 if win.filename == '': | |
| 216 win.filename = stdwin.askfile('Open file', '', 1) | |
| 217 f = open(win.filename, 'w') | |
| 218 f.write(win.editor.gettext()) | |
| 219 # | |
| 220 except KeyboardInterrupt: | |
| 221 pass # Don't give an error on cancel. | |
| 222 | |
| 223 def do_saveas(win): | |
| 224 currentFilename = win.filename | |
| 225 win.filename = '' | |
| 226 do_save(win) # Use do_save with empty filename | |
| 227 if win.filename == '': # Restore the name if do_save did not set it. | |
| 228 win.filename = currentFilename | |
| 229 # | |
| 230 def do_close(win): | |
| 231 if win.busy: | |
| 232 stdwin.message('Can\'t close busy window') | |
| 233 return # need to fail if quitting?? | |
| 234 win.editor = None # Break circular reference | |
| 235 #del win.editmenu # What about the filemenu?? | |
| 236 try: | |
| 237 os.unlink(win.outfile) | |
| 238 except os.error: | |
| 239 pass | |
| 240 mainloop.unregister(win) | |
| 241 # | |
| 242 def do_quit(win): | |
| 243 # Call win.dispatch instead of do_close because there | |
| 244 # may be 'alien' windows in the list. | |
| 245 for win in mainloop.windows: | |
| 246 mainloop.dispatch(WE_CLOSE, win, None) # need to catch failed close | |
| 247 | |
| 248 | |
| 249 # Edit menu handlers | |
| 250 # | |
| 251 def do_cut(win): | |
| 252 text = win.editor.getfocustext() | |
| 253 if not text: | |
| 254 stdwin.fleep() | |
| 255 return | |
| 256 stdwin.setcutbuffer(0, text) | |
| 257 replace(win, '') | |
| 258 # | |
| 259 def do_copy(win): | |
| 260 text = win.editor.getfocustext() | |
| 261 if not text: | |
| 262 stdwin.fleep() | |
| 263 return | |
| 264 stdwin.setcutbuffer(0, text) | |
| 265 # | |
| 266 def do_paste(win): | |
| 267 text = stdwin.getcutbuffer(0) | |
| 268 if not text: | |
| 269 stdwin.fleep() | |
| 270 return | |
| 271 replace(win, text) | |
| 272 # | |
| 273 def do_clear(win): | |
| 274 replace(win, '') | |
| 275 | |
| 276 # | |
| 277 # These would be better in a preferences dialog: | |
| 278 def do_auto(win): | |
| 279 win.auto = (not win.auto) | |
| 280 win.editmenu.check(win.iauto, win.auto) | |
| 281 # | |
| 282 def do_insertOutputOption(win): | |
| 283 win.insertOutput = (not win.insertOutput) | |
| 284 title = ['Append Output', 'Insert Output'][win.insertOutput] | |
| 285 win.editmenu.setitem(win.insertOutputNum, title) | |
| 286 # | |
| 287 def do_insertErrorOption(win): | |
| 288 win.insertError = (not win.insertError) | |
| 289 title = ['Error Dialog', 'Insert Error'][win.insertError] | |
| 290 win.editmenu.setitem(win.insertErrorNum, title) | |
| 291 | |
| 292 | |
| 293 # Extract a command from the editor and execute it, or pass input to | |
| 294 # an interpreter waiting for it. | |
| 295 # Incomplete commands are merely placed in the window's command buffer. | |
| 296 # All exceptions occurring during the execution are caught and reported. | |
| 297 # (Tracebacks are currently not possible, as the interpreter does not | |
| 298 # save the traceback pointer until it reaches its outermost level.) | |
| 299 # | |
| 300 def do_exec(win): | |
| 301 if win.busy: | |
| 302 if win not in inputwindows: | |
| 303 stdwin.message('Can\'t run recursive commands') | |
| 304 return | |
| 305 if win <> inputwindows[0]: | |
| 306 stdwin.message( \ | |
| 307 'Please complete recursive input first') | |
| 308 return | |
| 309 # | |
| 310 # Set text to the string to execute. | |
| 311 a, b = win.editor.getfocus() | |
| 312 alltext = win.editor.gettext() | |
| 313 n = len(alltext) | |
| 314 if a == b: | |
| 315 # There is no selected text, just an insert point; | |
| 316 # so execute the current line. | |
| 317 while 0 < a and alltext[a-1] <> '\n': a = a-1 # Find beginning of line. | |
| 318 while b < n and alltext[b] <> '\n': # Find end of line after b. | |
| 319 b = b+1 | |
| 320 text = alltext[a:b] + '\n' | |
| 321 else: | |
| 322 # Execute exactly the selected text. | |
| 323 text = win.editor.getfocustext() | |
| 324 if text[-1:] <> '\n': # Make sure text ends with newline. | |
| 325 text = text + '\n' | |
| 326 while b < n and alltext[b] <> '\n': # Find end of line after b. | |
| 327 b = b+1 | |
| 328 # | |
| 329 # Set the focus to expect the output, since there is always something. | |
| 330 # Output will be inserted at end of line after current focus, | |
| 331 # or appended to the end of the text. | |
| 332 b = [n, b][win.insertOutput] | |
| 333 win.editor.setfocus(b, b) | |
| 334 # | |
| 335 # Make sure there is a preceeding newline. | |
| 336 if alltext[b-1:b] <> '\n': | |
| 337 win.editor.replace('\n') | |
| 338 # | |
| 339 # | |
| 340 if win.busy: | |
| 341 # Send it to raw_input() below | |
| 342 raise InputAvailable, (None, text) | |
| 343 # | |
| 344 # Like the real Python interpreter, we want to execute | |
| 345 # single-line commands immediately, but save multi-line | |
| 346 # commands until they are terminated by a blank line. | |
| 347 # Unlike the real Python interpreter, we don't do any syntax | |
| 348 # checking while saving up parts of a multi-line command. | |
| 349 # | |
| 350 # The current heuristic to determine whether a command is | |
| 351 # the first line of a multi-line command simply checks whether | |
| 352 # the command ends in a colon (followed by a newline). | |
| 353 # This is not very robust (comments and continuations will | |
| 354 # confuse it), but it is usable, and simple to implement. | |
| 355 # (It even has the advantage that single-line loops etc. | |
| 356 # don't need te be terminated by a blank line.) | |
| 357 # | |
| 358 if win.command: | |
| 359 # Already continuing | |
| 360 win.command = win.command + text | |
| 361 if win.command[-2:] <> '\n\n': | |
| 362 win.settitle('Unfinished command...') | |
| 363 return # Need more... | |
| 364 else: | |
| 365 # New command | |
| 366 win.command = text | |
| 367 if text[-2:] == ':\n': | |
| 368 win.settitle('Unfinished command...') | |
| 369 return | |
| 370 command = win.command | |
| 371 win.command = '' | |
| 372 win.settitle('Executing command...') | |
| 373 # | |
| 374 # Some hacks: sys.stdout is temporarily redirected to a file, | |
| 375 # so we can intercept the command's output and insert it | |
| 376 # in the editor window; the built-in function raw_input | |
| 377 # and input() are replaced by out versions; | |
| 378 # and a second, undocumented argument | |
| 379 # to exec() is used to specify the directory holding the | |
| 380 # user's global variables. (If this wasn't done, the | |
| 381 # exec would be executed in the current local environment, | |
| 382 # and the user's assignments to globals would be lost...) | |
| 383 # | |
| 384 save_input = builtin.input | |
| 385 save_raw_input = builtin.raw_input | |
| 386 save_stdout = sys.stdout | |
| 387 save_stderr = sys.stderr | |
| 388 iwin = Input().init(win) | |
| 389 try: | |
| 390 builtin.input = iwin.input | |
| 391 builtin.raw_input = iwin.raw_input | |
| 392 sys.stdout = sys.stderr = open(win.outfile, 'w') | |
| 393 win.busy = 1 | |
| 394 try: | |
| 395 exec(command, win.globals) | |
| 396 except KeyboardInterrupt: | |
| 397 pass # Don't give an error. | |
| 398 except: | |
| 399 msg = sys.exc_type | |
| 400 if sys.exc_value <> None: | |
| 401 msg = msg + ': ' + `sys.exc_value` | |
| 402 if win.insertError: | |
| 403 stdwin.fleep() | |
| 404 replace(win, msg + '\n') | |
| 405 else: | |
| 406 win.settitle('Unhandled exception') | |
| 407 stdwin.message(msg) | |
| 408 finally: | |
| 409 # Restore redirected I/O in *all* cases | |
| 410 win.busy = 0 | |
| 411 sys.stderr = save_stderr | |
| 412 sys.stdout = save_stdout | |
| 413 builtin.raw_input = save_raw_input | |
| 414 builtin.input = save_input | |
| 415 settitle(win) | |
| 416 getoutput(win) | |
| 417 | |
| 418 | |
| 419 # Read any output the command may have produced back from the file | |
| 420 # and show it. Optionally insert it after the focus, like MPW does, | |
| 421 # or always append at the end. | |
| 422 # | |
| 423 def getoutput(win): | |
| 424 filename = win.outfile | |
| 425 try: | |
| 426 fp = open(filename, 'r') | |
| 427 except: | |
| 428 stdwin.message('Can\'t read output from ' + filename) | |
| 429 return | |
| 430 #out = fp.read() # Not in Python 0.9.1 | |
| 431 out = fp.read(10000) # For Python 0.9.1 | |
| 432 del fp # Close it | |
| 433 if out or win.insertOutput: | |
| 434 replace(win, out) | |
| 435 | |
| 436 | |
| 437 # Implementation of input() and raw_input(). | |
| 438 # This uses a class only because we must support calls | |
| 439 # with and without arguments; this can't be done normally in Python, | |
| 440 # but the extra, implicit argument for instance methods does the trick. | |
| 441 # | |
| 442 class Input: | |
| 443 # | |
| 444 def init(self, win): | |
| 445 self.win = win | |
| 446 return self | |
| 447 # | |
| 448 def input(args): | |
| 449 # Hack around call with or without argument: | |
| 450 if type(args) == type(()): | |
| 451 self, prompt = args | |
| 452 else: | |
| 453 self, prompt = args, '' | |
| 454 # | |
| 455 return eval(self.raw_input(prompt), self.win.globals) | |
| 456 # | |
| 457 def raw_input(args): | |
| 458 # Hack around call with or without argument: | |
| 459 if type(args) == type(()): | |
| 460 self, prompt = args | |
| 461 else: | |
| 462 self, prompt = args, '' | |
| 463 # | |
| 464 print prompt # Need to terminate with newline. | |
| 465 sys.stdout.close() | |
| 466 sys.stdout = sys.stderr = None | |
| 467 getoutput(self.win) | |
| 468 sys.stdout = sys.stderr = open(self.win.outfile, 'w') | |
| 469 save_title = self.win.gettitle() | |
| 470 n = len(inputwindows) | |
| 471 title = n*'(' + 'Requesting input...' + ')'*n | |
| 472 self.win.settitle(title) | |
| 473 inputwindows.insert(0, self.win) | |
| 474 try: | |
|
867
cb146aa987c9
Make it at least legal grammar!
Guido van Rossum <guido@python.org>
parents:
753
diff
changeset
|
475 try: |
|
cb146aa987c9
Make it at least legal grammar!
Guido van Rossum <guido@python.org>
parents:
753
diff
changeset
|
476 mainloop.mainloop() |
|
cb146aa987c9
Make it at least legal grammar!
Guido van Rossum <guido@python.org>
parents:
753
diff
changeset
|
477 except InputAvailable, (exc, val): # See do_exec above. |
|
cb146aa987c9
Make it at least legal grammar!
Guido van Rossum <guido@python.org>
parents:
753
diff
changeset
|
478 if exc: |
|
cb146aa987c9
Make it at least legal grammar!
Guido van Rossum <guido@python.org>
parents:
753
diff
changeset
|
479 raise exc, val |
|
cb146aa987c9
Make it at least legal grammar!
Guido van Rossum <guido@python.org>
parents:
753
diff
changeset
|
480 if val[-1:] == '\n': |
|
cb146aa987c9
Make it at least legal grammar!
Guido van Rossum <guido@python.org>
parents:
753
diff
changeset
|
481 val = val[:-1] |
|
cb146aa987c9
Make it at least legal grammar!
Guido van Rossum <guido@python.org>
parents:
753
diff
changeset
|
482 return val |
| 752 | 483 finally: |
| 484 del inputwindows[0] | |
| 485 self.win.settitle(save_title) | |
| 486 # If we don't catch InputAvailable, something's wrong... | |
| 487 raise EOFError | |
| 488 # | |
| 489 | |
| 490 | |
| 491 # Currently unused function to test a command's syntax without executing it | |
| 492 # | |
| 493 def testsyntax(s): | |
| 494 import string | |
| 495 lines = string.splitfields(s, '\n') | |
| 496 for i in range(len(lines)): lines[i] = '\t' + lines[i] | |
| 497 lines.insert(0, 'if 0:') | |
| 498 lines.append('') | |
| 499 exec(string.joinfields(lines, '\n')) | |
| 500 | |
| 501 | |
| 502 # Call the main program. | |
| 503 # | |
| 504 main() |
