Deprecated PyShell and PyShellWindow, added a snapshot of PyCrust.
Added the new virtual list capabilities to wxListCtrl. Other odds and ends. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@11380 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
148
wxPython/wxPython/lib/PyCrust/PyCrust.py
Normal file
148
wxPython/wxPython/lib/PyCrust/PyCrust.py
Normal file
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env python
|
||||
"""PyCrust is a python shell application.
|
||||
"""
|
||||
|
||||
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
|
||||
__cvsid__ = "$Id$"
|
||||
__date__ = "July 1, 2001"
|
||||
__version__ = "$Revision$"[11:-2]
|
||||
|
||||
from wxPython.wx import *
|
||||
|
||||
from PyCrustVersion import version
|
||||
from PyCrustShell import Shell
|
||||
|
||||
|
||||
class Frame(wxFrame):
|
||||
"""Main window for the PyCrust application."""
|
||||
def __init__(self, parent, id, title):
|
||||
"""Create the main frame object for the application."""
|
||||
wxFrame.__init__(self, parent, id, title)
|
||||
intro = 'Welcome To PyCrust %s - The Flakiest Python Shell' % version
|
||||
self.CreateStatusBar()
|
||||
self.SetStatusText(intro)
|
||||
self.createMenus()
|
||||
# Create the shell, which will create a default editor and
|
||||
# a default interpreter.
|
||||
self.shell = Shell(editorParent=self, introText=intro)
|
||||
# Override the editor so that status messages go to the status bar.
|
||||
self.shell.editor.setStatusText = self.SetStatusText
|
||||
|
||||
def createMenus(self):
|
||||
m = self.fileMenu = wxMenu()
|
||||
m.AppendSeparator()
|
||||
m.Append(wxID_EXIT, 'E&xit', 'Exit PyCrust')
|
||||
|
||||
m = self.editMenu = wxMenu()
|
||||
m.Append(wxID_UNDO, '&Undo \tCtrl+Z', 'Undo the last action')
|
||||
m.Append(wxID_REDO, '&Redo \tCtrl+Y', 'Redo the last undone action')
|
||||
m.AppendSeparator()
|
||||
m.Append(wxID_CUT, 'Cu&t \tCtrl+X', 'Cut the selection')
|
||||
m.Append(wxID_COPY, '&Copy \tCtrl+C', 'Copy the selection')
|
||||
m.Append(wxID_PASTE, '&Paste \tCtrl+V', 'Paste')
|
||||
m.AppendSeparator()
|
||||
m.Append(wxID_CLEAR, 'Cle&ar \tDel', 'Delete the selection')
|
||||
m.Append(wxID_SELECTALL, 'Select A&ll \tCtrl+A', 'Select all text')
|
||||
|
||||
m = self.helpMenu = wxMenu()
|
||||
m.AppendSeparator()
|
||||
m.Append(wxID_ABOUT, '&About...', 'About PyCrust')
|
||||
|
||||
b = self.menuBar = wxMenuBar()
|
||||
b.Append(self.fileMenu, '&File')
|
||||
b.Append(self.editMenu, '&Edit')
|
||||
b.Append(self.helpMenu, '&Help')
|
||||
self.SetMenuBar(b)
|
||||
|
||||
EVT_MENU(self, wxID_EXIT, self.OnExit)
|
||||
EVT_MENU(self, wxID_UNDO, self.OnUndo)
|
||||
EVT_MENU(self, wxID_REDO, self.OnRedo)
|
||||
EVT_MENU(self, wxID_CUT, self.OnCut)
|
||||
EVT_MENU(self, wxID_COPY, self.OnCopy)
|
||||
EVT_MENU(self, wxID_PASTE, self.OnPaste)
|
||||
EVT_MENU(self, wxID_CLEAR, self.OnClear)
|
||||
EVT_MENU(self, wxID_SELECTALL, self.OnSelectAll)
|
||||
EVT_MENU(self, wxID_ABOUT, self.OnAbout)
|
||||
|
||||
EVT_UPDATE_UI(self, wxID_UNDO, self.OnUpdateMenu)
|
||||
EVT_UPDATE_UI(self, wxID_REDO, self.OnUpdateMenu)
|
||||
EVT_UPDATE_UI(self, wxID_CUT, self.OnUpdateMenu)
|
||||
EVT_UPDATE_UI(self, wxID_COPY, self.OnUpdateMenu)
|
||||
EVT_UPDATE_UI(self, wxID_PASTE, self.OnUpdateMenu)
|
||||
EVT_UPDATE_UI(self, wxID_CLEAR, self.OnUpdateMenu)
|
||||
|
||||
def OnExit(self, event):
|
||||
self.Close(true)
|
||||
|
||||
def OnUndo(self, event):
|
||||
self.shell.editor.Undo()
|
||||
|
||||
def OnRedo(self, event):
|
||||
self.shell.editor.Redo()
|
||||
|
||||
def OnCut(self, event):
|
||||
self.shell.editor.Cut()
|
||||
|
||||
def OnCopy(self, event):
|
||||
self.shell.editor.Copy()
|
||||
|
||||
def OnPaste(self, event):
|
||||
self.shell.editor.Paste()
|
||||
|
||||
def OnClear(self, event):
|
||||
self.shell.editor.Clear()
|
||||
|
||||
def OnSelectAll(self, event):
|
||||
self.shell.editor.SelectAll()
|
||||
|
||||
def OnAbout(self, event):
|
||||
"""Display an About PyCrust window."""
|
||||
title = 'About PyCrust'
|
||||
text = 'PyCrust %s\n\n' % version + \
|
||||
'Yet another Python shell, only flakier.\n\n' + \
|
||||
'Half-baked by Patrick K. O\'Brien,\n' + \
|
||||
'the other half is still in the oven.'
|
||||
dialog = wxMessageDialog(self, text, title, wxOK | wxICON_INFORMATION)
|
||||
dialog.ShowModal()
|
||||
dialog.Destroy()
|
||||
|
||||
def OnUpdateMenu(self, event):
|
||||
"""Update menu items based on which should be enabled/disabled."""
|
||||
id = event.GetId()
|
||||
if id == wxID_UNDO:
|
||||
event.Enable(self.shell.editor.CanUndo())
|
||||
elif id == wxID_REDO:
|
||||
event.Enable(self.shell.editor.CanRedo())
|
||||
elif id == wxID_CUT:
|
||||
event.Enable(self.shell.editor.CanCut())
|
||||
elif id == wxID_COPY:
|
||||
event.Enable(self.shell.editor.CanCopy())
|
||||
elif id == wxID_PASTE:
|
||||
event.Enable(self.shell.editor.CanPaste())
|
||||
elif id == wxID_CLEAR:
|
||||
event.Enable(self.shell.editor.CanCut())
|
||||
|
||||
|
||||
class App(wxApp):
|
||||
def OnInit(self):
|
||||
parent = None
|
||||
id = -1
|
||||
title = 'PyCrust'
|
||||
self.frame = Frame(parent, id, title)
|
||||
self.frame.Show(true)
|
||||
self.SetTopWindow(self.frame)
|
||||
return true
|
||||
|
||||
|
||||
def main():
|
||||
import sys
|
||||
application = App(0)
|
||||
# Add the application object to the sys module's namespace.
|
||||
# This allows a shell user to do:
|
||||
# >>> import sys
|
||||
# >>> sys.application.whatever
|
||||
sys.application = application
|
||||
application.MainLoop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
349
wxPython/wxPython/lib/PyCrust/PyCrustEditor.py
Normal file
349
wxPython/wxPython/lib/PyCrust/PyCrustEditor.py
Normal file
@@ -0,0 +1,349 @@
|
||||
|
||||
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
|
||||
__cvsid__ = "$Id$"
|
||||
__date__ = "July 1, 2001"
|
||||
__version__ = "$Revision$"[11:-2]
|
||||
|
||||
from wxPython.wx import *
|
||||
from wxPython.stc import *
|
||||
|
||||
import keyword
|
||||
import sys
|
||||
|
||||
|
||||
if wxPlatform == '__WXMSW__':
|
||||
faces = { 'times' : 'Times New Roman',
|
||||
'mono' : 'Courier New',
|
||||
'helv' : 'Lucida Console',
|
||||
'lucida' : 'Lucida Console',
|
||||
'other' : 'Comic Sans MS',
|
||||
'size' : 8,
|
||||
'lnsize' : 7,
|
||||
'backcol': '#FFFFFF',
|
||||
}
|
||||
else: # GTK
|
||||
faces = { 'times' : 'Times',
|
||||
'mono' : 'Courier',
|
||||
'helv' : 'Helvetica',
|
||||
'other' : 'new century schoolbook',
|
||||
'size' : 9,
|
||||
'lnsize' : 8,
|
||||
'backcol': '#FFFFFF',
|
||||
}
|
||||
|
||||
|
||||
class Editor(wxStyledTextCtrl):
|
||||
"""PyCrust Editor based on wxStyledTextCtrl."""
|
||||
revision = __version__
|
||||
def __init__(self, parent, id):
|
||||
"""Create a PyCrust editor object based on wxStyledTextCtrl."""
|
||||
wxStyledTextCtrl.__init__(self, parent, id, style=wxCLIP_CHILDREN)
|
||||
# Commands get pushed to a method determined by the outer shell.
|
||||
#self.shellPush = pushMethod
|
||||
# Keep track of the most recent prompt starting and ending positions.
|
||||
self.promptPos = [0, 0]
|
||||
# Keep track of multi-line commands.
|
||||
self.more = 0
|
||||
# Configure various defaults and user preferences.
|
||||
self.config()
|
||||
# Assign handlers for keyboard events.
|
||||
EVT_KEY_DOWN(self, self.OnKeyDown)
|
||||
EVT_CHAR(self, self.OnChar)
|
||||
|
||||
def config(self):
|
||||
"""Configure editor based on user preferences."""
|
||||
self.SetMarginType(1, wxSTC_MARGIN_NUMBER)
|
||||
self.SetMarginWidth(1, 40)
|
||||
|
||||
self.SetLexer(wxSTC_LEX_PYTHON)
|
||||
self.SetKeyWords(0, ' '.join(keyword.kwlist))
|
||||
|
||||
self.setStyles(faces)
|
||||
self.SetViewWhiteSpace(0)
|
||||
self.SetTabWidth(4)
|
||||
self.SetUseTabs(0)
|
||||
# Do we want to automatically pop up command completion options?
|
||||
self.autoComplete = 1
|
||||
self.autoCompleteCaseInsensitive = 1
|
||||
self.AutoCompSetIgnoreCase(self.autoCompleteCaseInsensitive)
|
||||
# De we want to automatically pop up command argument help?
|
||||
self.autoCallTip = 1
|
||||
self.CallTipSetBackground(wxColour(255, 255, 232))
|
||||
|
||||
def setStyles(self, faces):
|
||||
"""Configure font size, typeface and color for lexer."""
|
||||
|
||||
# Default style
|
||||
self.StyleSetSpec(wxSTC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces)
|
||||
|
||||
self.StyleClearAll()
|
||||
|
||||
# Built in styles
|
||||
self.StyleSetSpec(wxSTC_STYLE_LINENUMBER, "back:#C0C0C0,face:%(mono)s,size:%(lnsize)d" % faces)
|
||||
self.StyleSetSpec(wxSTC_STYLE_CONTROLCHAR, "face:%(mono)s" % faces)
|
||||
self.StyleSetSpec(wxSTC_STYLE_BRACELIGHT, "fore:#0000FF,back:#FFFF88")
|
||||
self.StyleSetSpec(wxSTC_STYLE_BRACEBAD, "fore:#FF0000,back:#FFFF88")
|
||||
|
||||
# Python styles
|
||||
self.StyleSetSpec(wxSTC_P_DEFAULT, "face:%(mono)s" % faces)
|
||||
self.StyleSetSpec(wxSTC_P_COMMENTLINE, "fore:#007F00,face:%(mono)s" % faces)
|
||||
self.StyleSetSpec(wxSTC_P_NUMBER, "")
|
||||
self.StyleSetSpec(wxSTC_P_STRING, "fore:#7F007F,face:%(mono)s" % faces)
|
||||
self.StyleSetSpec(wxSTC_P_CHARACTER, "fore:#7F007F,face:%(mono)s" % faces)
|
||||
self.StyleSetSpec(wxSTC_P_WORD, "fore:#00007F,bold")
|
||||
self.StyleSetSpec(wxSTC_P_TRIPLE, "fore:#7F0000")
|
||||
self.StyleSetSpec(wxSTC_P_TRIPLEDOUBLE, "fore:#000033,back:#FFFFE8")
|
||||
self.StyleSetSpec(wxSTC_P_CLASSNAME, "fore:#0000FF,bold")
|
||||
self.StyleSetSpec(wxSTC_P_DEFNAME, "fore:#007F7F,bold")
|
||||
self.StyleSetSpec(wxSTC_P_OPERATOR, "")
|
||||
self.StyleSetSpec(wxSTC_P_IDENTIFIER, "")
|
||||
self.StyleSetSpec(wxSTC_P_COMMENTBLOCK, "fore:#7F7F7F")
|
||||
self.StyleSetSpec(wxSTC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eolfilled" % faces)
|
||||
|
||||
def OnKeyDown(self, event):
|
||||
"""Key down event handler.
|
||||
|
||||
The main goal here is to not allow modifications to previous
|
||||
lines of text.
|
||||
"""
|
||||
key = event.KeyCode()
|
||||
currpos = self.GetCurrentPos()
|
||||
stoppos = self.promptPos[1]
|
||||
# If the auto-complete window is up let it do its thing.
|
||||
if self.AutoCompActive():
|
||||
event.Skip()
|
||||
# Return is used to submit a command to the interpreter.
|
||||
elif key == WXK_RETURN:
|
||||
if self.CallTipActive: self.CallTipCancel()
|
||||
self.processLine()
|
||||
# Home needs to be aware of the prompt.
|
||||
elif key == WXK_HOME:
|
||||
if currpos >= stoppos:
|
||||
self.SetCurrentPos(stoppos)
|
||||
self.SetAnchor(stoppos)
|
||||
else:
|
||||
event.Skip()
|
||||
# Basic navigation keys should work anywhere.
|
||||
elif key in (WXK_END, WXK_LEFT, WXK_RIGHT, WXK_UP, WXK_DOWN, \
|
||||
WXK_PRIOR, WXK_NEXT):
|
||||
event.Skip()
|
||||
# Don't backspace over the latest prompt.
|
||||
elif key == WXK_BACK:
|
||||
if currpos > stoppos:
|
||||
event.Skip()
|
||||
# Only allow these keys after the latest prompt.
|
||||
elif key in (WXK_TAB, WXK_DELETE):
|
||||
if currpos >= stoppos:
|
||||
event.Skip()
|
||||
# Don't toggle between insert mode and overwrite mode.
|
||||
elif key == WXK_INSERT:
|
||||
pass
|
||||
else:
|
||||
event.Skip()
|
||||
|
||||
def OnChar(self, event):
|
||||
"""Keypress event handler.
|
||||
|
||||
The main goal here is to not allow modifications to previous
|
||||
lines of text.
|
||||
"""
|
||||
key = event.KeyCode()
|
||||
currpos = self.GetCurrentPos()
|
||||
stoppos = self.promptPos[1]
|
||||
if currpos >= stoppos:
|
||||
if key == 46:
|
||||
# "." The dot or period key activates auto completion.
|
||||
# Get the command between the prompt and the cursor.
|
||||
# Add a dot to the end of the command.
|
||||
command = self.GetTextRange(stoppos, currpos) + '.'
|
||||
self.write('.')
|
||||
if self.autoComplete: self.autoCompleteShow(command)
|
||||
elif key == 40:
|
||||
# "(" The left paren activates a call tip and cancels
|
||||
# an active auto completion.
|
||||
if self.AutoCompActive(): self.AutoCompCancel()
|
||||
# Get the command between the prompt and the cursor.
|
||||
# Add the '(' to the end of the command.
|
||||
command = self.GetTextRange(stoppos, currpos) + '('
|
||||
self.write('(')
|
||||
if self.autoCallTip: self.autoCallTipShow(command)
|
||||
else:
|
||||
# Allow the normal event handling to take place.
|
||||
event.Skip()
|
||||
else:
|
||||
pass
|
||||
|
||||
def setStatusText(self, text):
|
||||
"""Display status information."""
|
||||
|
||||
# This method will most likely be replaced by the enclosing app
|
||||
# to do something more interesting, like write to a status bar.
|
||||
print text
|
||||
|
||||
def autoCompleteShow(self, command):
|
||||
"""Display auto-completion popup list."""
|
||||
list = self.getAutoCompleteList(command)
|
||||
if list:
|
||||
options = ' '.join(list)
|
||||
offset = 0
|
||||
self.AutoCompShow(offset, options)
|
||||
|
||||
def getAutoCompleteList(self, command):
|
||||
"""Return list of auto-completion options for command."""
|
||||
|
||||
# This method needs to be replaced by the enclosing app
|
||||
# to get the proper auto complete list from the interpreter.
|
||||
return []
|
||||
|
||||
def autoCallTipShow(self, command):
|
||||
"""Display argument spec and docstring in a popup bubble thingie."""
|
||||
if self.CallTipActive: self.CallTipCancel()
|
||||
tip = self.getCallTip(command)
|
||||
if tip:
|
||||
offset = self.GetCurrentPos()
|
||||
self.CallTipShow(offset, tip)
|
||||
|
||||
def getCallTip(self, command):
|
||||
"""Return arguments and docstring for command."""
|
||||
|
||||
# This method needs to be replaced by the enclosing app
|
||||
# to get the proper auto complete list from the interpreter.
|
||||
return ''
|
||||
|
||||
def processLine(self):
|
||||
"""Process the line of text at which the user hit Enter."""
|
||||
|
||||
# The user hit ENTER and we need to decide what to do. They could be
|
||||
# sitting on any line in the editor.
|
||||
|
||||
# Grab information about the current line.
|
||||
thepos = self.GetCurrentPos()
|
||||
theline = self.GetCurrentLine()
|
||||
thetext = self.GetCurLine()[0]
|
||||
command = self.getCommand(thetext)
|
||||
# Go to the very bottom of the editor.
|
||||
endpos = self.GetTextLength()
|
||||
self.SetCurrentPos(endpos)
|
||||
endline = self.GetCurrentLine()
|
||||
# If they hit RETURN on the last line, execute the command.
|
||||
if theline == endline:
|
||||
self.push(command)
|
||||
# Otherwise, replace the last line with the new line.
|
||||
else:
|
||||
# If the new line contains a command (even an invalid one).
|
||||
if command:
|
||||
startpos = self.PositionFromLine(endline)
|
||||
self.SetSelection(startpos, endpos)
|
||||
self.ReplaceSelection('')
|
||||
self.prompt()
|
||||
self.write(command)
|
||||
# Otherwise, put the cursor back where we started.
|
||||
else:
|
||||
self.SetCurrentPos(thepos)
|
||||
self.SetAnchor(thepos)
|
||||
|
||||
def getCommand(self, text):
|
||||
"""Extract a command from text which may include a shell prompt.
|
||||
|
||||
The command may not necessarily be valid Python syntax.
|
||||
"""
|
||||
|
||||
# XXX Need to extract real prompts here. Need to keep track of the
|
||||
# prompt every time a command is issued. Do this in the interpreter
|
||||
# with a line number, prompt, command dictionary. For the history, perhaps.
|
||||
ps1 = str(sys.ps1)
|
||||
ps1size = len(ps1)
|
||||
ps2 = str(sys.ps2)
|
||||
ps2size = len(ps2)
|
||||
text = text.rstrip()
|
||||
# Strip the prompt off the front of text leaving just the command.
|
||||
if text[:ps1size] == ps1:
|
||||
command = text[ps1size:]
|
||||
elif text[:ps2size] == ps2:
|
||||
command = text[ps2size:]
|
||||
else:
|
||||
command = ''
|
||||
return command
|
||||
|
||||
def push(self, command):
|
||||
"""Start a new line, send command to the shell, display a prompt."""
|
||||
self.write('\n')
|
||||
self.more = self.shellPush(command)
|
||||
self.prompt()
|
||||
# Keep the undo feature from undoing previous responses. The only
|
||||
# thing that can be undone is stuff typed after the prompt, before
|
||||
# hitting enter. After they hit enter it becomes permanent.
|
||||
self.EmptyUndoBuffer()
|
||||
|
||||
def clear(self):
|
||||
"""Delete all text from the editor."""
|
||||
self.ClearAll()
|
||||
|
||||
def prompt(self):
|
||||
"""Display appropriate prompt for the context, either ps1 or ps2.
|
||||
|
||||
If this is a continuation line, autoindent as necessary.
|
||||
"""
|
||||
if self.more:
|
||||
prompt = str(sys.ps2)
|
||||
else:
|
||||
prompt = str(sys.ps1)
|
||||
pos = self.GetCurLine()[1]
|
||||
if pos > 0: self.write('\n')
|
||||
self.promptPos[0] = self.GetCurrentPos()
|
||||
self.write(prompt)
|
||||
self.promptPos[1] = self.GetCurrentPos()
|
||||
# XXX Add some autoindent magic here if more.
|
||||
if self.more:
|
||||
self.write('\t') # Temporary hack indentation.
|
||||
self.EnsureCaretVisible()
|
||||
self.ScrollToColumn(0)
|
||||
|
||||
def readIn(self):
|
||||
"""Replacement for stdin."""
|
||||
prompt = 'Please enter your response:'
|
||||
dialog = wxTextEntryDialog(None, prompt, \
|
||||
'Input Dialog (Standard)', '')
|
||||
try:
|
||||
if dialog.ShowModal() == wxID_OK:
|
||||
text = dialog.GetValue()
|
||||
self.write(text + '\n')
|
||||
return text
|
||||
finally:
|
||||
dialog.Destroy()
|
||||
return ''
|
||||
|
||||
def readRaw(self, prompt='Please enter your response:'):
|
||||
"""Replacement for raw_input."""
|
||||
dialog = wxTextEntryDialog(None, prompt, \
|
||||
'Input Dialog (Raw)', '')
|
||||
try:
|
||||
if dialog.ShowModal() == wxID_OK:
|
||||
text = dialog.GetValue()
|
||||
return text
|
||||
finally:
|
||||
dialog.Destroy()
|
||||
return ''
|
||||
|
||||
def write(self, text):
|
||||
"""Display text in the editor."""
|
||||
self.AddText(text)
|
||||
self.EnsureCaretVisible()
|
||||
#self.ScrollToColumn(0)
|
||||
|
||||
def writeOut(self, text):
|
||||
"""Replacement for stdout."""
|
||||
self.write(text)
|
||||
|
||||
def writeErr(self, text):
|
||||
"""Replacement for stderr."""
|
||||
self.write(text)
|
||||
|
||||
def CanCut(self):
|
||||
"""Return true if text is selected and can be cut."""
|
||||
return self.GetSelectionStart() != self.GetSelectionEnd()
|
||||
|
||||
def CanCopy(self):
|
||||
"""Return true if text is selected and can be copied."""
|
||||
return self.GetSelectionStart() != self.GetSelectionEnd()
|
||||
|
81
wxPython/wxPython/lib/PyCrust/PyCrustInterp.py
Normal file
81
wxPython/wxPython/lib/PyCrust/PyCrustInterp.py
Normal file
@@ -0,0 +1,81 @@
|
||||
|
||||
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
|
||||
__cvsid__ = "$Id$"
|
||||
__date__ = "July 1, 2001"
|
||||
__version__ = "$Revision$"[11:-2]
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from code import InteractiveInterpreter
|
||||
import introspect
|
||||
|
||||
|
||||
class Interpreter(InteractiveInterpreter):
|
||||
"""PyCrust Interpreter based on code.InteractiveInterpreter."""
|
||||
revision = __version__
|
||||
def __init__(self, locals=None, rawin=None,
|
||||
stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr):
|
||||
"""Create an interactive interpreter object."""
|
||||
InteractiveInterpreter.__init__(self, locals=locals)
|
||||
self.stdin = stdin
|
||||
self.stdout = stdout
|
||||
self.stderr = stderr
|
||||
if rawin is not None:
|
||||
import __builtin__
|
||||
__builtin__.raw_input = rawin
|
||||
del __builtin__
|
||||
copyright = 'Type "copyright", "credits" or "license" for more information.'
|
||||
self.introText = 'Python %s on %s\n%s' % \
|
||||
(sys.version, sys.platform, copyright)
|
||||
try:
|
||||
sys.ps1
|
||||
except AttributeError:
|
||||
sys.ps1 = '>>> '
|
||||
try:
|
||||
sys.ps2
|
||||
except AttributeError:
|
||||
sys.ps2 = '... '
|
||||
self.more = 0
|
||||
self.commandBuffer = [] # List of lists to support recursive push().
|
||||
self.commandHistory = []
|
||||
self.startupScript = os.environ.get('PYTHONSTARTUP')
|
||||
|
||||
def push(self, command):
|
||||
"""Send command to the interpreter to be executed.
|
||||
|
||||
Because this may be called recursively, we append a new list
|
||||
onto the commandBuffer list and then append commands into that.
|
||||
If the passed in command is part of a multi-line command we keep
|
||||
appending the pieces to the last list in commandBuffer until we
|
||||
have a complete command, then, finally, we delete that last list.
|
||||
"""
|
||||
if not self.more: self.commandBuffer.append([])
|
||||
self.commandBuffer[-1].append(command)
|
||||
source = '\n'.join(self.commandBuffer[-1])
|
||||
self.more = self.runsource(source)
|
||||
if not self.more: del self.commandBuffer[-1]
|
||||
return self.more
|
||||
|
||||
def runsource(self, source):
|
||||
"""Compile and run source code in the interpreter."""
|
||||
stdin, stdout, stderr = sys.stdin, sys.stdout, sys.stderr
|
||||
sys.stdin = self.stdin
|
||||
sys.stdout = self.stdout
|
||||
sys.stderr = self.stderr
|
||||
more = InteractiveInterpreter.runsource(self, source)
|
||||
sys.stdin, sys.stdout, sys.stderr = stdin, stdout, stderr
|
||||
return more
|
||||
|
||||
def getAutoCompleteList(self, command=''):
|
||||
"""Return list of auto-completion options for a command.
|
||||
|
||||
The list of options will be based on the locals namespace."""
|
||||
return introspect.getAutoCompleteList(command, self.locals)
|
||||
|
||||
def getCallTip(self, command=''):
|
||||
"""Return call tip text for a command.
|
||||
|
||||
The call tip information will be based on the locals namespace."""
|
||||
return introspect.getCallTip(command, self.locals)
|
||||
|
180
wxPython/wxPython/lib/PyCrust/PyCrustShell.py
Normal file
180
wxPython/wxPython/lib/PyCrust/PyCrustShell.py
Normal file
@@ -0,0 +1,180 @@
|
||||
"""
|
||||
"""
|
||||
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
|
||||
__cvsid__ = "$Id$"
|
||||
__date__ = "July 1, 2001"
|
||||
__version__ = "$Revision$"[11:-2]
|
||||
|
||||
import os
|
||||
from PyCrustVersion import version
|
||||
|
||||
class Shell:
|
||||
"""PyCrust Shell manages the Editor and Interpreter."""
|
||||
name = 'PyCrust Shell'
|
||||
revision = __version__
|
||||
def __init__(self, editorParent=None, introText='', editor=None, interp=None):
|
||||
"""Create a PyCrust shell object to manage the editor and interpreter."""
|
||||
try:
|
||||
eval('crap')
|
||||
except:
|
||||
pass
|
||||
self.introText = introText
|
||||
# Create a default editor if one isn't provided.
|
||||
if editor == None:
|
||||
from PyCrustEditor import Editor
|
||||
self.editor = Editor(editorParent, id=-1)
|
||||
else:
|
||||
self.editor = editor
|
||||
# Link the editor to the shell so that the shell is a conduit for
|
||||
# pushing commands to the interpreter.
|
||||
self.editor.shellPush = self.shellPush
|
||||
# Create a default interpreter if one isn't provided.
|
||||
if interp == None:
|
||||
from PyCrustInterp import Interpreter
|
||||
from pseudo import PseudoFileIn, PseudoFileOut, PseudoFileErr
|
||||
self.stdin = PseudoFileIn(self.editor.readIn)
|
||||
self.stdout = PseudoFileOut(self.editor.writeOut)
|
||||
self.stderr = PseudoFileErr(self.editor.writeErr)
|
||||
# Override the default locals so we have something interesting.
|
||||
locals = {'__name__': 'PyCrust',
|
||||
'__doc__': 'PyCrust, The Python Shell.',
|
||||
'__version__': version,
|
||||
}
|
||||
self.interp = Interpreter(locals=locals,
|
||||
rawin=self.editor.readRaw,
|
||||
stdin=self.stdin,
|
||||
stdout=self.stdout,
|
||||
stderr=self.stderr)
|
||||
else:
|
||||
self.interp = interp
|
||||
# XXX redo this using hasattr() or something so that we can link
|
||||
# these if a provided editor has this method.
|
||||
if editor == None or editor == self:
|
||||
# Override so the auto complete list comes from the interpreter.
|
||||
self.editor.getAutoCompleteList = self.interp.getAutoCompleteList
|
||||
# Override so the call tip comes from the interpreter.
|
||||
self.editor.getCallTip = self.interp.getCallTip
|
||||
# Keep track of whether the interpreter needs more.
|
||||
self.more = 0
|
||||
|
||||
try:
|
||||
self.showIntro(self.introText)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.setBuiltinKeywords()
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.setLocalShell()
|
||||
except:
|
||||
pass
|
||||
|
||||
# Do this last so the user has complete control over their
|
||||
# environment. They can override anything they want.
|
||||
try:
|
||||
self.execStartupScript(self.interp.startupScript)
|
||||
except:
|
||||
pass
|
||||
|
||||
def destroy(self):
|
||||
del self.editor
|
||||
del self.stdin
|
||||
del self.stdout
|
||||
del self.stderr
|
||||
del self.interp
|
||||
|
||||
def showIntro(self, text=''):
|
||||
"""Display introductory text in the shell editor."""
|
||||
if text:
|
||||
if text[-1] != '\n': text += '\n'
|
||||
self.editor.write(text)
|
||||
try:
|
||||
self.editor.write(self.interp.introText)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def setBuiltinKeywords(self):
|
||||
"""Create pseudo keywords as part of builtins.
|
||||
|
||||
This is a rather clever hack that sets "close", "exit" and "quit"
|
||||
to a PseudoKeyword object so that we can make them do what we want.
|
||||
In this case what we want is to call our self.quit() method.
|
||||
The user can type "close", "exit" or "quit" without the final parens.
|
||||
"""
|
||||
import __builtin__
|
||||
from pseudo import PseudoKeyword
|
||||
__builtin__.close = __builtin__.exit = __builtin__.quit = \
|
||||
PseudoKeyword(self.quit)
|
||||
|
||||
def quit(self):
|
||||
"""Quit the application."""
|
||||
|
||||
# XXX Good enough for now but later we want to send a close event.
|
||||
|
||||
# In the close event handler we can prompt to make sure they want to quit.
|
||||
# Other applications, like PythonCard, may choose to hide rather than
|
||||
# quit so we should just post the event and let the surrounding app
|
||||
# decide what it wants to do.
|
||||
self.editor.write('Click on the close button to leave the application.')
|
||||
|
||||
def setLocalShell(self):
|
||||
"""Add 'shell' to locals."""
|
||||
self.interp.locals['shell'] = self
|
||||
|
||||
def execStartupScript(self, startupScript):
|
||||
"""Execute the user's PYTHONSTARTUP script if they have one."""
|
||||
if startupScript and os.path.isfile(startupScript):
|
||||
startupText = 'Startup script executed: ' + startupScript
|
||||
self.editor.push('print %s;execfile(%s)' % \
|
||||
(`startupText`, `startupScript`))
|
||||
else:
|
||||
self.editor.push('')
|
||||
|
||||
def run(self, command, prompt=1, verbose=1):
|
||||
"""Execute command within the shell as if it was typed in directly.
|
||||
>>> shell.run('print "this"')
|
||||
>>> print "this"
|
||||
this
|
||||
>>>
|
||||
"""
|
||||
command = command.rstrip()
|
||||
if prompt: self.editor.prompt()
|
||||
if verbose: self.editor.write(command)
|
||||
self.editor.push(command)
|
||||
|
||||
def runfile(self, filename):
|
||||
"""Execute all commands in file as if they were typed into the shell."""
|
||||
file = open(filename)
|
||||
try:
|
||||
self.editor.prompt()
|
||||
for command in file.readlines():
|
||||
if command[:6] == 'shell.': # Run shell methods silently.
|
||||
self.run(command, prompt=0, verbose=0)
|
||||
else:
|
||||
self.run(command, prompt=0, verbose=1)
|
||||
finally:
|
||||
file.close()
|
||||
|
||||
def push(self, command):
|
||||
"""Send command to the interpreter for execution."""
|
||||
self.more = self.interp.push(command)
|
||||
return self.more
|
||||
|
||||
shellPush = push
|
||||
|
||||
def ask(self, prompt='Please enter your response:'):
|
||||
"""Get response from the user."""
|
||||
return raw_input(prompt=prompt)
|
||||
|
||||
def pause(self):
|
||||
"""Halt execution pending a response from the user."""
|
||||
self.ask('Press enter to continue:')
|
||||
|
||||
def clear(self):
|
||||
"""Delete all text from the shell editor."""
|
||||
self.editor.clear()
|
||||
|
||||
|
10
wxPython/wxPython/lib/PyCrust/PyCrustVersion.py
Normal file
10
wxPython/wxPython/lib/PyCrust/PyCrustVersion.py
Normal file
@@ -0,0 +1,10 @@
|
||||
"""Provides an object representing the current "version" or "release" of
|
||||
PyCrust as a whole. Individual classes, such as the shell, editor and
|
||||
interpreter, each have a revision property based on the CVS $Revision$."""
|
||||
|
||||
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
|
||||
__cvsid__ = "$Id$"
|
||||
__date__ = "July 1, 2001"
|
||||
__version__ = "$Revision$"[11:-2]
|
||||
|
||||
version = '0.5'
|
1
wxPython/wxPython/lib/PyCrust/__init__.py
Normal file
1
wxPython/wxPython/lib/PyCrust/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Orbtech python package.
|
140
wxPython/wxPython/lib/PyCrust/introspect.py
Normal file
140
wxPython/wxPython/lib/PyCrust/introspect.py
Normal file
@@ -0,0 +1,140 @@
|
||||
"""Provides a variety of introspective-type support functions for things
|
||||
like call tips and command auto completion."""
|
||||
|
||||
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
|
||||
__cvsid__ = "$Id$"
|
||||
__date__ = "August 8, 2001"
|
||||
__version__ = "$Revision$"[11:-2]
|
||||
|
||||
import inspect
|
||||
import string
|
||||
|
||||
def getAutoCompleteList(command='', locals=None):
|
||||
"""Return list of auto-completion options for command.
|
||||
|
||||
The list of options will be based on the locals namespace."""
|
||||
|
||||
# Get the proper chunk of code from the command.
|
||||
root = getRoot(command, terminator='.')
|
||||
try:
|
||||
object = eval(root, locals)
|
||||
attributes = getAttributes(object)
|
||||
return attributes
|
||||
except:
|
||||
return []
|
||||
|
||||
def getAttributes(object):
|
||||
"""Return list of unique attributes, including inherited, for an object."""
|
||||
attributes = []
|
||||
dict = {}
|
||||
# Remove duplicates from the attribute list.
|
||||
for item in getAllAttributes(object):
|
||||
dict[item] = None
|
||||
attributes += dict.keys()
|
||||
attributes.sort()
|
||||
return attributes
|
||||
|
||||
def getAllAttributes(object):
|
||||
"""Return list of all attributes, including inherited, for an object.
|
||||
|
||||
Recursively walk through a class and all base classes.
|
||||
"""
|
||||
attributes = []
|
||||
try:
|
||||
attributes += dir(object)
|
||||
if hasattr(object, '__class__'):
|
||||
attributes += getAllAttributes(object.__class__)
|
||||
if hasattr(object, '__bases__'):
|
||||
for base in object.__bases__:
|
||||
attributes += getAllAttributes(base)
|
||||
finally:
|
||||
return attributes
|
||||
|
||||
def getCallTip(command='', locals=None):
|
||||
"""Return call tip text for a command.
|
||||
|
||||
The call tip information will be based on the locals namespace."""
|
||||
|
||||
# Get the proper chunk of code from the command.
|
||||
root = getRoot(command, terminator='(')
|
||||
try:
|
||||
object = eval(root, locals)
|
||||
except:
|
||||
return ''
|
||||
dropSelf = 0
|
||||
if hasattr(object, '__name__'): # Make sure this is a useable object.
|
||||
# Switch to the object that has the information we need.
|
||||
if inspect.ismethod(object):
|
||||
# Get the function from the object otherwise inspec.getargspec()
|
||||
# complains that the object isn't a Python function.
|
||||
object = object.im_func
|
||||
dropSelf = 1
|
||||
elif inspect.isclass(object):
|
||||
# Get the __init__ method for the class.
|
||||
try:
|
||||
object = object.__init__.im_func
|
||||
dropSelf = 1
|
||||
except AttributeError:
|
||||
for base in object.__bases__:
|
||||
constructor = _find_constructor(base)
|
||||
if constructor is not None:
|
||||
object = constructor
|
||||
dropSelf = 1
|
||||
break
|
||||
name = object.__name__
|
||||
if inspect.isbuiltin(object):
|
||||
# Builtin functions don't have an argspec that we can get.
|
||||
tip1 = ''
|
||||
else:
|
||||
# tip1 is a string like: "getCallTip(command='', locals=None)"
|
||||
argspec = apply(inspect.formatargspec, inspect.getargspec(object))
|
||||
if dropSelf:
|
||||
# The first parameter to a method is a reference to the
|
||||
# instance, usually coded as "self", and is passed
|
||||
# automatically by Python and therefore we want to drop it.
|
||||
temp = argspec.split(',')
|
||||
if len(temp) == 1: # No other arguments.
|
||||
argspec = '()'
|
||||
else: # Drop the first argument.
|
||||
argspec = '(' + ','.join(temp[1:]).lstrip()
|
||||
tip1 = name + argspec
|
||||
doc = inspect.getdoc(object)
|
||||
if doc:
|
||||
# tip2 is the first separated line of the docstring, like:
|
||||
# "Return call tip text for a command."
|
||||
# tip3 is the rest of the docstring, like:
|
||||
# "The call tip information will be based on ... <snip>
|
||||
docpieces = doc.split('\n\n')
|
||||
tip2 = docpieces[0]
|
||||
tip3 = '\n\n'.join(docpieces[1:])
|
||||
tip = '%s\n\n%s\n\n%s' % (tip1, tip2, tip3)
|
||||
else:
|
||||
tip = tip1
|
||||
return tip.strip()
|
||||
else:
|
||||
return ''
|
||||
|
||||
def getRoot(command, terminator=None):
|
||||
"""Return the rightmost root portion of an arbitrary Python command.
|
||||
|
||||
The command would normally terminate with a "(" or ".". Anything after
|
||||
the terminator will be dropped, allowing you to get back to the root.
|
||||
"""
|
||||
root = ''
|
||||
validChars = "._" + string.uppercase + string.lowercase + string.digits
|
||||
# Remove all whitespace from the command.
|
||||
command = ''.join(command.split())
|
||||
# Deal with the terminator.
|
||||
if terminator:
|
||||
pieces = command.split(terminator)
|
||||
if len(pieces) > 1:
|
||||
# Drop the final terminator and anything that follows.
|
||||
command = terminator.join(pieces[:-1])
|
||||
# Go backward through the command until we hit an "invalid" character.
|
||||
i = len(command)
|
||||
while i and command[i-1] in validChars:
|
||||
i -= 1
|
||||
# Grab everything from the "invalid" character to the end.
|
||||
root = command[i:]
|
||||
return root
|
||||
|
90
wxPython/wxPython/lib/PyCrust/pseudo.py
Normal file
90
wxPython/wxPython/lib/PyCrust/pseudo.py
Normal file
@@ -0,0 +1,90 @@
|
||||
"""Provides a variety of classes to create pseudo keywords and pseudo files."""
|
||||
|
||||
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
|
||||
__cvsid__ = "$Id$"
|
||||
__date__ = "July 1, 2001"
|
||||
__version__ = "$Revision$"[11:-2]
|
||||
|
||||
class PseudoKeyword:
|
||||
"""A callable class that calls a method passed as a parameter.
|
||||
|
||||
Good for creating a pseudo keyword in the python runtime
|
||||
environment. The keyword is really an object that has a repr()
|
||||
that calls itself which calls the method that was passed in the
|
||||
init of the object. All this just to avoid having to type in the
|
||||
closing parens on a method. So, for example:
|
||||
|
||||
>>> quit = PseudoKeyword(SomeObject.someMethod)
|
||||
>>> quit
|
||||
|
||||
SomeObject.someMethod gets executed as if it had been called
|
||||
directly and the user didn't have to type the parens, like
|
||||
"quit()". This technique is most applicable for pseudo keywords
|
||||
like quit, exit and help.
|
||||
|
||||
If SomeObject.someMethod can take parameters, they can still be
|
||||
passed by using the keyword in the traditional way with parens.
|
||||
"""
|
||||
def __init__(self, method):
|
||||
"""Create a callable object that executes method when called."""
|
||||
|
||||
# XXX Should probably check that this is a callable object.
|
||||
self.method = method
|
||||
|
||||
def __call__(self, *args, **kwds):
|
||||
self.method(*args, **kwds)
|
||||
|
||||
def __repr__(self):
|
||||
self()
|
||||
return ''
|
||||
|
||||
|
||||
class PseudoFile:
|
||||
|
||||
def __init__(self):
|
||||
"""Create a file-like object."""
|
||||
pass
|
||||
|
||||
def readline(self):
|
||||
pass
|
||||
|
||||
def write(self, s):
|
||||
pass
|
||||
|
||||
def writelines(self, l):
|
||||
map(self.write, l)
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
def isatty(self):
|
||||
pass
|
||||
|
||||
|
||||
class PseudoFileIn(PseudoFile):
|
||||
|
||||
def __init__(self, readline):
|
||||
self.readline = readline
|
||||
|
||||
def isatty(self):
|
||||
return 1
|
||||
|
||||
|
||||
class PseudoFileOut(PseudoFile):
|
||||
|
||||
def __init__(self, write):
|
||||
self.write = write
|
||||
|
||||
def isatty(self):
|
||||
return 1
|
||||
|
||||
|
||||
class PseudoFileErr(PseudoFile):
|
||||
|
||||
def __init__(self, write):
|
||||
self.write = write
|
||||
|
||||
def isatty(self):
|
||||
return 1
|
||||
|
||||
|
@@ -20,6 +20,10 @@ There is still much to be done to improve this class, such as line
|
||||
buffering/recall, autoindent, calltips, autocomplete, fixing the colourizer,
|
||||
etc... But it's a good start.
|
||||
|
||||
|
||||
8-10-2001 THIS MODULE IS NOW DEPRECATED. Please see the most excellent
|
||||
PyCrust package instead.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
@@ -17,11 +17,14 @@ History:
|
||||
input prompts and output styles added to customized demo
|
||||
some html cleanups
|
||||
04-oct-1999 [rpd] Changed to use the new sizers
|
||||
05-oct-1990 [als] changes inspired by code.InteractiveInterpreter()
|
||||
05-oct-1999 [als] changes inspired by code.InteractiveInterpreter()
|
||||
from Python Library. if i knew about this class earlier,
|
||||
i would rather inherit from it.
|
||||
renamed to wxPyShell.py since i've renounced the 8.3 scheme
|
||||
|
||||
8-10-2001 THIS MODULE IS NOW DEPRECATED. Please see the most excellent
|
||||
PyCrust package instead.
|
||||
|
||||
"""
|
||||
__version__ ="$Revision$"
|
||||
# $RCSfile$
|
||||
|
Reference in New Issue
Block a user