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                'Platform: %s\n' % sys
.platform 
+ \
 
  76                'Python Version: %s\n' % sys
.version
.split()[0] + \
 
  77                'wxPython Version: %s\n' % wx
.VERSION_STRING 
+ \
 
  78                ('\t(%s)\n' % ", ".join(wx
.PlatformInfo
[1:]))  
  79         dialog 
= wx
.MessageDialog(self
, text
, title
, 
  80                                   wx
.OK | wx
.ICON_INFORMATION
) 
  86     """Simplified interface to all shell-related functionality. 
  88     This is a semi-transparent facade, in that all attributes of other 
  89     are accessible, even though only some are visible to the user.""" 
  91     name 
= 'Shell Interface' 
  92     revision 
= __revision__
 
  94     def __init__(self
, other
): 
  95         """Create a ShellFacade instance.""" 
 101 Home              Go to the beginning of the command or line. 
 102 Shift+Home        Select to the beginning of the command or line. 
 103 Shift+End         Select to the end of the line. 
 104 End               Go to the end of the line. 
 105 Ctrl+C            Copy selected text, removing prompts. 
 106 Ctrl+Shift+C      Copy selected text, retaining prompts. 
 107 Ctrl+X            Cut selected text. 
 108 Ctrl+V            Paste from clipboard. 
 109 Ctrl+Shift+V      Paste and run multiple commands from clipboard. 
 110 Ctrl+Up Arrow     Retrieve Previous History item. 
 111 Alt+P             Retrieve Previous History item. 
 112 Ctrl+Down Arrow   Retrieve Next History item. 
 113 Alt+N             Retrieve Next History item. 
 114 Shift+Up Arrow    Insert Previous History item. 
 115 Shift+Down Arrow  Insert Next History item. 
 116 F8                Command-completion of History item. 
 117                   (Type a few characters of a previous command and press F8.) 
 118 Ctrl+Enter        Insert new line into multiline command. 
 119 Ctrl+]            Increase font size. 
 120 Ctrl+[            Decrease font size. 
 121 Ctrl+=            Default font size. 
 125         """Display some useful information about how to use the shell.""" 
 126         self
.write(self
.helpText
) 
 128     def __getattr__(self
, name
): 
 129         if hasattr(self
.other
, name
): 
 130             return getattr(self
.other
, name
) 
 132             raise AttributeError, name
 
 134     def __setattr__(self
, name
, value
): 
 135         if self
.__dict
__.has_key(name
): 
 136             self
.__dict
__[name
] = value
 
 137         elif hasattr(self
.other
, name
): 
 138             setattr(self
.other
, name
, value
) 
 140             raise AttributeError, name
 
 142     def _getAttributeNames(self
): 
 143         """Return list of magic attributes to extend introspection.""" 
 149             'autoCompleteAutoHide', 
 150             'autoCompleteCaseInsensitive', 
 151             'autoCompleteIncludeDouble', 
 152             'autoCompleteIncludeMagic', 
 153             'autoCompleteIncludeSingle', 
 170 class Shell(editwindow
.EditWindow
): 
 171     """Shell based on StyledTextCtrl.""" 
 174     revision 
= __revision__
 
 176     def __init__(self
, parent
, id=-1, pos
=wx
.DefaultPosition
, 
 177                  size
=wx
.DefaultSize
, style
=wx
.CLIP_CHILDREN
, 
 178                  introText
='', locals=None, InterpClass
=None, *args
, **kwds
): 
 179         """Create Shell instance.""" 
 180         editwindow
.EditWindow
.__init
__(self
, parent
, id, pos
, size
, style
) 
 184             locals = __main__
.__dict
__ 
 185         # Grab these so they can be restored by self.redirect* methods. 
 186         self
.stdin 
= sys
.stdin
 
 187         self
.stdout 
= sys
.stdout
 
 188         self
.stderr 
= sys
.stderr
 
 189         # Import a default interpreter class if one isn't provided. 
 190         if InterpClass 
== None: 
 191             from interpreter 
import Interpreter
 
 193             Interpreter 
= InterpClass
 
 194         # Create a replacement for stdin. 
 195         self
.reader 
= PseudoFileIn(self
.readline
, self
.readlines
) 
 196         self
.reader
.input = '' 
 197         self
.reader
.isreading 
= False 
 198         # Set up the interpreter. 
 199         self
.interp 
= Interpreter(locals=locals, 
 200                                   rawin
=self
.raw_input, 
 202                                   stdout
=PseudoFileOut(self
.writeOut
), 
 203                                   stderr
=PseudoFileErr(self
.writeErr
), 
 206         self
.buffer = Buffer() 
 207         # Find out for which keycodes the interpreter will autocomplete. 
 208         self
.autoCompleteKeys 
= self
.interp
.getAutoCompleteKeys() 
 209         # Keep track of the last non-continuation prompt positions. 
 210         self
.promptPosStart 
= 0 
 211         self
.promptPosEnd 
= 0 
 212         # Keep track of multi-line commands. 
 214         # Create the command history.  Commands are added into the 
 215         # front of the list (ie. at index 0) as they are entered. 
 216         # self.historyIndex is the current position in the history; it 
 217         # gets incremented as you retrieve the previous command, 
 218         # decremented as you retrieve the next, and reset when you hit 
 219         # Enter.  self.historyIndex == -1 means you're on the current 
 220         # command, not in the history. 
 222         self
.historyIndex 
= -1 
 223         # Assign handlers for keyboard events. 
 224         wx
.EVT_CHAR(self
, self
.OnChar
) 
 225         wx
.EVT_KEY_DOWN(self
, self
.OnKeyDown
) 
 226         # Assign handler for idle time. 
 228         wx
.EVT_IDLE(self
, self
.OnIdle
) 
 229         # Display the introductory banner information. 
 230         self
.showIntro(introText
) 
 231         # Assign some pseudo keywords to the interpreter's namespace. 
 232         self
.setBuiltinKeywords() 
 233         # Add 'shell' to the interpreter's local namespace. 
 235         # Do this last so the user has complete control over their 
 236         # environment.  They can override anything they want. 
 237         self
.execStartupScript(self
.interp
.startupScript
) 
 238         wx
.CallAfter(self
.ScrollToLine
, 0) 
 244         """Set focus to the shell.""" 
 247     def OnIdle(self
, event
): 
 248         """Free the CPU to do other things.""" 
 253     def showIntro(self
, text
=''): 
 254         """Display introductory text in the shell.""" 
 256             if not text
.endswith(os
.linesep
): 
 260             self
.write(self
.interp
.introText
) 
 261         except AttributeError: 
 264     def setBuiltinKeywords(self
): 
 265         """Create pseudo keywords as part of builtins. 
 267         This sets `close`, `exit` and `quit` to a helpful string. 
 270         __builtin__
.close 
= __builtin__
.exit 
= __builtin__
.quit 
= \
 
 271             'Click on the close button to leave the application.' 
 274         """Quit the application.""" 
 276         # XXX Good enough for now but later we want to send a close event. 
 278         # In the close event handler we can make sure they want to 
 279         # quit.  Other applications, like PythonCard, may choose to 
 280         # hide rather than quit so we should just post the event and 
 281         # let the surrounding app decide what it wants to do. 
 282         self
.write('Click on the close button to leave the application.') 
 284     def setLocalShell(self
): 
 285         """Add 'shell' to locals as reference to ShellFacade instance.""" 
 286         self
.interp
.locals['shell'] = ShellFacade(other
=self
) 
 288     def execStartupScript(self
, startupScript
): 
 289         """Execute the user's PYTHONSTARTUP script if they have one.""" 
 290         if startupScript 
and os
.path
.isfile(startupScript
): 
 291             text 
= 'Startup script executed: ' + startupScript
 
 292             self
.push('print %r; execfile(%r)' % (text
, startupScript
)) 
 297         """Display information about Py.""" 
 301 Py Shell Revision: %s 
 302 Py Interpreter Revision: %s 
 306         (__author__
, VERSION
, self
.revision
, self
.interp
.revision
, 
 307          sys
.version
.split()[0], wx
.VERSION_STRING
, sys
.platform
) 
 308         self
.write(text
.strip()) 
 310     def OnChar(self
, event
): 
 311         """Keypress event handler. 
 313         Only receives an event if OnKeyDown calls event.Skip() for the 
 314         corresponding event.""" 
 316         # Prevent modification of previously submitted 
 317         # commands/responses. 
 318         if not self
.CanEdit(): 
 320         key 
= event
.KeyCode() 
 321         currpos 
= self
.GetCurrentPos() 
 322         stoppos 
= self
.promptPosEnd
 
 323         # Return (Enter) needs to be ignored in this handler. 
 324         if key 
== wx
.WXK_RETURN
: 
 326         elif key 
in self
.autoCompleteKeys
: 
 327             # Usually the dot (period) key activates auto completion. 
 328             # Get the command between the prompt and the cursor.  Add 
 329             # the autocomplete character to the end of the command. 
 330             if self
.AutoCompActive(): 
 331                 self
.AutoCompCancel() 
 332             command 
= self
.GetTextRange(stoppos
, currpos
) + chr(key
) 
 334             if self
.autoComplete
: 
 335                 self
.autoCompleteShow(command
) 
 336         elif key 
== ord('('): 
 337             # The left paren activates a call tip and cancels an 
 338             # active auto completion. 
 339             if self
.AutoCompActive(): 
 340                 self
.AutoCompCancel() 
 341             # Get the command between the prompt and the cursor.  Add 
 342             # the '(' to the end of the command. 
 343             self
.ReplaceSelection('') 
 344             command 
= self
.GetTextRange(stoppos
, currpos
) + '(' 
 346             self
.autoCallTipShow(command
) 
 348             # Allow the normal event handling to take place. 
 351     def OnKeyDown(self
, event
): 
 352         """Key down event handler.""" 
 354         key 
= event
.KeyCode() 
 355         # If the auto-complete window is up let it do its thing. 
 356         if self
.AutoCompActive(): 
 359         # Prevent modification of previously submitted 
 360         # commands/responses. 
 361         controlDown 
= event
.ControlDown() 
 362         altDown 
= event
.AltDown() 
 363         shiftDown 
= event
.ShiftDown() 
 364         currpos 
= self
.GetCurrentPos() 
 365         endpos 
= self
.GetTextLength() 
 366         selecting 
= self
.GetSelectionStart() != self
.GetSelectionEnd() 
 367         # Return (Enter) is used to submit a command to the 
 369         if not controlDown 
and key 
== wx
.WXK_RETURN
: 
 370             if self
.CallTipActive(): 
 373         # Ctrl+Return (Cntrl+Enter) is used to insert a line break. 
 374         elif controlDown 
and key 
== wx
.WXK_RETURN
: 
 375             if self
.CallTipActive(): 
 377             if currpos 
== endpos
: 
 380                 self
.insertLineBreak() 
 381         # Let Ctrl-Alt-* get handled normally. 
 382         elif controlDown 
and altDown
: 
 384         # Clear the current, unexecuted command. 
 385         elif key 
== wx
.WXK_ESCAPE
: 
 386             if self
.CallTipActive(): 
 390         # Increase font size. 
 391         elif controlDown 
and key 
in (ord(']'),): 
 392             dispatcher
.send(signal
='FontIncrease') 
 393         # Decrease font size. 
 394         elif controlDown 
and key 
in (ord('['),): 
 395             dispatcher
.send(signal
='FontDecrease') 
 397         elif controlDown 
and key 
in (ord('='),): 
 398             dispatcher
.send(signal
='FontDefault') 
 399         # Cut to the clipboard. 
 400         elif (controlDown 
and key 
in (ord('X'), ord('x'))) \
 
 401         or (shiftDown 
and key 
== wx
.WXK_DELETE
): 
 403         # Copy to the clipboard. 
 404         elif controlDown 
and not shiftDown \
 
 405             and key 
in (ord('C'), ord('c'), wx
.WXK_INSERT
): 
 407         # Copy to the clipboard, including prompts. 
 408         elif controlDown 
and shiftDown \
 
 409             and key 
in (ord('C'), ord('c'), wx
.WXK_INSERT
): 
 410             self
.CopyWithPrompts() 
 411         # Copy to the clipboard, including prefixed prompts. 
 412         elif altDown 
and not controlDown \
 
 413             and key 
in (ord('C'), ord('c'), wx
.WXK_INSERT
): 
 414             self
.CopyWithPromptsPrefixed() 
 415         # Home needs to be aware of the prompt. 
 416         elif key 
== wx
.WXK_HOME
: 
 417             home 
= self
.promptPosEnd
 
 419                 self
.SetCurrentPos(home
) 
 420                 if not selecting 
and not shiftDown
: 
 422                     self
.EnsureCaretVisible() 
 426         # The following handlers modify text, so we need to see if 
 427         # there is a selection that includes text prior to the prompt. 
 429         # Don't modify a selection with text prior to the prompt. 
 430         elif selecting 
and key 
not in NAVKEYS 
and not self
.CanEdit(): 
 432         # Paste from the clipboard. 
 433         elif (controlDown 
and not shiftDown 
and key 
in (ord('V'), ord('v'))) \
 
 434                  or (shiftDown 
and not controlDown 
and key 
== wx
.WXK_INSERT
): 
 436         # Paste from the clipboard, run commands. 
 437         elif controlDown 
and shiftDown 
and key 
in (ord('V'), ord('v')): 
 439         # Replace with the previous command from the history buffer. 
 440         elif (controlDown 
and key 
== wx
.WXK_UP
) \
 
 441                  or (altDown 
and key 
in (ord('P'), ord('p'))): 
 442             self
.OnHistoryReplace(step
=+1) 
 443         # Replace with the next command from the history buffer. 
 444         elif (controlDown 
and key 
== wx
.WXK_DOWN
) \
 
 445                  or (altDown 
and key 
in (ord('N'), ord('n'))): 
 446             self
.OnHistoryReplace(step
=-1) 
 447         # Insert the previous command from the history buffer. 
 448         elif (shiftDown 
and key 
== wx
.WXK_UP
) and self
.CanEdit(): 
 449             self
.OnHistoryInsert(step
=+1) 
 450         # Insert the next command from the history buffer. 
 451         elif (shiftDown 
and key 
== wx
.WXK_DOWN
) and self
.CanEdit(): 
 452             self
.OnHistoryInsert(step
=-1) 
 453         # Search up the history for the text in front of the cursor. 
 454         elif key 
== wx
.WXK_F8
: 
 455             self
.OnHistorySearch() 
 456         # Don't backspace over the latest non-continuation prompt. 
 457         elif key 
== wx
.WXK_BACK
: 
 458             if selecting 
and self
.CanEdit(): 
 460             elif currpos 
> self
.promptPosEnd
: 
 462         # Only allow these keys after the latest prompt. 
 463         elif key 
in (wx
.WXK_TAB
, wx
.WXK_DELETE
): 
 466         # Don't toggle between insert mode and overwrite mode. 
 467         elif key 
== wx
.WXK_INSERT
: 
 469         # Don't allow line deletion. 
 470         elif controlDown 
and key 
in (ord('L'), ord('l')): 
 472         # Don't allow line transposition. 
 473         elif controlDown 
and key 
in (ord('T'), ord('t')): 
 475         # Basic navigation keys should work anywhere. 
 478         # Protect the readonly portion of the shell. 
 479         elif not self
.CanEdit(): 
 484     def clearCommand(self
): 
 485         """Delete the current, unexecuted command.""" 
 486         startpos 
= self
.promptPosEnd
 
 487         endpos 
= self
.GetTextLength() 
 488         self
.SetSelection(startpos
, endpos
) 
 489         self
.ReplaceSelection('') 
 492     def OnHistoryReplace(self
, step
): 
 493         """Replace with the previous/next command from the history buffer.""" 
 495         self
.replaceFromHistory(step
) 
 497     def replaceFromHistory(self
, step
): 
 498         """Replace selection with command from the history buffer.""" 
 500         self
.ReplaceSelection('') 
 501         newindex 
= self
.historyIndex 
+ step
 
 502         if -1 <= newindex 
<= len(self
.history
): 
 503             self
.historyIndex 
= newindex
 
 504         if 0 <= newindex 
<= len(self
.history
)-1: 
 505             command 
= self
.history
[self
.historyIndex
] 
 506             command 
= command
.replace('\n', os
.linesep 
+ ps2
) 
 507             self
.ReplaceSelection(command
) 
 509     def OnHistoryInsert(self
, step
): 
 510         """Insert the previous/next command from the history buffer.""" 
 511         if not self
.CanEdit(): 
 513         startpos 
= self
.GetCurrentPos() 
 514         self
.replaceFromHistory(step
) 
 515         endpos 
= self
.GetCurrentPos() 
 516         self
.SetSelection(endpos
, startpos
) 
 518     def OnHistorySearch(self
): 
 519         """Search up the history buffer for the text in front of the cursor.""" 
 520         if not self
.CanEdit(): 
 522         startpos 
= self
.GetCurrentPos() 
 523         # The text up to the cursor is what we search for. 
 524         numCharsAfterCursor 
= self
.GetTextLength() - startpos
 
 525         searchText 
= self
.getCommand(rstrip
=False) 
 526         if numCharsAfterCursor 
> 0: 
 527             searchText 
= searchText
[:-numCharsAfterCursor
] 
 530         # Search upwards from the current history position and loop 
 531         # back to the beginning if we don't find anything. 
 532         if (self
.historyIndex 
<= -1) \
 
 533         or (self
.historyIndex 
>= len(self
.history
)-2): 
 534             searchOrder 
= range(len(self
.history
)) 
 536             searchOrder 
= range(self
.historyIndex
+1, len(self
.history
)) + \
 
 537                           range(self
.historyIndex
) 
 538         for i 
in searchOrder
: 
 539             command 
= self
.history
[i
] 
 540             if command
[:len(searchText
)] == searchText
: 
 541                 # Replace the current selection with the one we found. 
 542                 self
.ReplaceSelection(command
[len(searchText
):]) 
 543                 endpos 
= self
.GetCurrentPos() 
 544                 self
.SetSelection(endpos
, startpos
) 
 545                 # We've now warped into middle of the history. 
 546                 self
.historyIndex 
= i
 
 549     def setStatusText(self
, text
): 
 550         """Display status information.""" 
 552         # This method will likely be replaced by the enclosing app to 
 553         # do something more interesting, like write to a status bar. 
 556     def insertLineBreak(self
): 
 557         """Insert a new line break.""" 
 559             self
.write(os
.linesep
) 
 563     def processLine(self
): 
 564         """Process the line of text at which the user hit Enter.""" 
 566         # The user hit ENTER and we need to decide what to do. They 
 567         # could be sitting on any line in the shell. 
 569         thepos 
= self
.GetCurrentPos() 
 570         startpos 
= self
.promptPosEnd
 
 571         endpos 
= self
.GetTextLength() 
 573         # If they hit RETURN inside the current command, execute the 
 576             self
.SetCurrentPos(endpos
) 
 577             self
.interp
.more 
= False 
 578             command 
= self
.GetTextRange(startpos
, endpos
) 
 579             lines 
= command
.split(os
.linesep 
+ ps2
) 
 580             lines 
= [line
.rstrip() for line 
in lines
] 
 581             command 
= '\n'.join(lines
) 
 582             if self
.reader
.isreading
: 
 584                     # Match the behavior of the standard Python shell 
 585                     # when the user hits return without entering a 
 588                 self
.reader
.input = command
 
 589                 self
.write(os
.linesep
) 
 592         # Or replace the current command with the other command. 
 594             # If the line contains a command (even an invalid one). 
 595             if self
.getCommand(rstrip
=False): 
 596                 command 
= self
.getMultilineCommand() 
 599             # Otherwise, put the cursor back where we started. 
 601                 self
.SetCurrentPos(thepos
) 
 602                 self
.SetAnchor(thepos
) 
 604     def getMultilineCommand(self
, rstrip
=True): 
 605         """Extract a multi-line command from the editor. 
 607         The command may not necessarily be valid Python syntax.""" 
 608         # XXX Need to extract real prompts here. Need to keep track of 
 609         # the prompt every time a command is issued. 
 614         # This is a total hack job, but it works. 
 615         text 
= self
.GetCurLine()[0] 
 616         line 
= self
.GetCurrentLine() 
 617         while text
[:ps2size
] == ps2 
and line 
> 0: 
 620             text 
= self
.GetCurLine()[0] 
 621         if text
[:ps1size
] == ps1
: 
 622             line 
= self
.GetCurrentLine() 
 624             startpos 
= self
.GetCurrentPos() + ps1size
 
 627             while self
.GetCurLine()[0][:ps2size
] == ps2
: 
 630             stoppos 
= self
.GetCurrentPos() 
 631             command 
= self
.GetTextRange(startpos
, stoppos
) 
 632             command 
= command
.replace(os
.linesep 
+ ps2
, '\n') 
 633             command 
= command
.rstrip() 
 634             command 
= command
.replace('\n', os
.linesep 
+ ps2
) 
 638             command 
= command
.rstrip() 
 641     def getCommand(self
, text
=None, rstrip
=True): 
 642         """Extract a command from text which may include a shell prompt. 
 644         The command may not necessarily be valid Python syntax.""" 
 646             text 
= self
.GetCurLine()[0] 
 647         # Strip the prompt off the front leaving just the command. 
 648         command 
= self
.lstripPrompt(text
) 
 650             command 
= ''  # Real commands have prompts. 
 652             command 
= command
.rstrip() 
 655     def lstripPrompt(self
, text
): 
 656         """Return text without a leading prompt.""" 
 661         # Strip the prompt off the front of text. 
 662         if text
[:ps1size
] == ps1
: 
 663             text 
= text
[ps1size
:] 
 664         elif text
[:ps2size
] == ps2
: 
 665             text 
= text
[ps2size
:] 
 668     def push(self
, command
): 
 669         """Send command to the interpreter for execution.""" 
 670         self
.write(os
.linesep
) 
 671         busy 
= wx
.BusyCursor() 
 673         self
.more 
= self
.interp
.push(command
) 
 677             self
.addHistory(command
.rstrip()) 
 680     def addHistory(self
, command
): 
 681         """Add command to the command history.""" 
 682         # Reset the history position. 
 683         self
.historyIndex 
= -1 
 684         # Insert this command into the history, unless it's a blank 
 685         # line or the same as the last command. 
 687         and (len(self
.history
) == 0 or command 
!= self
.history
[0]): 
 688             self
.history
.insert(0, command
) 
 690     def write(self
, text
): 
 691         """Display text in the shell. 
 693         Replace line endings with OS-specific endings.""" 
 694         text 
= self
.fixLineEndings(text
) 
 696         self
.EnsureCaretVisible() 
 698     def fixLineEndings(self
, text
): 
 699         """Return text with line endings replaced by OS-specific endings.""" 
 700         lines 
= text
.split('\r\n') 
 701         for l 
in range(len(lines
)): 
 702             chunks 
= lines
[l
].split('\r') 
 703             for c 
in range(len(chunks
)): 
 704                 chunks
[c
] = os
.linesep
.join(chunks
[c
].split('\n')) 
 705             lines
[l
] = os
.linesep
.join(chunks
) 
 706         text 
= os
.linesep
.join(lines
) 
 710         """Display proper prompt for the context: ps1, ps2 or ps3. 
 712         If this is a continuation line, autoindent as necessary.""" 
 713         isreading 
= self
.reader
.isreading
 
 716             prompt 
= str(sys
.ps3
) 
 718             prompt 
= str(sys
.ps2
) 
 720             prompt 
= str(sys
.ps1
) 
 721         pos 
= self
.GetCurLine()[1] 
 726                 self
.write(os
.linesep
) 
 728             self
.promptPosStart 
= self
.GetCurrentPos() 
 732             self
.promptPosEnd 
= self
.GetCurrentPos() 
 733             # Keep the undo feature from undoing previous responses. 
 734             self
.EmptyUndoBuffer() 
 735         # XXX Add some autoindent magic here if more. 
 737             self
.write(' '*4)  # Temporary hack indentation. 
 738         self
.EnsureCaretVisible() 
 739         self
.ScrollToColumn(0) 
 742         """Replacement for stdin.readline().""" 
 745         reader
.isreading 
= True 
 748             while not reader
.input: 
 753             reader
.isreading 
= False 
 754         input = str(input)  # In case of Unicode. 
 758         """Replacement for stdin.readlines().""" 
 760         while lines
[-1:] != ['\n']: 
 761             lines
.append(self
.readline()) 
 764     def raw_input(self
, prompt
=''): 
 765         """Return string based on user input.""" 
 768         return self
.readline() 
 770     def ask(self
, prompt
='Please enter your response:'): 
 771         """Get response from the user using a dialog box.""" 
 772         dialog 
= wx
.TextEntryDialog(None, prompt
, 
 773                                     'Input Dialog (Raw)', '') 
 775             if dialog
.ShowModal() == wx
.ID_OK
: 
 776                 text 
= dialog
.GetValue() 
 783         """Halt execution pending a response from the user.""" 
 784         self
.ask('Press enter to continue:') 
 787         """Delete all text from the shell.""" 
 790     def run(self
, command
, prompt
=True, verbose
=True): 
 791         """Execute command as if it was typed in directly. 
 792         >>> shell.run('print "this"') 
 797         # Go to the very bottom of the text. 
 798         endpos 
= self
.GetTextLength() 
 799         self
.SetCurrentPos(endpos
) 
 800         command 
= command
.rstrip() 
 801         if prompt
: self
.prompt() 
 802         if verbose
: self
.write(command
) 
 805     def runfile(self
, filename
): 
 806         """Execute all commands in file as if they were typed into the 
 808         file = open(filename
) 
 811             for command 
in file.readlines(): 
 812                 if command
[:6] == 'shell.': 
 813                     # Run shell methods silently. 
 814                     self
.run(command
, prompt
=False, verbose
=False) 
 816                     self
.run(command
, prompt
=False, verbose
=True) 
 820     def autoCompleteShow(self
, command
): 
 821         """Display auto-completion popup list.""" 
 822         self
.AutoCompSetAutoHide(self
.autoCompleteAutoHide
) 
 823         self
.AutoCompSetIgnoreCase(self
.autoCompleteCaseInsensitive
) 
 824         list = self
.interp
.getAutoCompleteList(command
, 
 825                     includeMagic
=self
.autoCompleteIncludeMagic
, 
 826                     includeSingle
=self
.autoCompleteIncludeSingle
, 
 827                     includeDouble
=self
.autoCompleteIncludeDouble
) 
 829             options 
= ' '.join(list) 
 831             self
.AutoCompShow(offset
, options
) 
 833     def autoCallTipShow(self
, command
): 
 834         """Display argument spec and docstring in a popup window.""" 
 835         if self
.CallTipActive(): 
 837         (name
, argspec
, tip
) = self
.interp
.getCallTip(command
) 
 839             dispatcher
.send(signal
='Shell.calltip', sender
=self
, calltip
=tip
) 
 840         if not self
.autoCallTip
: 
 843             startpos 
= self
.GetCurrentPos() 
 844             self
.write(argspec 
+ ')') 
 845             endpos 
= self
.GetCurrentPos() 
 846             self
.SetSelection(endpos
, startpos
) 
 848             curpos 
= self
.GetCurrentPos() 
 849             tippos 
= curpos 
- (len(name
) + 1) 
 850             fallback 
= curpos 
- self
.GetColumn(curpos
) 
 851             # In case there isn't enough room, only go back to the 
 853             tippos 
= max(tippos
, fallback
) 
 854             self
.CallTipShow(tippos
, tip
) 
 856     def writeOut(self
, text
): 
 857         """Replacement for stdout.""" 
 860     def writeErr(self
, text
): 
 861         """Replacement for stderr.""" 
 864     def redirectStdin(self
, redirect
=True): 
 865         """If redirect is true then sys.stdin will come from the shell.""" 
 867             sys
.stdin 
= self
.reader
 
 869             sys
.stdin 
= self
.stdin
 
 871     def redirectStdout(self
, redirect
=True): 
 872         """If redirect is true then sys.stdout will go to the shell.""" 
 874             sys
.stdout 
= PseudoFileOut(self
.writeOut
) 
 876             sys
.stdout 
= self
.stdout
 
 878     def redirectStderr(self
, redirect
=True): 
 879         """If redirect is true then sys.stderr will go to the shell.""" 
 881             sys
.stderr 
= PseudoFileErr(self
.writeErr
) 
 883             sys
.stderr 
= self
.stderr
 
 886         """Return true if text is selected and can be cut.""" 
 887         if self
.GetSelectionStart() != self
.GetSelectionEnd() \
 
 888                and self
.GetSelectionStart() >= self
.promptPosEnd \
 
 889                and self
.GetSelectionEnd() >= self
.promptPosEnd
: 
 895         """Return true if a paste should succeed.""" 
 896         if self
.CanEdit() and editwindow
.EditWindow
.CanPaste(self
): 
 902         """Return true if editing should succeed.""" 
 903         if self
.GetSelectionStart() != self
.GetSelectionEnd(): 
 904             if self
.GetSelectionStart() >= self
.promptPosEnd \
 
 905                    and self
.GetSelectionEnd() >= self
.promptPosEnd
: 
 910             return self
.GetCurrentPos() >= self
.promptPosEnd
 
 913         """Remove selection and place it on the clipboard.""" 
 914         if self
.CanCut() and self
.CanCopy(): 
 915             if self
.AutoCompActive(): 
 916                 self
.AutoCompCancel() 
 917             if self
.CallTipActive(): 
 920             self
.ReplaceSelection('') 
 923         """Copy selection and place it on the clipboard.""" 
 927             command 
= self
.GetSelectedText() 
 928             command 
= command
.replace(os
.linesep 
+ ps2
, os
.linesep
) 
 929             command 
= command
.replace(os
.linesep 
+ ps1
, os
.linesep
) 
 930             command 
= self
.lstripPrompt(text
=command
) 
 931             data 
= wx
.TextDataObject(command
) 
 934     def CopyWithPrompts(self
): 
 935         """Copy selection, including prompts, and place it on the clipboard.""" 
 937             command 
= self
.GetSelectedText() 
 938             data 
= wx
.TextDataObject(command
) 
 941     def CopyWithPromptsPrefixed(self
): 
 942         """Copy selection, including prompts prefixed with four 
 943         spaces, and place it on the clipboard.""" 
 945             command 
= self
.GetSelectedText() 
 947             command 
= spaces 
+ command
.replace(os
.linesep
, 
 949             data 
= wx
.TextDataObject(command
) 
 952     def _clip(self
, data
): 
 953         if wx
.TheClipboard
.Open(): 
 954             wx
.TheClipboard
.UsePrimarySelection(False) 
 955             wx
.TheClipboard
.SetData(data
) 
 956             wx
.TheClipboard
.Flush() 
 957             wx
.TheClipboard
.Close() 
 960         """Replace selection with clipboard contents.""" 
 961         if self
.CanPaste() and wx
.TheClipboard
.Open(): 
 963             if wx
.TheClipboard
.IsSupported(wx
.DataFormat(wx
.DF_TEXT
)): 
 964                 data 
= wx
.TextDataObject() 
 965                 if wx
.TheClipboard
.GetData(data
): 
 966                     self
.ReplaceSelection('') 
 967                     command 
= data
.GetText() 
 968                     command 
= command
.rstrip() 
 969                     command 
= self
.fixLineEndings(command
) 
 970                     command 
= self
.lstripPrompt(text
=command
) 
 971                     command 
= command
.replace(os
.linesep 
+ ps2
, '\n') 
 972                     command 
= command
.replace(os
.linesep
, '\n') 
 973                     command 
= command
.replace('\n', os
.linesep 
+ ps2
) 
 975             wx
.TheClipboard
.Close() 
 977     def PasteAndRun(self
): 
 978         """Replace selection with clipboard contents, run commands.""" 
 979         if wx
.TheClipboard
.Open(): 
 982             if wx
.TheClipboard
.IsSupported(wx
.DataFormat(wx
.DF_TEXT
)): 
 983                 data 
= wx
.TextDataObject() 
 984                 if wx
.TheClipboard
.GetData(data
): 
 985                     endpos 
= self
.GetTextLength() 
 986                     self
.SetCurrentPos(endpos
) 
 987                     startpos 
= self
.promptPosEnd
 
 988                     self
.SetSelection(startpos
, endpos
) 
 989                     self
.ReplaceSelection('') 
 990                     text 
= data
.GetText() 
 992                     text 
= self
.fixLineEndings(text
) 
 993                     text 
= self
.lstripPrompt(text
) 
 994                     text 
= text
.replace(os
.linesep 
+ ps1
, '\n') 
 995                     text 
= text
.replace(os
.linesep 
+ ps2
, '\n') 
 996                     text 
= text
.replace(os
.linesep
, '\n') 
 997                     lines 
= text
.split('\n') 
1001                         if line
.strip() == ps2
.strip(): 
1002                             # If we are pasting from something like a 
1003                             # web page that drops the trailing space 
1004                             # from the ps2 prompt of a blank line. 
1006                         if line
.strip() != '' and line
.lstrip() == line
: 
1009                                 # Add the previous command to the list. 
1010                                 commands
.append(command
) 
1011                             # Start a new command, which may be multiline. 
1014                             # Multiline command. Add to the command. 
1017                     commands
.append(command
) 
1018                     for command 
in commands
: 
1019                         command 
= command
.replace('\n', os
.linesep 
+ ps2
) 
1022             wx
.TheClipboard
.Close() 
1024     def wrap(self
, wrap
=True): 
1025         """Sets whether text is word wrapped.""" 
1027             self
.SetWrapMode(wrap
) 
1028         except AttributeError: 
1029             return 'Wrapping is not available in this version.' 
1031     def zoom(self
, points
=0): 
1032         """Set the zoom level. 
1034         This number of points is added to the size of all fonts.  It 
1035         may be positive to magnify or negative to reduce.""" 
1036         self
.SetZoom(points
)