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] 
  19 from buffer import Buffer
 
  23 from pseudo 
import PseudoFileIn
 
  24 from pseudo 
import PseudoFileOut
 
  25 from pseudo 
import PseudoFileErr
 
  26 from version 
import VERSION
 
  28 sys
.ps3 
= '<-- '  # Input prompt. 
  30 NAVKEYS 
= (wx
.WXK_END
, wx
.WXK_LEFT
, wx
.WXK_RIGHT
, 
  31            wx
.WXK_UP
, wx
.WXK_DOWN
, wx
.WXK_PRIOR
, wx
.WXK_NEXT
) 
  34 class ShellFrame(frame
.Frame
): 
  35     """Frame containing the shell component.""" 
  38     revision 
= __revision__
 
  40     def __init__(self
, parent
=None, id=-1, title
='PyShell', 
  41                  pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
, 
  42                  style
=wx
.DEFAULT_FRAME_STYLE
, locals=None, 
  43                  InterpClass
=None, *args
, **kwds
): 
  44         """Create ShellFrame instance.""" 
  45         frame
.Frame
.__init
__(self
, parent
, id, title
, pos
, size
, style
) 
  46         intro 
= 'PyShell %s - The Flakiest Python Shell' % VERSION
 
  47         intro 
+= '\nSponsored by Orbtech - ' + \
 
  48                  'Your source for Python programming expertise.' 
  49         self
.SetStatusText(intro
.replace('\n', ', ')) 
  50         self
.shell 
= Shell(parent
=self
, id=-1, introText
=intro
, 
  51                            locals=locals, InterpClass
=InterpClass
, 
  53         # Override the shell so that status messages go to the status bar. 
  54         self
.shell
.setStatusText 
= self
.SetStatusText
 
  56     def OnClose(self
, event
): 
  57         """Event handler for closing.""" 
  58         # This isn't working the way I want, but I'll leave it for now. 
  59         if self
.shell
.waiting
: 
  66     def OnAbout(self
, event
): 
  67         """Display an About window.""" 
  68         title 
= 'About PyShell' 
  69         text 
= 'PyShell %s\n\n' % VERSION 
+ \
 
  70                'Yet another Python shell, only flakier.\n\n' + \
 
  71                'Half-baked by Patrick K. O\'Brien,\n' + \
 
  72                'the other half is still in the oven.\n\n' + \
 
  73                'Shell Revision: %s\n' % self
.shell
.revision 
+ \
 
  74                'Interpreter Revision: %s\n\n' % self
.shell
.interp
.revision 
+ \
 
  75                'Python Version: %s\n' % sys
.version
.split()[0] + \
 
  76                'wxPython Version: %s\n' % wx
.VERSION_STRING 
+ \
 
  77                'Platform: %s\n' % sys
.platform
 
  78         dialog 
= wx
.MessageDialog(self
, text
, title
, 
  79                                   wx
.OK | wx
.ICON_INFORMATION
) 
  85     """Simplified interface to all shell-related functionality. 
  87     This is a semi-transparent facade, in that all attributes of other 
  88     are accessible, even though only some are visible to the user.""" 
  90     name 
= 'Shell Interface' 
  91     revision 
= __revision__
 
  93     def __init__(self
, other
): 
  94         """Create a ShellFacade instance.""" 
 100 Home              Go to the beginning of the command or line. 
 101 Shift+Home        Select to the beginning of the command or line. 
 102 Shift+End         Select to the end of the line. 
 103 End               Go to the end of the line. 
 104 Ctrl+C            Copy selected text, removing prompts. 
 105 Ctrl+Shift+C      Copy selected text, retaining prompts. 
 106 Ctrl+X            Cut selected text. 
 107 Ctrl+V            Paste from clipboard. 
 108 Ctrl+Shift+V      Paste and run multiple commands from clipboard. 
 109 Ctrl+Up Arrow     Retrieve Previous History item. 
 110 Alt+P             Retrieve Previous History item. 
 111 Ctrl+Down Arrow   Retrieve Next History item. 
 112 Alt+N             Retrieve Next History item. 
 113 Shift+Up Arrow    Insert Previous History item. 
 114 Shift+Down Arrow  Insert Next History item. 
 115 F8                Command-completion of History item. 
 116                   (Type a few characters of a previous command and press F8.) 
 117 Ctrl+Enter        Insert new line into multiline command. 
 118 Ctrl+]            Increase font size. 
 119 Ctrl+[            Decrease font size. 
 120 Ctrl+=            Default font size. 
 124         """Display some useful information about how to use the shell.""" 
 125         self
.write(self
.helpText
) 
 127     def __getattr__(self
, name
): 
 128         if hasattr(self
.other
, name
): 
 129             return getattr(self
.other
, name
) 
 131             raise AttributeError, name
 
 133     def __setattr__(self
, name
, value
): 
 134         if self
.__dict
__.has_key(name
): 
 135             self
.__dict
__[name
] = value
 
 136         elif hasattr(self
.other
, name
): 
 137             setattr(self
.other
, name
, value
) 
 139             raise AttributeError, name
 
 141     def _getAttributeNames(self
): 
 142         """Return list of magic attributes to extend introspection.""" 
 148             'autoCompleteAutoHide', 
 149             'autoCompleteCaseInsensitive', 
 150             'autoCompleteIncludeDouble', 
 151             'autoCompleteIncludeMagic', 
 152             'autoCompleteIncludeSingle', 
 169 class Shell(editwindow
.EditWindow
): 
 170     """Shell based on StyledTextCtrl.""" 
 173     revision 
= __revision__
 
 175     def __init__(self
, parent
, id=-1, pos
=wx
.DefaultPosition
, 
 176                  size
=wx
.DefaultSize
, style
=wx
.CLIP_CHILDREN
, 
 177                  introText
='', locals=None, InterpClass
=None, *args
, **kwds
): 
 178         """Create Shell instance.""" 
 179         editwindow
.EditWindow
.__init
__(self
, parent
, id, pos
, size
, style
) 
 183             locals = __main__
.__dict
__ 
 184         # Grab these so they can be restored by self.redirect* methods. 
 185         self
.stdin 
= sys
.stdin
 
 186         self
.stdout 
= sys
.stdout
 
 187         self
.stderr 
= sys
.stderr
 
 188         # Import a default interpreter class if one isn't provided. 
 189         if InterpClass 
== None: 
 190             from interpreter 
import Interpreter
 
 192             Interpreter 
= InterpClass
 
 193         # Create a replacement for stdin. 
 194         self
.reader 
= PseudoFileIn(self
.readline
, self
.readlines
) 
 195         self
.reader
.input = '' 
 196         self
.reader
.isreading 
= False 
 197         # Set up the interpreter. 
 198         self
.interp 
= Interpreter(locals=locals, 
 199                                   rawin
=self
.raw_input, 
 201                                   stdout
=PseudoFileOut(self
.writeOut
), 
 202                                   stderr
=PseudoFileErr(self
.writeErr
), 
 205         self
.buffer = Buffer() 
 206         # Find out for which keycodes the interpreter will autocomplete. 
 207         self
.autoCompleteKeys 
= self
.interp
.getAutoCompleteKeys() 
 208         # Keep track of the last non-continuation prompt positions. 
 209         self
.promptPosStart 
= 0 
 210         self
.promptPosEnd 
= 0 
 211         # Keep track of multi-line commands. 
 213         # Create the command history.  Commands are added into the 
 214         # front of the list (ie. at index 0) as they are entered. 
 215         # self.historyIndex is the current position in the history; it 
 216         # gets incremented as you retrieve the previous command, 
 217         # decremented as you retrieve the next, and reset when you hit 
 218         # Enter.  self.historyIndex == -1 means you're on the current 
 219         # command, not in the history. 
 221         self
.historyIndex 
= -1 
 222         # Assign handlers for keyboard events. 
 223         wx
.EVT_CHAR(self
, self
.OnChar
) 
 224         wx
.EVT_KEY_DOWN(self
, self
.OnKeyDown
) 
 225         # Assign handler for idle time. 
 227         wx
.EVT_IDLE(self
, self
.OnIdle
) 
 228         # Display the introductory banner information. 
 229         self
.showIntro(introText
) 
 230         # Assign some pseudo keywords to the interpreter's namespace. 
 231         self
.setBuiltinKeywords() 
 232         # Add 'shell' to the interpreter's local namespace. 
 234         # Do this last so the user has complete control over their 
 235         # environment.  They can override anything they want. 
 236         self
.execStartupScript(self
.interp
.startupScript
) 
 237         wx
.CallAfter(self
.ScrollToLine
, 0) 
 243         """Set focus to the shell.""" 
 246     def OnIdle(self
, event
): 
 247         """Free the CPU to do other things.""" 
 252     def showIntro(self
, text
=''): 
 253         """Display introductory text in the shell.""" 
 255             if not text
.endswith(os
.linesep
): 
 259             self
.write(self
.interp
.introText
) 
 260         except AttributeError: 
 263     def setBuiltinKeywords(self
): 
 264         """Create pseudo keywords as part of builtins. 
 266         This sets `close`, `exit` and `quit` to a helpful string. 
 269         __builtin__
.close 
= __builtin__
.exit 
= __builtin__
.quit 
= \
 
 270             'Click on the close button to leave the application.' 
 273         """Quit the application.""" 
 275         # XXX Good enough for now but later we want to send a close event. 
 277         # In the close event handler we can make sure they want to 
 278         # quit.  Other applications, like PythonCard, may choose to 
 279         # hide rather than quit so we should just post the event and 
 280         # let the surrounding app decide what it wants to do. 
 281         self
.write('Click on the close button to leave the application.') 
 283     def setLocalShell(self
): 
 284         """Add 'shell' to locals as reference to ShellFacade instance.""" 
 285         self
.interp
.locals['shell'] = ShellFacade(other
=self
) 
 287     def execStartupScript(self
, startupScript
): 
 288         """Execute the user's PYTHONSTARTUP script if they have one.""" 
 289         if startupScript 
and os
.path
.isfile(startupScript
): 
 290             text 
= 'Startup script executed: ' + startupScript
 
 291             self
.push('print %r; execfile(%r)' % (text
, startupScript
)) 
 296         """Display information about Py.""" 
 300 Py Shell Revision: %s 
 301 Py Interpreter Revision: %s 
 305         (__author__
, VERSION
, self
.revision
, self
.interp
.revision
, 
 306          sys
.version
.split()[0], wx
.VERSION_STRING
, sys
.platform
) 
 307         self
.write(text
.strip()) 
 309     def OnChar(self
, event
): 
 310         """Keypress event handler. 
 312         Only receives an event if OnKeyDown calls event.Skip() for the 
 313         corresponding event.""" 
 315         # Prevent modification of previously submitted 
 316         # commands/responses. 
 317         if not self
.CanEdit(): 
 319         key 
= event
.KeyCode() 
 320         currpos 
= self
.GetCurrentPos() 
 321         stoppos 
= self
.promptPosEnd
 
 322         # Return (Enter) needs to be ignored in this handler. 
 323         if key 
== wx
.WXK_RETURN
: 
 325         elif key 
in self
.autoCompleteKeys
: 
 326             # Usually the dot (period) key activates auto completion. 
 327             # Get the command between the prompt and the cursor.  Add 
 328             # the autocomplete character to the end of the command. 
 329             if self
.AutoCompActive(): 
 330                 self
.AutoCompCancel() 
 331             command 
= self
.GetTextRange(stoppos
, currpos
) + chr(key
) 
 333             if self
.autoComplete
: 
 334                 self
.autoCompleteShow(command
) 
 335         elif key 
== ord('('): 
 336             # The left paren activates a call tip and cancels an 
 337             # active auto completion. 
 338             if self
.AutoCompActive(): 
 339                 self
.AutoCompCancel() 
 340             # Get the command between the prompt and the cursor.  Add 
 341             # the '(' to the end of the command. 
 342             self
.ReplaceSelection('') 
 343             command 
= self
.GetTextRange(stoppos
, currpos
) + '(' 
 345             self
.autoCallTipShow(command
) 
 347             # Allow the normal event handling to take place. 
 350     def OnKeyDown(self
, event
): 
 351         """Key down event handler.""" 
 353         key 
= event
.KeyCode() 
 354         # If the auto-complete window is up let it do its thing. 
 355         if self
.AutoCompActive(): 
 358         # Prevent modification of previously submitted 
 359         # commands/responses. 
 360         controlDown 
= event
.ControlDown() 
 361         altDown 
= event
.AltDown() 
 362         shiftDown 
= event
.ShiftDown() 
 363         currpos 
= self
.GetCurrentPos() 
 364         endpos 
= self
.GetTextLength() 
 365         selecting 
= self
.GetSelectionStart() != self
.GetSelectionEnd() 
 366         # Return (Enter) is used to submit a command to the 
 368         if not controlDown 
and key 
== wx
.WXK_RETURN
: 
 369             if self
.CallTipActive(): 
 372         # Ctrl+Return (Cntrl+Enter) is used to insert a line break. 
 373         elif controlDown 
and key 
== wx
.WXK_RETURN
: 
 374             if self
.CallTipActive(): 
 376             if currpos 
== endpos
: 
 379                 self
.insertLineBreak() 
 380         # Let Ctrl-Alt-* get handled normally. 
 381         elif controlDown 
and altDown
: 
 383         # Clear the current, unexecuted command. 
 384         elif key 
== wx
.WXK_ESCAPE
: 
 385             if self
.CallTipActive(): 
 389         # Increase font size. 
 390         elif controlDown 
and key 
in (ord(']'),): 
 391             dispatcher
.send(signal
='FontIncrease') 
 392         # Decrease font size. 
 393         elif controlDown 
and key 
in (ord('['),): 
 394             dispatcher
.send(signal
='FontDecrease') 
 396         elif controlDown 
and key 
in (ord('='),): 
 397             dispatcher
.send(signal
='FontDefault') 
 398         # Cut to the clipboard. 
 399         elif (controlDown 
and key 
in (ord('X'), ord('x'))) \
 
 400         or (shiftDown 
and key 
== wx
.WXK_DELETE
): 
 402         # Copy to the clipboard. 
 403         elif controlDown 
and not shiftDown \
 
 404             and key 
in (ord('C'), ord('c'), wx
.WXK_INSERT
): 
 406         # Copy to the clipboard, including prompts. 
 407         elif controlDown 
and shiftDown \
 
 408             and key 
in (ord('C'), ord('c'), wx
.WXK_INSERT
): 
 409             self
.CopyWithPrompts() 
 410         # Copy to the clipboard, including prefixed prompts. 
 411         elif altDown 
and not controlDown \
 
 412             and key 
in (ord('C'), ord('c'), wx
.WXK_INSERT
): 
 413             self
.CopyWithPromptsPrefixed() 
 414         # Home needs to be aware of the prompt. 
 415         elif key 
== wx
.WXK_HOME
: 
 416             home 
= self
.promptPosEnd
 
 418                 self
.SetCurrentPos(home
) 
 419                 if not selecting 
and not shiftDown
: 
 421                     self
.EnsureCaretVisible() 
 425         # The following handlers modify text, so we need to see if 
 426         # there is a selection that includes text prior to the prompt. 
 428         # Don't modify a selection with text prior to the prompt. 
 429         elif selecting 
and key 
not in NAVKEYS 
and not self
.CanEdit(): 
 431         # Paste from the clipboard. 
 432         elif (controlDown 
and not shiftDown 
and key 
in (ord('V'), ord('v'))) \
 
 433                  or (shiftDown 
and not controlDown 
and key 
== wx
.WXK_INSERT
): 
 435         # Paste from the clipboard, run commands. 
 436         elif controlDown 
and shiftDown 
and key 
in (ord('V'), ord('v')): 
 438         # Replace with the previous command from the history buffer. 
 439         elif (controlDown 
and key 
== wx
.WXK_UP
) \
 
 440                  or (altDown 
and key 
in (ord('P'), ord('p'))): 
 441             self
.OnHistoryReplace(step
=+1) 
 442         # Replace with the next command from the history buffer. 
 443         elif (controlDown 
and key 
== wx
.WXK_DOWN
) \
 
 444                  or (altDown 
and key 
in (ord('N'), ord('n'))): 
 445             self
.OnHistoryReplace(step
=-1) 
 446         # Insert the previous command from the history buffer. 
 447         elif (shiftDown 
and key 
== wx
.WXK_UP
) and self
.CanEdit(): 
 448             self
.OnHistoryInsert(step
=+1) 
 449         # Insert the next command from the history buffer. 
 450         elif (shiftDown 
and key 
== wx
.WXK_DOWN
) and self
.CanEdit(): 
 451             self
.OnHistoryInsert(step
=-1) 
 452         # Search up the history for the text in front of the cursor. 
 453         elif key 
== wx
.WXK_F8
: 
 454             self
.OnHistorySearch() 
 455         # Don't backspace over the latest non-continuation prompt. 
 456         elif key 
== wx
.WXK_BACK
: 
 457             if selecting 
and self
.CanEdit(): 
 459             elif currpos 
> self
.promptPosEnd
: 
 461         # Only allow these keys after the latest prompt. 
 462         elif key 
in (wx
.WXK_TAB
, wx
.WXK_DELETE
): 
 465         # Don't toggle between insert mode and overwrite mode. 
 466         elif key 
== wx
.WXK_INSERT
: 
 468         # Don't allow line deletion. 
 469         elif controlDown 
and key 
in (ord('L'), ord('l')): 
 471         # Don't allow line transposition. 
 472         elif controlDown 
and key 
in (ord('T'), ord('t')): 
 474         # Basic navigation keys should work anywhere. 
 477         # Protect the readonly portion of the shell. 
 478         elif not self
.CanEdit(): 
 483     def clearCommand(self
): 
 484         """Delete the current, unexecuted command.""" 
 485         startpos 
= self
.promptPosEnd
 
 486         endpos 
= self
.GetTextLength() 
 487         self
.SetSelection(startpos
, endpos
) 
 488         self
.ReplaceSelection('') 
 491     def OnHistoryReplace(self
, step
): 
 492         """Replace with the previous/next command from the history buffer.""" 
 494         self
.replaceFromHistory(step
) 
 496     def replaceFromHistory(self
, step
): 
 497         """Replace selection with command from the history buffer.""" 
 499         self
.ReplaceSelection('') 
 500         newindex 
= self
.historyIndex 
+ step
 
 501         if -1 <= newindex 
<= len(self
.history
): 
 502             self
.historyIndex 
= newindex
 
 503         if 0 <= newindex 
<= len(self
.history
)-1: 
 504             command 
= self
.history
[self
.historyIndex
] 
 505             command 
= command
.replace('\n', os
.linesep 
+ ps2
) 
 506             self
.ReplaceSelection(command
) 
 508     def OnHistoryInsert(self
, step
): 
 509         """Insert the previous/next command from the history buffer.""" 
 510         if not self
.CanEdit(): 
 512         startpos 
= self
.GetCurrentPos() 
 513         self
.replaceFromHistory(step
) 
 514         endpos 
= self
.GetCurrentPos() 
 515         self
.SetSelection(endpos
, startpos
) 
 517     def OnHistorySearch(self
): 
 518         """Search up the history buffer for the text in front of the cursor.""" 
 519         if not self
.CanEdit(): 
 521         startpos 
= self
.GetCurrentPos() 
 522         # The text up to the cursor is what we search for. 
 523         numCharsAfterCursor 
= self
.GetTextLength() - startpos
 
 524         searchText 
= self
.getCommand(rstrip
=False) 
 525         if numCharsAfterCursor 
> 0: 
 526             searchText 
= searchText
[:-numCharsAfterCursor
] 
 529         # Search upwards from the current history position and loop 
 530         # back to the beginning if we don't find anything. 
 531         if (self
.historyIndex 
<= -1) \
 
 532         or (self
.historyIndex 
>= len(self
.history
)-2): 
 533             searchOrder 
= range(len(self
.history
)) 
 535             searchOrder 
= range(self
.historyIndex
+1, len(self
.history
)) + \
 
 536                           range(self
.historyIndex
) 
 537         for i 
in searchOrder
: 
 538             command 
= self
.history
[i
] 
 539             if command
[:len(searchText
)] == searchText
: 
 540                 # Replace the current selection with the one we found. 
 541                 self
.ReplaceSelection(command
[len(searchText
):]) 
 542                 endpos 
= self
.GetCurrentPos() 
 543                 self
.SetSelection(endpos
, startpos
) 
 544                 # We've now warped into middle of the history. 
 545                 self
.historyIndex 
= i
 
 548     def setStatusText(self
, text
): 
 549         """Display status information.""" 
 551         # This method will likely be replaced by the enclosing app to 
 552         # do something more interesting, like write to a status bar. 
 555     def insertLineBreak(self
): 
 556         """Insert a new line break.""" 
 558             self
.write(os
.linesep
) 
 562     def processLine(self
): 
 563         """Process the line of text at which the user hit Enter.""" 
 565         # The user hit ENTER and we need to decide what to do. They 
 566         # could be sitting on any line in the shell. 
 568         thepos 
= self
.GetCurrentPos() 
 569         startpos 
= self
.promptPosEnd
 
 570         endpos 
= self
.GetTextLength() 
 572         # If they hit RETURN inside the current command, execute the 
 575             self
.SetCurrentPos(endpos
) 
 576             self
.interp
.more 
= False 
 577             command 
= self
.GetTextRange(startpos
, endpos
) 
 578             lines 
= command
.split(os
.linesep 
+ ps2
) 
 579             lines 
= [line
.rstrip() for line 
in lines
] 
 580             command 
= '\n'.join(lines
) 
 581             if self
.reader
.isreading
: 
 583                     # Match the behavior of the standard Python shell 
 584                     # when the user hits return without entering a 
 587                 self
.reader
.input = command
 
 588                 self
.write(os
.linesep
) 
 591         # Or replace the current command with the other command. 
 593             # If the line contains a command (even an invalid one). 
 594             if self
.getCommand(rstrip
=False): 
 595                 command 
= self
.getMultilineCommand() 
 598             # Otherwise, put the cursor back where we started. 
 600                 self
.SetCurrentPos(thepos
) 
 601                 self
.SetAnchor(thepos
) 
 603     def getMultilineCommand(self
, rstrip
=True): 
 604         """Extract a multi-line command from the editor. 
 606         The command may not necessarily be valid Python syntax.""" 
 607         # XXX Need to extract real prompts here. Need to keep track of 
 608         # the prompt every time a command is issued. 
 613         # This is a total hack job, but it works. 
 614         text 
= self
.GetCurLine()[0] 
 615         line 
= self
.GetCurrentLine() 
 616         while text
[:ps2size
] == ps2 
and line 
> 0: 
 619             text 
= self
.GetCurLine()[0] 
 620         if text
[:ps1size
] == ps1
: 
 621             line 
= self
.GetCurrentLine() 
 623             startpos 
= self
.GetCurrentPos() + ps1size
 
 626             while self
.GetCurLine()[0][:ps2size
] == ps2
: 
 629             stoppos 
= self
.GetCurrentPos() 
 630             command 
= self
.GetTextRange(startpos
, stoppos
) 
 631             command 
= command
.replace(os
.linesep 
+ ps2
, '\n') 
 632             command 
= command
.rstrip() 
 633             command 
= command
.replace('\n', os
.linesep 
+ ps2
) 
 637             command 
= command
.rstrip() 
 640     def getCommand(self
, text
=None, rstrip
=True): 
 641         """Extract a command from text which may include a shell prompt. 
 643         The command may not necessarily be valid Python syntax.""" 
 645             text 
= self
.GetCurLine()[0] 
 646         # Strip the prompt off the front leaving just the command. 
 647         command 
= self
.lstripPrompt(text
) 
 649             command 
= ''  # Real commands have prompts. 
 651             command 
= command
.rstrip() 
 654     def lstripPrompt(self
, text
): 
 655         """Return text without a leading prompt.""" 
 660         # Strip the prompt off the front of text. 
 661         if text
[:ps1size
] == ps1
: 
 662             text 
= text
[ps1size
:] 
 663         elif text
[:ps2size
] == ps2
: 
 664             text 
= text
[ps2size
:] 
 667     def push(self
, command
): 
 668         """Send command to the interpreter for execution.""" 
 669         self
.write(os
.linesep
) 
 670         busy 
= wx
.BusyCursor() 
 672         self
.more 
= self
.interp
.push(command
) 
 676             self
.addHistory(command
.rstrip()) 
 679     def addHistory(self
, command
): 
 680         """Add command to the command history.""" 
 681         # Reset the history position. 
 682         self
.historyIndex 
= -1 
 683         # Insert this command into the history, unless it's a blank 
 684         # line or the same as the last command. 
 686         and (len(self
.history
) == 0 or command 
!= self
.history
[0]): 
 687             self
.history
.insert(0, command
) 
 689     def write(self
, text
): 
 690         """Display text in the shell. 
 692         Replace line endings with OS-specific endings.""" 
 693         text 
= self
.fixLineEndings(text
) 
 695         self
.EnsureCaretVisible() 
 697     def fixLineEndings(self
, text
): 
 698         """Return text with line endings replaced by OS-specific endings.""" 
 699         lines 
= text
.split('\r\n') 
 700         for l 
in range(len(lines
)): 
 701             chunks 
= lines
[l
].split('\r') 
 702             for c 
in range(len(chunks
)): 
 703                 chunks
[c
] = os
.linesep
.join(chunks
[c
].split('\n')) 
 704             lines
[l
] = os
.linesep
.join(chunks
) 
 705         text 
= os
.linesep
.join(lines
) 
 709         """Display proper prompt for the context: ps1, ps2 or ps3. 
 711         If this is a continuation line, autoindent as necessary.""" 
 712         isreading 
= self
.reader
.isreading
 
 715             prompt 
= str(sys
.ps3
) 
 717             prompt 
= str(sys
.ps2
) 
 719             prompt 
= str(sys
.ps1
) 
 720         pos 
= self
.GetCurLine()[1] 
 725                 self
.write(os
.linesep
) 
 727             self
.promptPosStart 
= self
.GetCurrentPos() 
 731             self
.promptPosEnd 
= self
.GetCurrentPos() 
 732             # Keep the undo feature from undoing previous responses. 
 733             self
.EmptyUndoBuffer() 
 734         # XXX Add some autoindent magic here if more. 
 736             self
.write(' '*4)  # Temporary hack indentation. 
 737         self
.EnsureCaretVisible() 
 738         self
.ScrollToColumn(0) 
 741         """Replacement for stdin.readline().""" 
 744         reader
.isreading 
= True 
 747             while not reader
.input: 
 752             reader
.isreading 
= False 
 753         input = str(input)  # In case of Unicode. 
 757         """Replacement for stdin.readlines().""" 
 759         while lines
[-1:] != ['\n']: 
 760             lines
.append(self
.readline()) 
 763     def raw_input(self
, prompt
=''): 
 764         """Return string based on user input.""" 
 767         return self
.readline() 
 769     def ask(self
, prompt
='Please enter your response:'): 
 770         """Get response from the user using a dialog box.""" 
 771         dialog 
= wx
.TextEntryDialog(None, prompt
, 
 772                                     'Input Dialog (Raw)', '') 
 774             if dialog
.ShowModal() == wx
.ID_OK
: 
 775                 text 
= dialog
.GetValue() 
 782         """Halt execution pending a response from the user.""" 
 783         self
.ask('Press enter to continue:') 
 786         """Delete all text from the shell.""" 
 789     def run(self
, command
, prompt
=True, verbose
=True): 
 790         """Execute command as if it was typed in directly. 
 791         >>> shell.run('print "this"') 
 796         # Go to the very bottom of the text. 
 797         endpos 
= self
.GetTextLength() 
 798         self
.SetCurrentPos(endpos
) 
 799         command 
= command
.rstrip() 
 800         if prompt
: self
.prompt() 
 801         if verbose
: self
.write(command
) 
 804     def runfile(self
, filename
): 
 805         """Execute all commands in file as if they were typed into the 
 807         file = open(filename
) 
 810             for command 
in file.readlines(): 
 811                 if command
[:6] == 'shell.': 
 812                     # Run shell methods silently. 
 813                     self
.run(command
, prompt
=False, verbose
=False) 
 815                     self
.run(command
, prompt
=False, verbose
=True) 
 819     def autoCompleteShow(self
, command
): 
 820         """Display auto-completion popup list.""" 
 821         self
.AutoCompSetAutoHide(self
.autoCompleteAutoHide
) 
 822         self
.AutoCompSetIgnoreCase(self
.autoCompleteCaseInsensitive
) 
 823         list = self
.interp
.getAutoCompleteList(command
, 
 824                     includeMagic
=self
.autoCompleteIncludeMagic
, 
 825                     includeSingle
=self
.autoCompleteIncludeSingle
, 
 826                     includeDouble
=self
.autoCompleteIncludeDouble
) 
 828             options 
= ' '.join(list) 
 830             self
.AutoCompShow(offset
, options
) 
 832     def autoCallTipShow(self
, command
): 
 833         """Display argument spec and docstring in a popup window.""" 
 834         if self
.CallTipActive(): 
 836         (name
, argspec
, tip
) = self
.interp
.getCallTip(command
) 
 838             dispatcher
.send(signal
='Shell.calltip', sender
=self
, calltip
=tip
) 
 839         if not self
.autoCallTip
: 
 842             startpos 
= self
.GetCurrentPos() 
 843             self
.write(argspec 
+ ')') 
 844             endpos 
= self
.GetCurrentPos() 
 845             self
.SetSelection(endpos
, startpos
) 
 847             curpos 
= self
.GetCurrentPos() 
 848             tippos 
= curpos 
- (len(name
) + 1) 
 849             fallback 
= curpos 
- self
.GetColumn(curpos
) 
 850             # In case there isn't enough room, only go back to the 
 852             tippos 
= max(tippos
, fallback
) 
 853             self
.CallTipShow(tippos
, tip
) 
 855     def writeOut(self
, text
): 
 856         """Replacement for stdout.""" 
 859     def writeErr(self
, text
): 
 860         """Replacement for stderr.""" 
 863     def redirectStdin(self
, redirect
=True): 
 864         """If redirect is true then sys.stdin will come from the shell.""" 
 866             sys
.stdin 
= self
.reader
 
 868             sys
.stdin 
= self
.stdin
 
 870     def redirectStdout(self
, redirect
=True): 
 871         """If redirect is true then sys.stdout will go to the shell.""" 
 873             sys
.stdout 
= PseudoFileOut(self
.writeOut
) 
 875             sys
.stdout 
= self
.stdout
 
 877     def redirectStderr(self
, redirect
=True): 
 878         """If redirect is true then sys.stderr will go to the shell.""" 
 880             sys
.stderr 
= PseudoFileErr(self
.writeErr
) 
 882             sys
.stderr 
= self
.stderr
 
 885         """Return true if text is selected and can be cut.""" 
 886         if self
.GetSelectionStart() != self
.GetSelectionEnd() \
 
 887                and self
.GetSelectionStart() >= self
.promptPosEnd \
 
 888                and self
.GetSelectionEnd() >= self
.promptPosEnd
: 
 894         """Return true if a paste should succeed.""" 
 895         if self
.CanEdit() and editwindow
.EditWindow
.CanPaste(self
): 
 901         """Return true if editing should succeed.""" 
 902         if self
.GetSelectionStart() != self
.GetSelectionEnd(): 
 903             if self
.GetSelectionStart() >= self
.promptPosEnd \
 
 904                    and self
.GetSelectionEnd() >= self
.promptPosEnd
: 
 909             return self
.GetCurrentPos() >= self
.promptPosEnd
 
 912         """Remove selection and place it on the clipboard.""" 
 913         if self
.CanCut() and self
.CanCopy(): 
 914             if self
.AutoCompActive(): 
 915                 self
.AutoCompCancel() 
 916             if self
.CallTipActive(): 
 919             self
.ReplaceSelection('') 
 922         """Copy selection and place it on the clipboard.""" 
 926             command 
= self
.GetSelectedText() 
 927             command 
= command
.replace(os
.linesep 
+ ps2
, os
.linesep
) 
 928             command 
= command
.replace(os
.linesep 
+ ps1
, os
.linesep
) 
 929             command 
= self
.lstripPrompt(text
=command
) 
 930             data 
= wx
.TextDataObject(command
) 
 933     def CopyWithPrompts(self
): 
 934         """Copy selection, including prompts, and place it on the clipboard.""" 
 936             command 
= self
.GetSelectedText() 
 937             data 
= wx
.TextDataObject(command
) 
 940     def CopyWithPromptsPrefixed(self
): 
 941         """Copy selection, including prompts prefixed with four 
 942         spaces, and place it on the clipboard.""" 
 944             command 
= self
.GetSelectedText() 
 946             command 
= spaces 
+ command
.replace(os
.linesep
, 
 948             data 
= wx
.TextDataObject(command
) 
 951     def _clip(self
, data
): 
 952         if wx
.TheClipboard
.Open(): 
 953             wx
.TheClipboard
.UsePrimarySelection(False) 
 954             wx
.TheClipboard
.SetData(data
) 
 955             wx
.TheClipboard
.Flush() 
 956             wx
.TheClipboard
.Close() 
 959         """Replace selection with clipboard contents.""" 
 960         if self
.CanPaste() and wx
.TheClipboard
.Open(): 
 962             if wx
.TheClipboard
.IsSupported(wx
.DataFormat(wx
.DF_TEXT
)): 
 963                 data 
= wx
.TextDataObject() 
 964                 if wx
.TheClipboard
.GetData(data
): 
 965                     self
.ReplaceSelection('') 
 966                     command 
= data
.GetText() 
 967                     command 
= command
.rstrip() 
 968                     command 
= self
.fixLineEndings(command
) 
 969                     command 
= self
.lstripPrompt(text
=command
) 
 970                     command 
= command
.replace(os
.linesep 
+ ps2
, '\n') 
 971                     command 
= command
.replace(os
.linesep
, '\n') 
 972                     command 
= command
.replace('\n', os
.linesep 
+ ps2
) 
 974             wx
.TheClipboard
.Close() 
 976     def PasteAndRun(self
): 
 977         """Replace selection with clipboard contents, run commands.""" 
 978         if wx
.TheClipboard
.Open(): 
 981             if wx
.TheClipboard
.IsSupported(wx
.DataFormat(wx
.DF_TEXT
)): 
 982                 data 
= wx
.TextDataObject() 
 983                 if wx
.TheClipboard
.GetData(data
): 
 984                     endpos 
= self
.GetTextLength() 
 985                     self
.SetCurrentPos(endpos
) 
 986                     startpos 
= self
.promptPosEnd
 
 987                     self
.SetSelection(startpos
, endpos
) 
 988                     self
.ReplaceSelection('') 
 989                     text 
= data
.GetText() 
 991                     text 
= self
.fixLineEndings(text
) 
 992                     text 
= self
.lstripPrompt(text
) 
 993                     text 
= text
.replace(os
.linesep 
+ ps1
, '\n') 
 994                     text 
= text
.replace(os
.linesep 
+ ps2
, '\n') 
 995                     text 
= text
.replace(os
.linesep
, '\n') 
 996                     lines 
= text
.split('\n') 
1000                         if line
.strip() == ps2
.strip(): 
1001                             # If we are pasting from something like a 
1002                             # web page that drops the trailing space 
1003                             # from the ps2 prompt of a blank line. 
1005                         if line
.strip() != '' and line
.lstrip() == line
: 
1008                                 # Add the previous command to the list. 
1009                                 commands
.append(command
) 
1010                             # Start a new command, which may be multiline. 
1013                             # Multiline command. Add to the command. 
1016                     commands
.append(command
) 
1017                     for command 
in commands
: 
1018                         command 
= command
.replace('\n', os
.linesep 
+ ps2
) 
1021             wx
.TheClipboard
.Close() 
1023     def wrap(self
, wrap
=True): 
1024         """Sets whether text is word wrapped.""" 
1026             self
.SetWrapMode(wrap
) 
1027         except AttributeError: 
1028             return 'Wrapping is not available in this version.' 
1030     def zoom(self
, points
=0): 
1031         """Set the zoom level. 
1033         This number of points is added to the size of all fonts.  It 
1034         may be positive to magnify or negative to reduce.""" 
1035         self
.SetZoom(points
)