1 """Shell is an interactive text control in which a user types in 
   2 commands to be sent to the interpreter.  This particular shell is 
   3 based on wxPython's wxStyledTextCtrl. 
   5 Sponsored by Orbtech - Your source for Python programming expertise.""" 
   7 __author__ 
= "Patrick K. O'Brien <pobrien@orbtech.com>" 
   9 __revision__ 
= "$Revision$"[11:-2] 
  11 #from wxd.d_wx import wx 
  12 #from wxd.d_stc import stc 
  22 from buffer import Buffer
 
  26 from pseudo 
import PseudoFileIn
 
  27 from pseudo 
import PseudoFileOut
 
  28 from pseudo 
import PseudoFileErr
 
  29 from version 
import VERSION
 
  37 sys
.ps3 
= '<-- '  # Input prompt. 
  39 NAVKEYS 
= (wx
.WXK_END
, wx
.WXK_LEFT
, wx
.WXK_RIGHT
, 
  40            wx
.WXK_UP
, wx
.WXK_DOWN
, wx
.WXK_PRIOR
, wx
.WXK_NEXT
) 
  43 class ShellFrame(frame
.Frame
): 
  44     """Frame containing the shell component.""" 
  47     revision 
= __revision__
 
  49     def __init__(self
, parent
=None, id=-1, title
='PyShell', 
  50                  pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
, 
  51                  style
=wx
.DEFAULT_FRAME_STYLE
, locals=None, 
  52                  InterpClass
=None, *args
, **kwds
): 
  53         """Create ShellFrame instance.""" 
  54         frame
.Frame
.__init
__(self
, parent
, id, title
, pos
, size
, style
) 
  55         intro 
= 'PyShell %s - The Flakiest Python Shell' % VERSION
 
  56         intro 
+= '\nSponsored by Orbtech - ' + \
 
  57                  'Your source for Python programming expertise.' 
  58         self
.SetStatusText(intro
.replace('\n', ', ')) 
  59         self
.shell 
= Shell(parent
=self
, id=-1, introText
=intro
, 
  60                            locals=locals, InterpClass
=InterpClass
, 
  62         # Override the shell so that status messages go to the status bar. 
  63         self
.shell
.setStatusText 
= self
.SetStatusText
 
  65     def OnClose(self
, event
): 
  66         """Event handler for closing.""" 
  67         # This isn't working the way I want, but I'll leave it for now. 
  68         if self
.shell
.waiting
: 
  75     def OnAbout(self
, event
): 
  76         """Display an About window.""" 
  77         title 
= 'About PyShell' 
  78         text 
= 'PyShell %s\n\n' % VERSION 
+ \
 
  79                'Yet another Python shell, only flakier.\n\n' + \
 
  80                'Half-baked by Patrick K. O\'Brien,\n' + \
 
  81                'the other half is still in the oven.\n\n' + \
 
  82                'Shell Revision: %s\n' % self
.shell
.revision 
+ \
 
  83                'Interpreter Revision: %s\n\n' % self
.shell
.interp
.revision 
+ \
 
  84                'Python Version: %s\n' % sys
.version
.split()[0] + \
 
  85                'wxPython Version: %s\n' % wx
.VERSION_STRING 
+ \
 
  86                'Platform: %s\n' % sys
.platform
 
  87         dialog 
= wx
.MessageDialog(self
, text
, title
, 
  88                                   wx
.OK | wx
.ICON_INFORMATION
) 
  94     """Simplified interface to all shell-related functionality. 
  96     This is a semi-transparent facade, in that all attributes of other 
  97     are accessible, even though only some are visible to the user.""" 
  99     name 
= 'Shell Interface' 
 100     revision 
= __revision__
 
 102     def __init__(self
, other
): 
 103         """Create a ShellFacade instance.""" 
 109 Home              Go to the beginning of the command or line. 
 110 Shift+Home        Select to the beginning of the command or line. 
 111 Shift+End         Select to the end of the line. 
 112 End               Go to the end of the line. 
 113 Ctrl+C            Copy selected text, removing prompts. 
 114 Ctrl+Shift+C      Copy selected text, retaining prompts. 
 115 Ctrl+X            Cut selected text. 
 116 Ctrl+V            Paste from clipboard. 
 117 Ctrl+Shift+V      Paste and run multiple commands from clipboard. 
 118 Ctrl+Up Arrow     Retrieve Previous History item. 
 119 Alt+P             Retrieve Previous History item. 
 120 Ctrl+Down Arrow   Retrieve Next History item. 
 121 Alt+N             Retrieve Next History item. 
 122 Shift+Up Arrow    Insert Previous History item. 
 123 Shift+Down Arrow  Insert Next History item. 
 124 F8                Command-completion of History item. 
 125                   (Type a few characters of a previous command and press F8.) 
 126 Ctrl+Enter        Insert new line into multiline command. 
 127 Ctrl+]            Increase font size. 
 128 Ctrl+[            Decrease font size. 
 129 Ctrl+=            Default font size. 
 133         """Display some useful information about how to use the shell.""" 
 134         self
.write(self
.helpText
) 
 136     def __getattr__(self
, name
): 
 137         if hasattr(self
.other
, name
): 
 138             return getattr(self
.other
, name
) 
 140             raise AttributeError, name
 
 142     def __setattr__(self
, name
, value
): 
 143         if self
.__dict
__.has_key(name
): 
 144             self
.__dict
__[name
] = value
 
 145         elif hasattr(self
.other
, name
): 
 146             setattr(self
.other
, name
, value
) 
 148             raise AttributeError, name
 
 150     def _getAttributeNames(self
): 
 151         """Return list of magic attributes to extend introspection.""" 
 157             'autoCompleteCaseInsensitive', 
 158             'autoCompleteIncludeDouble', 
 159             'autoCompleteIncludeMagic', 
 160             'autoCompleteIncludeSingle', 
 177 class Shell(editwindow
.EditWindow
): 
 178     """Shell based on StyledTextCtrl.""" 
 181     revision 
= __revision__
 
 183     def __init__(self
, parent
, id=-1, pos
=wx
.DefaultPosition
, 
 184                  size
=wx
.DefaultSize
, style
=wx
.CLIP_CHILDREN
, 
 185                  introText
='', locals=None, InterpClass
=None, *args
, **kwds
): 
 186         """Create Shell instance.""" 
 187         editwindow
.EditWindow
.__init
__(self
, parent
, id, pos
, size
, style
) 
 191         # Grab these so they can be restored by self.redirect* methods. 
 192         self
.stdin 
= sys
.stdin
 
 193         self
.stdout 
= sys
.stdout
 
 194         self
.stderr 
= sys
.stderr
 
 195         # Import a default interpreter class if one isn't provided. 
 196         if InterpClass 
== None: 
 197             from interpreter 
import Interpreter
 
 199             Interpreter 
= InterpClass
 
 200         # Create a replacement for stdin. 
 201         self
.reader 
= PseudoFileIn(self
.readline
, self
.readlines
) 
 202         self
.reader
.input = '' 
 203         self
.reader
.isreading 
= False 
 204         # Set up the interpreter. 
 205         self
.interp 
= Interpreter(locals=locals, 
 206                                   rawin
=self
.raw_input, 
 208                                   stdout
=PseudoFileOut(self
.writeOut
), 
 209                                   stderr
=PseudoFileErr(self
.writeErr
), 
 212         self
.buffer = Buffer() 
 213         # Find out for which keycodes the interpreter will autocomplete. 
 214         self
.autoCompleteKeys 
= self
.interp
.getAutoCompleteKeys() 
 215         # Keep track of the last non-continuation prompt positions. 
 216         self
.promptPosStart 
= 0 
 217         self
.promptPosEnd 
= 0 
 218         # Keep track of multi-line commands. 
 220         # Create the command history.  Commands are added into the 
 221         # front of the list (ie. at index 0) as they are entered. 
 222         # self.historyIndex is the current position in the history; it 
 223         # gets incremented as you retrieve the previous command, 
 224         # decremented as you retrieve the next, and reset when you hit 
 225         # Enter.  self.historyIndex == -1 means you're on the current 
 226         # command, not in the history. 
 228         self
.historyIndex 
= -1 
 229         # Assign handlers for keyboard events. 
 230         wx
.EVT_CHAR(self
, self
.OnChar
) 
 231         wx
.EVT_KEY_DOWN(self
, self
.OnKeyDown
) 
 232         # Assign handler for idle time. 
 234         wx
.EVT_IDLE(self
, self
.OnIdle
) 
 235         # Display the introductory banner information. 
 236         self
.showIntro(introText
) 
 237         # Assign some pseudo keywords to the interpreter's namespace. 
 238         self
.setBuiltinKeywords() 
 239         # Add 'shell' to the interpreter's local namespace. 
 241         # Do this last so the user has complete control over their 
 242         # environment.  They can override anything they want. 
 243         self
.execStartupScript(self
.interp
.startupScript
) 
 244         wx
.CallAfter(self
.ScrollToLine
, 0) 
 250         """Set focus to the shell.""" 
 253     def OnIdle(self
, event
): 
 254         """Free the CPU to do other things.""" 
 259     def showIntro(self
, text
=''): 
 260         """Display introductory text in the shell.""" 
 262             if not text
.endswith(os
.linesep
): 
 266             self
.write(self
.interp
.introText
) 
 267         except AttributeError: 
 270     def setBuiltinKeywords(self
): 
 271         """Create pseudo keywords as part of builtins. 
 273         This sets `close`, `exit` and `quit` to a helpful string. 
 276         __builtin__
.close 
= __builtin__
.exit 
= __builtin__
.quit 
= \
 
 277             'Click on the close button to leave the application.' 
 280         """Quit the application.""" 
 282         # XXX Good enough for now but later we want to send a close event. 
 284         # In the close event handler we can make sure they want to 
 285         # quit.  Other applications, like PythonCard, may choose to 
 286         # hide rather than quit so we should just post the event and 
 287         # let the surrounding app decide what it wants to do. 
 288         self
.write('Click on the close button to leave the application.') 
 290     def setLocalShell(self
): 
 291         """Add 'shell' to locals as reference to ShellFacade instance.""" 
 292         self
.interp
.locals['shell'] = ShellFacade(other
=self
) 
 294     def execStartupScript(self
, startupScript
): 
 295         """Execute the user's PYTHONSTARTUP script if they have one.""" 
 296         if startupScript 
and os
.path
.isfile(startupScript
): 
 297             text 
= 'Startup script executed: ' + startupScript
 
 298             self
.push('print %r; execfile(%r)' % (text
, startupScript
)) 
 303         """Display information about Py.""" 
 307 Py Shell Revision: %s 
 308 Py Interpreter Revision: %s 
 312         (__author__
, VERSION
, self
.revision
, self
.interp
.revision
, 
 313          sys
.version
.split()[0], wx
.VERSION_STRING
, sys
.platform
) 
 314         self
.write(text
.strip()) 
 316     def OnChar(self
, event
): 
 317         """Keypress event handler. 
 319         Only receives an event if OnKeyDown calls event.Skip() for the 
 320         corresponding event.""" 
 322         # Prevent modification of previously submitted 
 323         # commands/responses. 
 324         if not self
.CanEdit(): 
 326         key 
= event
.KeyCode() 
 327         currpos 
= self
.GetCurrentPos() 
 328         stoppos 
= self
.promptPosEnd
 
 329         # Return (Enter) needs to be ignored in this handler. 
 330         if key 
== wx
.WXK_RETURN
: 
 332         elif key 
in self
.autoCompleteKeys
: 
 333             # Usually the dot (period) key activates auto completion. 
 334             # Get the command between the prompt and the cursor.  Add 
 335             # the autocomplete character to the end of the command. 
 336             if self
.AutoCompActive(): 
 337                 self
.AutoCompCancel() 
 338             command 
= self
.GetTextRange(stoppos
, currpos
) + chr(key
) 
 340             if self
.autoComplete
: 
 341                 self
.autoCompleteShow(command
) 
 342         elif key 
== ord('('): 
 343             # The left paren activates a call tip and cancels an 
 344             # active auto completion. 
 345             if self
.AutoCompActive(): 
 346                 self
.AutoCompCancel() 
 347             # Get the command between the prompt and the cursor.  Add 
 348             # the '(' to the end of the command. 
 349             self
.ReplaceSelection('') 
 350             command 
= self
.GetTextRange(stoppos
, currpos
) + '(' 
 352             self
.autoCallTipShow(command
) 
 354             # Allow the normal event handling to take place. 
 357     def OnKeyDown(self
, event
): 
 358         """Key down event handler.""" 
 360         key 
= event
.KeyCode() 
 361         # If the auto-complete window is up let it do its thing. 
 362         if self
.AutoCompActive(): 
 365         # Prevent modification of previously submitted 
 366         # commands/responses. 
 367         controlDown 
= event
.ControlDown() 
 368         altDown 
= event
.AltDown() 
 369         shiftDown 
= event
.ShiftDown() 
 370         currpos 
= self
.GetCurrentPos() 
 371         endpos 
= self
.GetTextLength() 
 372         selecting 
= self
.GetSelectionStart() != self
.GetSelectionEnd() 
 373         # Return (Enter) is used to submit a command to the 
 375         if not controlDown 
and key 
== wx
.WXK_RETURN
: 
 376             if self
.CallTipActive(): 
 379         # Ctrl+Return (Cntrl+Enter) is used to insert a line break. 
 380         elif controlDown 
and key 
== wx
.WXK_RETURN
: 
 381             if self
.CallTipActive(): 
 383             if currpos 
== endpos
: 
 386                 self
.insertLineBreak() 
 387         # Let Ctrl-Alt-* get handled normally. 
 388         elif controlDown 
and altDown
: 
 390         # Clear the current, unexecuted command. 
 391         elif key 
== wx
.WXK_ESCAPE
: 
 392             if self
.CallTipActive(): 
 396         # Increase font size. 
 397         elif controlDown 
and key 
in (ord(']'),): 
 398             dispatcher
.send(signal
='FontIncrease') 
 399         # Decrease font size. 
 400         elif controlDown 
and key 
in (ord('['),): 
 401             dispatcher
.send(signal
='FontDecrease') 
 403         elif controlDown 
and key 
in (ord('='),): 
 404             dispatcher
.send(signal
='FontDefault') 
 405         # Cut to the clipboard. 
 406         elif (controlDown 
and key 
in (ord('X'), ord('x'))) \
 
 407         or (shiftDown 
and key 
== wx
.WXK_DELETE
): 
 409         # Copy to the clipboard. 
 410         elif controlDown 
and not shiftDown \
 
 411             and key 
in (ord('C'), ord('c'), wx
.WXK_INSERT
): 
 413         # Copy to the clipboard, including prompts. 
 414         elif controlDown 
and shiftDown \
 
 415             and key 
in (ord('C'), ord('c'), wx
.WXK_INSERT
): 
 416             self
.CopyWithPrompts() 
 417         # Copy to the clipboard, including prefixed prompts. 
 418         elif altDown 
and not controlDown \
 
 419             and key 
in (ord('C'), ord('c'), wx
.WXK_INSERT
): 
 420             self
.CopyWithPromptsPrefixed() 
 421         # Home needs to be aware of the prompt. 
 422         elif key 
== wx
.WXK_HOME
: 
 423             home 
= self
.promptPosEnd
 
 425                 self
.SetCurrentPos(home
) 
 426                 if not selecting 
and not shiftDown
: 
 428                     self
.EnsureCaretVisible() 
 432         # The following handlers modify text, so we need to see if 
 433         # there is a selection that includes text prior to the prompt. 
 435         # Don't modify a selection with text prior to the prompt. 
 436         elif selecting 
and key 
not in NAVKEYS 
and not self
.CanEdit(): 
 438         # Paste from the clipboard. 
 439         elif (controlDown 
and not shiftDown 
and key 
in (ord('V'), ord('v'))) \
 
 440                  or (shiftDown 
and not controlDown 
and key 
== wx
.WXK_INSERT
): 
 442         # Paste from the clipboard, run commands. 
 443         elif controlDown 
and shiftDown 
and key 
in (ord('V'), ord('v')): 
 445         # Replace with the previous command from the history buffer. 
 446         elif (controlDown 
and key 
== wx
.WXK_UP
) \
 
 447                  or (altDown 
and key 
in (ord('P'), ord('p'))): 
 448             self
.OnHistoryReplace(step
=+1) 
 449         # Replace with the next command from the history buffer. 
 450         elif (controlDown 
and key 
== wx
.WXK_DOWN
) \
 
 451                  or (altDown 
and key 
in (ord('N'), ord('n'))): 
 452             self
.OnHistoryReplace(step
=-1) 
 453         # Insert the previous command from the history buffer. 
 454         elif (shiftDown 
and key 
== wx
.WXK_UP
) and self
.CanEdit(): 
 455             self
.OnHistoryInsert(step
=+1) 
 456         # Insert the next command from the history buffer. 
 457         elif (shiftDown 
and key 
== wx
.WXK_DOWN
) and self
.CanEdit(): 
 458             self
.OnHistoryInsert(step
=-1) 
 459         # Search up the history for the text in front of the cursor. 
 460         elif key 
== wx
.WXK_F8
: 
 461             self
.OnHistorySearch() 
 462         # Don't backspace over the latest non-continuation prompt. 
 463         elif key 
== wx
.WXK_BACK
: 
 464             if selecting 
and self
.CanEdit(): 
 466             elif currpos 
> self
.promptPosEnd
: 
 468         # Only allow these keys after the latest prompt. 
 469         elif key 
in (wx
.WXK_TAB
, wx
.WXK_DELETE
): 
 472         # Don't toggle between insert mode and overwrite mode. 
 473         elif key 
== wx
.WXK_INSERT
: 
 475         # Don't allow line deletion. 
 476         elif controlDown 
and key 
in (ord('L'), ord('l')): 
 478         # Don't allow line transposition. 
 479         elif controlDown 
and key 
in (ord('T'), ord('t')): 
 481         # Basic navigation keys should work anywhere. 
 484         # Protect the readonly portion of the shell. 
 485         elif not self
.CanEdit(): 
 490     def clearCommand(self
): 
 491         """Delete the current, unexecuted command.""" 
 492         startpos 
= self
.promptPosEnd
 
 493         endpos 
= self
.GetTextLength() 
 494         self
.SetSelection(startpos
, endpos
) 
 495         self
.ReplaceSelection('') 
 498     def OnHistoryReplace(self
, step
): 
 499         """Replace with the previous/next command from the history buffer.""" 
 501         self
.replaceFromHistory(step
) 
 503     def replaceFromHistory(self
, step
): 
 504         """Replace selection with command from the history buffer.""" 
 506         self
.ReplaceSelection('') 
 507         newindex 
= self
.historyIndex 
+ step
 
 508         if -1 <= newindex 
<= len(self
.history
): 
 509             self
.historyIndex 
= newindex
 
 510         if 0 <= newindex 
<= len(self
.history
)-1: 
 511             command 
= self
.history
[self
.historyIndex
] 
 512             command 
= command
.replace('\n', os
.linesep 
+ ps2
) 
 513             self
.ReplaceSelection(command
) 
 515     def OnHistoryInsert(self
, step
): 
 516         """Insert the previous/next command from the history buffer.""" 
 517         if not self
.CanEdit(): 
 519         startpos 
= self
.GetCurrentPos() 
 520         self
.replaceFromHistory(step
) 
 521         endpos 
= self
.GetCurrentPos() 
 522         self
.SetSelection(endpos
, startpos
) 
 524     def OnHistorySearch(self
): 
 525         """Search up the history buffer for the text in front of the cursor.""" 
 526         if not self
.CanEdit(): 
 528         startpos 
= self
.GetCurrentPos() 
 529         # The text up to the cursor is what we search for. 
 530         numCharsAfterCursor 
= self
.GetTextLength() - startpos
 
 531         searchText 
= self
.getCommand(rstrip
=False) 
 532         if numCharsAfterCursor 
> 0: 
 533             searchText 
= searchText
[:-numCharsAfterCursor
] 
 536         # Search upwards from the current history position and loop 
 537         # back to the beginning if we don't find anything. 
 538         if (self
.historyIndex 
<= -1) \
 
 539         or (self
.historyIndex 
>= len(self
.history
)-2): 
 540             searchOrder 
= range(len(self
.history
)) 
 542             searchOrder 
= range(self
.historyIndex
+1, len(self
.history
)) + \
 
 543                           range(self
.historyIndex
) 
 544         for i 
in searchOrder
: 
 545             command 
= self
.history
[i
] 
 546             if command
[:len(searchText
)] == searchText
: 
 547                 # Replace the current selection with the one we found. 
 548                 self
.ReplaceSelection(command
[len(searchText
):]) 
 549                 endpos 
= self
.GetCurrentPos() 
 550                 self
.SetSelection(endpos
, startpos
) 
 551                 # We've now warped into middle of the history. 
 552                 self
.historyIndex 
= i
 
 555     def setStatusText(self
, text
): 
 556         """Display status information.""" 
 558         # This method will likely be replaced by the enclosing app to 
 559         # do something more interesting, like write to a status bar. 
 562     def insertLineBreak(self
): 
 563         """Insert a new line break.""" 
 565             self
.write(os
.linesep
) 
 569     def processLine(self
): 
 570         """Process the line of text at which the user hit Enter.""" 
 572         # The user hit ENTER and we need to decide what to do. They 
 573         # could be sitting on any line in the shell. 
 575         thepos 
= self
.GetCurrentPos() 
 576         startpos 
= self
.promptPosEnd
 
 577         endpos 
= self
.GetTextLength() 
 579         # If they hit RETURN inside the current command, execute the 
 582             self
.SetCurrentPos(endpos
) 
 583             self
.interp
.more 
= False 
 584             command 
= self
.GetTextRange(startpos
, endpos
) 
 585             lines 
= command
.split(os
.linesep 
+ ps2
) 
 586             lines 
= [line
.rstrip() for line 
in lines
] 
 587             command 
= '\n'.join(lines
) 
 588             if self
.reader
.isreading
: 
 590                     # Match the behavior of the standard Python shell 
 591                     # when the user hits return without entering a 
 594                 self
.reader
.input = command
 
 595                 self
.write(os
.linesep
) 
 598         # Or replace the current command with the other command. 
 600             # If the line contains a command (even an invalid one). 
 601             if self
.getCommand(rstrip
=False): 
 602                 command 
= self
.getMultilineCommand() 
 605             # Otherwise, put the cursor back where we started. 
 607                 self
.SetCurrentPos(thepos
) 
 608                 self
.SetAnchor(thepos
) 
 610     def getMultilineCommand(self
, rstrip
=True): 
 611         """Extract a multi-line command from the editor. 
 613         The command may not necessarily be valid Python syntax.""" 
 614         # XXX Need to extract real prompts here. Need to keep track of 
 615         # the prompt every time a command is issued. 
 620         # This is a total hack job, but it works. 
 621         text 
= self
.GetCurLine()[0] 
 622         line 
= self
.GetCurrentLine() 
 623         while text
[:ps2size
] == ps2 
and line 
> 0: 
 626             text 
= self
.GetCurLine()[0] 
 627         if text
[:ps1size
] == ps1
: 
 628             line 
= self
.GetCurrentLine() 
 630             startpos 
= self
.GetCurrentPos() + ps1size
 
 633             while self
.GetCurLine()[0][:ps2size
] == ps2
: 
 636             stoppos 
= self
.GetCurrentPos() 
 637             command 
= self
.GetTextRange(startpos
, stoppos
) 
 638             command 
= command
.replace(os
.linesep 
+ ps2
, '\n') 
 639             command 
= command
.rstrip() 
 640             command 
= command
.replace('\n', os
.linesep 
+ ps2
) 
 644             command 
= command
.rstrip() 
 647     def getCommand(self
, text
=None, rstrip
=True): 
 648         """Extract a command from text which may include a shell prompt. 
 650         The command may not necessarily be valid Python syntax.""" 
 652             text 
= self
.GetCurLine()[0] 
 653         # Strip the prompt off the front leaving just the command. 
 654         command 
= self
.lstripPrompt(text
) 
 656             command 
= ''  # Real commands have prompts. 
 658             command 
= command
.rstrip() 
 661     def lstripPrompt(self
, text
): 
 662         """Return text without a leading prompt.""" 
 667         # Strip the prompt off the front of text. 
 668         if text
[:ps1size
] == ps1
: 
 669             text 
= text
[ps1size
:] 
 670         elif text
[:ps2size
] == ps2
: 
 671             text 
= text
[ps2size
:] 
 674     def push(self
, command
): 
 675         """Send command to the interpreter for execution.""" 
 676         self
.write(os
.linesep
) 
 677         busy 
= wx
.BusyCursor() 
 679         self
.more 
= self
.interp
.push(command
) 
 683             self
.addHistory(command
.rstrip()) 
 686     def addHistory(self
, command
): 
 687         """Add command to the command history.""" 
 688         # Reset the history position. 
 689         self
.historyIndex 
= -1 
 690         # Insert this command into the history, unless it's a blank 
 691         # line or the same as the last command. 
 693         and (len(self
.history
) == 0 or command 
!= self
.history
[0]): 
 694             self
.history
.insert(0, command
) 
 696     def write(self
, text
): 
 697         """Display text in the shell. 
 699         Replace line endings with OS-specific endings.""" 
 700         text 
= self
.fixLineEndings(text
) 
 702         self
.EnsureCaretVisible() 
 704     def fixLineEndings(self
, text
): 
 705         """Return text with line endings replaced by OS-specific endings.""" 
 706         lines 
= text
.split('\r\n') 
 707         for l 
in range(len(lines
)): 
 708             chunks 
= lines
[l
].split('\r') 
 709             for c 
in range(len(chunks
)): 
 710                 chunks
[c
] = os
.linesep
.join(chunks
[c
].split('\n')) 
 711             lines
[l
] = os
.linesep
.join(chunks
) 
 712         text 
= os
.linesep
.join(lines
) 
 716         """Display proper prompt for the context: ps1, ps2 or ps3. 
 718         If this is a continuation line, autoindent as necessary.""" 
 719         isreading 
= self
.reader
.isreading
 
 722             prompt 
= str(sys
.ps3
) 
 724             prompt 
= str(sys
.ps2
) 
 726             prompt 
= str(sys
.ps1
) 
 727         pos 
= self
.GetCurLine()[1] 
 732                 self
.write(os
.linesep
) 
 734             self
.promptPosStart 
= self
.GetCurrentPos() 
 738             self
.promptPosEnd 
= self
.GetCurrentPos() 
 739             # Keep the undo feature from undoing previous responses. 
 740             self
.EmptyUndoBuffer() 
 741         # XXX Add some autoindent magic here if more. 
 743             self
.write(' '*4)  # Temporary hack indentation. 
 744         self
.EnsureCaretVisible() 
 745         self
.ScrollToColumn(0) 
 748         """Replacement for stdin.readline().""" 
 751         reader
.isreading 
= True 
 754             while not reader
.input: 
 759             reader
.isreading 
= False 
 760         input = str(input)  # In case of Unicode. 
 764         """Replacement for stdin.readlines().""" 
 766         while lines
[-1:] != ['\n']: 
 767             lines
.append(self
.readline()) 
 770     def raw_input(self
, prompt
=''): 
 771         """Return string based on user input.""" 
 774         return self
.readline() 
 776     def ask(self
, prompt
='Please enter your response:'): 
 777         """Get response from the user using a dialog box.""" 
 778         dialog 
= wx
.TextEntryDialog(None, prompt
, 
 779                                     'Input Dialog (Raw)', '') 
 781             if dialog
.ShowModal() == wx
.ID_OK
: 
 782                 text 
= dialog
.GetValue() 
 789         """Halt execution pending a response from the user.""" 
 790         self
.ask('Press enter to continue:') 
 793         """Delete all text from the shell.""" 
 796     def run(self
, command
, prompt
=True, verbose
=True): 
 797         """Execute command as if it was typed in directly. 
 798         >>> shell.run('print "this"') 
 803         # Go to the very bottom of the text. 
 804         endpos 
= self
.GetTextLength() 
 805         self
.SetCurrentPos(endpos
) 
 806         command 
= command
.rstrip() 
 807         if prompt
: self
.prompt() 
 808         if verbose
: self
.write(command
) 
 811     def runfile(self
, filename
): 
 812         """Execute all commands in file as if they were typed into the 
 814         file = open(filename
) 
 817             for command 
in file.readlines(): 
 818                 if command
[:6] == 'shell.': 
 819                     # Run shell methods silently. 
 820                     self
.run(command
, prompt
=False, verbose
=False) 
 822                     self
.run(command
, prompt
=False, verbose
=True) 
 826     def autoCompleteShow(self
, command
): 
 827         """Display auto-completion popup list.""" 
 828         list = self
.interp
.getAutoCompleteList(command
, 
 829                     includeMagic
=self
.autoCompleteIncludeMagic
, 
 830                     includeSingle
=self
.autoCompleteIncludeSingle
, 
 831                     includeDouble
=self
.autoCompleteIncludeDouble
) 
 833             options 
= ' '.join(list) 
 835             self
.AutoCompShow(offset
, options
) 
 837     def autoCallTipShow(self
, command
): 
 838         """Display argument spec and docstring in a popup window.""" 
 839         if self
.CallTipActive(): 
 841         (name
, argspec
, tip
) = self
.interp
.getCallTip(command
) 
 843             dispatcher
.send(signal
='Shell.calltip', sender
=self
, calltip
=tip
) 
 844         if not self
.autoCallTip
: 
 847             startpos 
= self
.GetCurrentPos() 
 848             self
.write(argspec 
+ ')') 
 849             endpos 
= self
.GetCurrentPos() 
 850             self
.SetSelection(endpos
, startpos
) 
 852             curpos 
= self
.GetCurrentPos() 
 853             tippos 
= curpos 
- (len(name
) + 1) 
 854             fallback 
= curpos 
- self
.GetColumn(curpos
) 
 855             # In case there isn't enough room, only go back to the 
 857             tippos 
= max(tippos
, fallback
) 
 858             self
.CallTipShow(tippos
, tip
) 
 860     def writeOut(self
, text
): 
 861         """Replacement for stdout.""" 
 864     def writeErr(self
, text
): 
 865         """Replacement for stderr.""" 
 868     def redirectStdin(self
, redirect
=True): 
 869         """If redirect is true then sys.stdin will come from the shell.""" 
 871             sys
.stdin 
= self
.reader
 
 873             sys
.stdin 
= self
.stdin
 
 875     def redirectStdout(self
, redirect
=True): 
 876         """If redirect is true then sys.stdout will go to the shell.""" 
 878             sys
.stdout 
= PseudoFileOut(self
.writeOut
) 
 880             sys
.stdout 
= self
.stdout
 
 882     def redirectStderr(self
, redirect
=True): 
 883         """If redirect is true then sys.stderr will go to the shell.""" 
 885             sys
.stderr 
= PseudoFileErr(self
.writeErr
) 
 887             sys
.stderr 
= self
.stderr
 
 890         """Return true if text is selected and can be cut.""" 
 891         if self
.GetSelectionStart() != self
.GetSelectionEnd() \
 
 892                and self
.GetSelectionStart() >= self
.promptPosEnd \
 
 893                and self
.GetSelectionEnd() >= self
.promptPosEnd
: 
 899         """Return true if a paste should succeed.""" 
 900         if self
.CanEdit() and editwindow
.EditWindow
.CanPaste(self
): 
 906         """Return true if editing should succeed.""" 
 907         if self
.GetSelectionStart() != self
.GetSelectionEnd(): 
 908             if self
.GetSelectionStart() >= self
.promptPosEnd \
 
 909                    and self
.GetSelectionEnd() >= self
.promptPosEnd
: 
 914             return self
.GetCurrentPos() >= self
.promptPosEnd
 
 917         """Remove selection and place it on the clipboard.""" 
 918         if self
.CanCut() and self
.CanCopy(): 
 919             if self
.AutoCompActive(): 
 920                 self
.AutoCompCancel() 
 921             if self
.CallTipActive(): 
 924             self
.ReplaceSelection('') 
 927         """Copy selection and place it on the clipboard.""" 
 931             command 
= self
.GetSelectedText() 
 932             command 
= command
.replace(os
.linesep 
+ ps2
, os
.linesep
) 
 933             command 
= command
.replace(os
.linesep 
+ ps1
, os
.linesep
) 
 934             command 
= self
.lstripPrompt(text
=command
) 
 935             data 
= wx
.TextDataObject(command
) 
 938     def CopyWithPrompts(self
): 
 939         """Copy selection, including prompts, and place it on the clipboard.""" 
 941             command 
= self
.GetSelectedText() 
 942             data 
= wx
.TextDataObject(command
) 
 945     def CopyWithPromptsPrefixed(self
): 
 946         """Copy selection, including prompts prefixed with four 
 947         spaces, and place it on the clipboard.""" 
 949             command 
= self
.GetSelectedText() 
 951             command 
= spaces 
+ command
.replace(os
.linesep
, 
 953             data 
= wx
.TextDataObject(command
) 
 956     def _clip(self
, data
): 
 957         if wx
.TheClipboard
.Open(): 
 958             wx
.TheClipboard
.UsePrimarySelection(False) 
 959             wx
.TheClipboard
.SetData(data
) 
 960             wx
.TheClipboard
.Flush() 
 961             wx
.TheClipboard
.Close() 
 964         """Replace selection with clipboard contents.""" 
 965         if self
.CanPaste() and wx
.TheClipboard
.Open(): 
 967             if wx
.TheClipboard
.IsSupported(wx
.DataFormat(wx
.DF_TEXT
)): 
 968                 data 
= wx
.TextDataObject() 
 969                 if wx
.TheClipboard
.GetData(data
): 
 970                     self
.ReplaceSelection('') 
 971                     command 
= data
.GetText() 
 972                     command 
= command
.rstrip() 
 973                     command 
= self
.fixLineEndings(command
) 
 974                     command 
= self
.lstripPrompt(text
=command
) 
 975                     command 
= command
.replace(os
.linesep 
+ ps2
, '\n') 
 976                     command 
= command
.replace(os
.linesep
, '\n') 
 977                     command 
= command
.replace('\n', os
.linesep 
+ ps2
) 
 979             wx
.TheClipboard
.Close() 
 981     def PasteAndRun(self
): 
 982         """Replace selection with clipboard contents, run commands.""" 
 983         if wx
.TheClipboard
.Open(): 
 986             if wx
.TheClipboard
.IsSupported(wx
.DataFormat(wx
.DF_TEXT
)): 
 987                 data 
= wx
.TextDataObject() 
 988                 if wx
.TheClipboard
.GetData(data
): 
 989                     endpos 
= self
.GetTextLength() 
 990                     self
.SetCurrentPos(endpos
) 
 991                     startpos 
= self
.promptPosEnd
 
 992                     self
.SetSelection(startpos
, endpos
) 
 993                     self
.ReplaceSelection('') 
 994                     text 
= data
.GetText() 
 996                     text 
= self
.fixLineEndings(text
) 
 997                     text 
= self
.lstripPrompt(text
) 
 998                     text 
= text
.replace(os
.linesep 
+ ps1
, '\n') 
 999                     text 
= text
.replace(os
.linesep 
+ ps2
, '\n') 
1000                     text 
= text
.replace(os
.linesep
, '\n') 
1001                     lines 
= text
.split('\n') 
1005                         if line
.strip() == ps2
.strip(): 
1006                             # If we are pasting from something like a 
1007                             # web page that drops the trailing space 
1008                             # from the ps2 prompt of a blank line. 
1010                         if line
.strip() != '' and line
.lstrip() == line
: 
1013                                 # Add the previous command to the list. 
1014                                 commands
.append(command
) 
1015                             # Start a new command, which may be multiline. 
1018                             # Multiline command. Add to the command. 
1021                     commands
.append(command
) 
1022                     for command 
in commands
: 
1023                         command 
= command
.replace('\n', os
.linesep 
+ ps2
) 
1026             wx
.TheClipboard
.Close() 
1028     def wrap(self
, wrap
=True): 
1029         """Sets whether text is word wrapped.""" 
1031             self
.SetWrapMode(wrap
) 
1032         except AttributeError: 
1033             return 'Wrapping is not available in this version.' 
1035     def zoom(self
, points
=0): 
1036         """Set the zoom level. 
1038         This number of points is added to the size of all fonts.  It 
1039         may be positive to magnify or negative to reduce.""" 
1040         self
.SetZoom(points
)