Updated PyCrust from Patrick O'Brien

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@14343 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2002-02-22 00:09:42 +00:00
parent b8125adfd9
commit 689fceb7a7
4 changed files with 223 additions and 119 deletions

View File

@@ -52,7 +52,7 @@ class CrustFrame(wxFrame, ShellMenu):
"""Create a PyCrust CrustFrame instance.""" """Create a PyCrust CrustFrame instance."""
wxFrame.__init__(self, parent, id, title, pos, size, style) wxFrame.__init__(self, parent, id, title, pos, size, style)
intro = 'Welcome To PyCrust %s - The Flakiest Python Shell' % VERSION intro = 'Welcome To PyCrust %s - The Flakiest Python Shell' % VERSION
intro += '\nSponsored by Orbtech.com - Your Source For Python Development Services' intro += '\nSponsored by Orbtech - Your Source For Python Development Services'
self.CreateStatusBar() self.CreateStatusBar()
self.SetStatusText(intro.replace('\n', ', ')) self.SetStatusText(intro.replace('\n', ', '))
if wxPlatform == '__WXMSW__': if wxPlatform == '__WXMSW__':

View File

@@ -50,11 +50,13 @@ class FillingTree(wxTreeCtrl):
"""Return a dictionary with the attributes or contents of object.""" """Return a dictionary with the attributes or contents of object."""
dict = {} dict = {}
objtype = type(object) objtype = type(object)
if objtype is types.DictType: if (objtype is types.DictType) \
or str(objtype)[17:23] == 'BTrees' and hasattr(object, 'keys'):
dict = object dict = object
elif (objtype in (types.ClassType, \ elif (objtype in (types.ClassType, \
types.InstanceType, \ types.InstanceType, \
types.ModuleType)): types.ModuleType)) \
or str(objtype)[1:10] == 'extension':
for key in introspect.getAttributeNames(object): for key in introspect.getAttributeNames(object):
# Believe it or not, some attributes can disappear, such as # Believe it or not, some attributes can disappear, such as
# the exc_traceback attribute of the sys module. So this is # the exc_traceback attribute of the sys module. So this is
@@ -74,7 +76,10 @@ class FillingTree(wxTreeCtrl):
if not children: if not children:
return return
list = children.keys() list = children.keys()
try:
list.sort(lambda x, y: cmp(x.lower(), y.lower())) list.sort(lambda x, y: cmp(x.lower(), y.lower()))
except:
pass
for item in list: for item in list:
itemtext = str(item) itemtext = str(item)
# Show string dictionary items with single quotes, except for # Show string dictionary items with single quotes, except for
@@ -101,7 +106,7 @@ class FillingTree(wxTreeCtrl):
object = self.GetPyData(item) object = self.GetPyData(item)
text = '' text = ''
text += self.getFullName(item) text += self.getFullName(item)
text += '\n\nType: ' + str(type(object))[7:-2] text += '\n\nType: ' + str(type(object))
value = str(object) value = str(object)
if type(object) is types.StringType: if type(object) is types.StringType:
value = repr(value) value = repr(value)
@@ -133,7 +138,9 @@ class FillingTree(wxTreeCtrl):
name = self.GetItemText(item) name = self.GetItemText(item)
# Apply dictionary syntax to dictionary items, except the root # Apply dictionary syntax to dictionary items, except the root
# and first level children of a namepace. # and first level children of a namepace.
if type(parentobject) is types.DictType \ if (type(parentobject) is types.DictType \
or str(type(parentobject))[17:23] == 'BTrees' \
and hasattr(parentobject, 'keys')) \
and ((item != self.root and parent != self.root) \ and ((item != self.root and parent != self.root) \
or (parent == self.root and not self.rootIsNamespace)): or (parent == self.root and not self.rootIsNamespace)):
name = '[' + name + ']' name = '[' + name + ']'

View File

@@ -93,28 +93,40 @@ def getCallTip(command='', locals=None):
The call tip information will be based on the locals namespace.""" The call tip information will be based on the locals namespace."""
calltip = ('', '', '')
# Get the proper chunk of code from the command. # Get the proper chunk of code from the command.
root = getRoot(command, terminator='(') root = getRoot(command, terminator='(')
try: try:
object = eval(root, locals) object = eval(root, locals)
except: except:
return '' return calltip
dropSelf = 0 name = ''
if hasattr(object, '__name__'): # Make sure this is a useable object. dropSelf = 1
# Switch to the object that has the information we need. # Switch to the object that has the information we need.
if inspect.ismethod(object) or hasattr(object, 'im_func'): if inspect.isbuiltin(object):
# Builtin functions don't have an argspec that we can get.
pass
elif inspect.ismethod(object) or hasattr(object, 'im_func'):
# Get the function from the object otherwise inspect.getargspec() # Get the function from the object otherwise inspect.getargspec()
# complains that the object isn't a Python function. # complains that the object isn't a Python function.
object = object.im_func object = object.im_func
dropSelf = 1
elif inspect.isclass(object): elif inspect.isclass(object):
# Get the __init__ method function for the class. # Get the __init__ method function for the class.
constructor = getConstructor(object) constructor = getConstructor(object)
if constructor is not None: if constructor is not None:
object = constructor object = constructor
dropSelf = 1 elif callable(object):
# Get the __call__ method instead.
try:
object = object.__call__.im_func
except:
dropSelf = 0
else:
dropSelf = 0
if hasattr(object, '__name__'):
name = object.__name__ name = object.__name__
tip1 = '' tip1 = ''
argspec = ''
if inspect.isbuiltin(object): if inspect.isbuiltin(object):
# Builtin functions don't have an argspec that we can get. # Builtin functions don't have an argspec that we can get.
pass pass
@@ -143,9 +155,8 @@ def getCallTip(command='', locals=None):
tip = '%s\n\n%s\n\n%s' % (tip1, tip2, tip3) tip = '%s\n\n%s\n\n%s' % (tip1, tip2, tip3)
else: else:
tip = tip1 tip = tip1
return tip.strip() calltip = (name, argspec[1:-1], tip.strip())
else: return calltip
return ''
def getConstructor(object): def getConstructor(object):
"""Return constructor for class object, or None if there isn't one.""" """Return constructor for class object, or None if there isn't one."""

View File

@@ -2,7 +2,7 @@
commands to be sent to the interpreter. This particular shell is based on commands to be sent to the interpreter. This particular shell is based on
wxPython's wxStyledTextCtrl. The latest files are always available at the wxPython's wxStyledTextCtrl. The latest files are always available at the
SourceForge project page at http://sourceforge.net/projects/pycrust/. SourceForge project page at http://sourceforge.net/projects/pycrust/.
Sponsored by Orbtech.com - Your Source For Python Development Services""" Sponsored by Orbtech - Your Source For Python Development Services"""
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$" __cvsid__ = "$Id$"
@@ -82,10 +82,13 @@ Ctrl+C Copy selected text, removing prompts.
Ctrl+Shift+C Copy selected text, retaining prompts. Ctrl+Shift+C Copy selected text, retaining prompts.
Ctrl+X Cut selected text. Ctrl+X Cut selected text.
Ctrl+V Paste from clipboard. Ctrl+V Paste from clipboard.
Ctrl+Shift+V Paste and run multiple commands from clipboard.
Ctrl+Up Arrow Retrieve Previous History item. Ctrl+Up Arrow Retrieve Previous History item.
Alt+P Retrieve Previous History item. Alt+P Retrieve Previous History item.
Ctrl+Down Arrow Retrieve Next History item. Ctrl+Down Arrow Retrieve Next History item.
Alt+N Retrieve Next History item. Alt+N Retrieve Next History item.
Shift+Up Arrow Insert Previous History item.
Shift+Down Arrow Insert Next History item.
F8 Command-completion of History item. F8 Command-completion of History item.
(Type a few characters of a previous command and then press F8.) (Type a few characters of a previous command and then press F8.)
""" """
@@ -335,17 +338,32 @@ class Shell(wxStyledTextCtrl):
altDown = event.AltDown() altDown = event.AltDown()
shiftDown = event.ShiftDown() shiftDown = event.ShiftDown()
currpos = self.GetCurrentPos() currpos = self.GetCurrentPos()
# Return is used to submit a command to the interpreter. endpos = self.GetTextLength()
if key == WXK_RETURN: # Return (Enter) is used to submit a command to the interpreter.
if not controlDown and key == WXK_RETURN:
if self.AutoCompActive(): self.AutoCompCancel() if self.AutoCompActive(): self.AutoCompCancel()
if self.CallTipActive(): self.CallTipCancel() if self.CallTipActive(): self.CallTipCancel()
self.processLine() self.processLine()
# Ctrl+Return (Cntrl+Enter) is used to insert a line break.
elif controlDown and key == WXK_RETURN:
if self.AutoCompActive(): self.AutoCompCancel()
if self.CallTipActive(): self.CallTipCancel()
if currpos == endpos:
self.processLine()
else:
self.insertLineBreak()
# If the auto-complete window is up let it do its thing. # If the auto-complete window is up let it do its thing.
elif self.AutoCompActive(): elif self.AutoCompActive():
event.Skip() event.Skip()
# Let Ctrl-Alt-* get handled normally. # Let Ctrl-Alt-* get handled normally.
elif controlDown and altDown: elif controlDown and altDown:
event.Skip() event.Skip()
# Clear the current, unexecuted command.
elif key == WXK_ESCAPE:
if self.CallTipActive():
event.Skip()
else:
self.clearCommand()
# Cut to the clipboard. # Cut to the clipboard.
elif (controlDown and key in (ord('X'), ord('x'))) \ elif (controlDown and key in (ord('X'), ord('x'))) \
or (shiftDown and key == WXK_DELETE): or (shiftDown and key == WXK_DELETE):
@@ -359,17 +377,28 @@ class Shell(wxStyledTextCtrl):
and key in (ord('C'), ord('c'), WXK_INSERT): and key in (ord('C'), ord('c'), WXK_INSERT):
self.CopyWithPrompts() self.CopyWithPrompts()
# Paste from the clipboard. # Paste from the clipboard.
elif (controlDown and key in (ord('V'), ord('v'), WXK_INSERT)) \ elif (controlDown and not shiftDown \
or (shiftDown and key == WXK_INSERT): and key in (ord('V'), ord('v'))) \
or (shiftDown and not controlDown and key == WXK_INSERT):
self.Paste() self.Paste()
# Retrieve the previous command from the history buffer. # Paste from the clipboard, run commands.
elif controlDown and shiftDown \
and key in (ord('V'), ord('v')):
self.PasteAndRun()
# Replace with the previous command from the history buffer.
elif (controlDown and key == WXK_UP) \ elif (controlDown and key == WXK_UP) \
or (altDown and key in (ord('P'), ord('p'))): or (altDown and key in (ord('P'), ord('p'))):
self.OnHistoryRetrieve(step=+1) self.OnHistoryReplace(step=+1)
# Retrieve the next command from the history buffer. # Replace with the next command from the history buffer.
elif (controlDown and key == WXK_DOWN) \ elif (controlDown and key == WXK_DOWN) \
or (altDown and key in (ord('N'), ord('n'))): or (altDown and key in (ord('N'), ord('n'))):
self.OnHistoryRetrieve(step=-1) self.OnHistoryReplace(step=-1)
# Insert the previous command from the history buffer.
elif (shiftDown and key == WXK_UP):
self.OnHistoryInsert(step=+1)
# Insert the next command from the history buffer.
elif (shiftDown and key == WXK_DOWN):
self.OnHistoryInsert(step=-1)
# Search up the history for the text in front of the cursor. # Search up the history for the text in front of the cursor.
elif key == WXK_F8: elif key == WXK_F8:
self.OnHistorySearch() self.OnHistorySearch()
@@ -383,6 +412,7 @@ class Shell(wxStyledTextCtrl):
else: else:
self.SetCurrentPos(home) self.SetCurrentPos(home)
self.SetAnchor(home) self.SetAnchor(home)
self.EnsureCaretVisible()
else: else:
event.Skip() event.Skip()
# Basic navigation keys should work anywhere. # Basic navigation keys should work anywhere.
@@ -412,22 +442,36 @@ class Shell(wxStyledTextCtrl):
else: else:
event.Skip() event.Skip()
def OnHistoryRetrieve(self, step): def clearCommand(self):
"""Retrieve the previous/next command from the history buffer.""" """Delete the current, unexecuted command."""
if not self.CanEdit(): startpos = self.promptPosEnd
return endpos = self.GetTextLength()
startpos = self.GetCurrentPos() self.SetSelection(startpos, endpos)
self.ReplaceSelection('')
self.more = 0
def OnHistoryReplace(self, step):
"""Replace with the previous/next command from the history buffer."""
self.clearCommand()
self.replaceFromHistory(step)
def replaceFromHistory(self, step):
"""Replace selection with command from the history buffer."""
self.ReplaceSelection('')
newindex = self.historyIndex + step newindex = self.historyIndex + step
if not (-1 <= newindex < len(self.history)): if -1 <= newindex <= len(self.history):
return
self.historyIndex = newindex self.historyIndex = newindex
if newindex == -1: if 0 <= newindex <= len(self.history)-1:
self.ReplaceSelection('')
else:
self.ReplaceSelection('')
command = self.history[self.historyIndex] command = self.history[self.historyIndex]
command = command.replace('\n', os.linesep + sys.ps2) command = command.replace('\n', os.linesep + sys.ps2)
self.ReplaceSelection(command) self.ReplaceSelection(command)
def OnHistoryInsert(self, step):
"""Insert the previous/next command from the history buffer."""
if not self.CanEdit():
return
startpos = self.GetCurrentPos()
self.replaceFromHistory(step)
endpos = self.GetCurrentPos() endpos = self.GetCurrentPos()
self.SetSelection(endpos, startpos) self.SetSelection(endpos, startpos)
@@ -469,6 +513,13 @@ class Shell(wxStyledTextCtrl):
# to do something more interesting, like write to a status bar. # to do something more interesting, like write to a status bar.
print text print text
def insertLineBreak(self):
"""Insert a new line break."""
if self.CanEdit():
self.write(os.linesep)
self.more = 1
self.prompt()
def processLine(self): def processLine(self):
"""Process the line of text at which the user hit Enter.""" """Process the line of text at which the user hit Enter."""
@@ -476,41 +527,28 @@ class Shell(wxStyledTextCtrl):
# sitting on any line in the shell. # sitting on any line in the shell.
thepos = self.GetCurrentPos() thepos = self.GetCurrentPos()
startpos = self.promptPosEnd
endpos = self.GetTextLength() endpos = self.GetTextLength()
# If they hit RETURN at the very bottom, execute the command. # If they hit RETURN inside the current command, execute the command.
if thepos == endpos: if self.CanEdit():
self.SetCurrentPos(endpos)
self.interp.more = 0 self.interp.more = 0
if self.getCommand(): command = self.GetTextRange(startpos, endpos)
command = self.GetTextRange(self.promptPosEnd, endpos) lines = command.split(os.linesep + sys.ps2)
else: lines = [line.rstrip() for line in lines]
# This is a hack, now that we allow editing of previous command = '\n'.join(lines)
# lines, which throws off our promptPos values.
newend = endpos - len(self.getCommand(rstrip=0))
command = self.GetTextRange(self.promptPosEnd, newend)
command = command.replace(os.linesep + sys.ps2, '\n')
self.push(command) self.push(command)
# Or replace the current command with the other command. # Or replace the current command with the other command.
elif thepos < self.promptPosStart: else:
theline = self.GetCurrentLine() # If the line contains a command (even an invalid one).
command = self.getCommand(rstrip=0) if self.getCommand(rstrip=0):
# If the new line contains a command (even an invalid one).
if command:
command = self.getMultilineCommand() command = self.getMultilineCommand()
self.SetCurrentPos(endpos) self.clearCommand()
startpos = self.promptPosEnd
self.SetSelection(startpos, endpos)
self.ReplaceSelection('')
self.write(command) self.write(command)
self.more = 0
# Otherwise, put the cursor back where we started. # Otherwise, put the cursor back where we started.
else: else:
self.SetCurrentPos(thepos) self.SetCurrentPos(thepos)
self.SetAnchor(thepos) self.SetAnchor(thepos)
# Or add a new line to the current single or multi-line command.
elif thepos > self.promptPosEnd:
self.write(os.linesep)
self.more = 1
self.prompt()
def getMultilineCommand(self, rstrip=1): def getMultilineCommand(self, rstrip=1):
"""Extract a multi-line command from the editor. """Extract a multi-line command from the editor.
@@ -716,10 +754,19 @@ class Shell(wxStyledTextCtrl):
def autoCallTipShow(self, command): def autoCallTipShow(self, command):
"""Display argument spec and docstring in a popup bubble thingie.""" """Display argument spec and docstring in a popup bubble thingie."""
if self.CallTipActive: self.CallTipCancel() if self.CallTipActive: self.CallTipCancel()
tip = self.interp.getCallTip(command) (name, argspec, tip) = self.interp.getCallTip(command)
if argspec:
startpos = self.GetCurrentPos()
self.write(argspec + ')')
endpos = self.GetCurrentPos()
self.SetSelection(endpos, startpos)
if tip: if tip:
offset = self.GetCurrentPos() curpos = self.GetCurrentPos()
self.CallTipShow(offset, tip) tippos = curpos - (len(name) + 1)
fallback = curpos - self.GetColumn(curpos)
# In case there isn't enough room, only go back to the fallback.
tippos = max(tippos, fallback)
self.CallTipShow(tippos, tip)
def writeOut(self, text): def writeOut(self, text):
"""Replacement for stdout.""" """Replacement for stdout."""
@@ -805,11 +852,11 @@ class Shell(wxStyledTextCtrl):
def Paste(self): def Paste(self):
"""Replace selection with clipboard contents.""" """Replace selection with clipboard contents."""
if self.CanPaste(): if self.CanPaste() and wxTheClipboard.Open():
if wxTheClipboard.Open():
if wxTheClipboard.IsSupported(wxDataFormat(wxDF_TEXT)): if wxTheClipboard.IsSupported(wxDataFormat(wxDF_TEXT)):
data = wxTextDataObject() data = wxTextDataObject()
if wxTheClipboard.GetData(data): if wxTheClipboard.GetData(data):
self.ReplaceSelection('')
command = data.GetText() command = data.GetText()
command = command.rstrip() command = command.rstrip()
command = self.fixLineEndings(command) command = self.fixLineEndings(command)
@@ -817,10 +864,49 @@ class Shell(wxStyledTextCtrl):
command = command.replace(os.linesep + sys.ps2, '\n') command = command.replace(os.linesep + sys.ps2, '\n')
command = command.replace(os.linesep, '\n') command = command.replace(os.linesep, '\n')
command = command.replace('\n', os.linesep + sys.ps2) command = command.replace('\n', os.linesep + sys.ps2)
self.ReplaceSelection('')
self.write(command) self.write(command)
wxTheClipboard.Close() wxTheClipboard.Close()
def PasteAndRun(self):
"""Replace selection with clipboard contents, run commands."""
if wxTheClipboard.Open():
if wxTheClipboard.IsSupported(wxDataFormat(wxDF_TEXT)):
data = wxTextDataObject()
if wxTheClipboard.GetData(data):
endpos = self.GetTextLength()
self.SetCurrentPos(endpos)
startpos = self.promptPosEnd
self.SetSelection(startpos, endpos)
self.ReplaceSelection('')
text = data.GetText()
text = text.strip()
text = self.fixLineEndings(text)
text = self.lstripPrompt(text=text)
text = text.replace(os.linesep + sys.ps1, '\n')
text = text.replace(os.linesep + sys.ps2, '\n')
text = text.replace(os.linesep, '\n')
lines = text.split('\n')
commands = []
command = ''
for line in lines:
if line.strip() != '' and line.lstrip() == line:
# New command.
if command:
# Add the previous command to the list.
commands.append(command)
# Start a new command, which may be multiline.
command = line
else:
# Multiline command. Add to the command.
command += '\n'
command += line
commands.append(command)
for command in commands:
command = command.replace('\n', os.linesep + sys.ps2)
self.write(command)
self.processLine()
wxTheClipboard.Close()
wxID_SELECTALL = NewId() # This *should* be defined by wxPython. wxID_SELECTALL = NewId() # This *should* be defined by wxPython.
ID_AUTOCOMP = NewId() ID_AUTOCOMP = NewId()
@@ -1014,7 +1100,7 @@ class ShellFrame(wxFrame, ShellMenu):
"""Create a PyCrust ShellFrame instance.""" """Create a PyCrust ShellFrame instance."""
wxFrame.__init__(self, parent, id, title, pos, size, style) wxFrame.__init__(self, parent, id, title, pos, size, style)
intro = 'Welcome To PyCrust %s - The Flakiest Python Shell' % VERSION intro = 'Welcome To PyCrust %s - The Flakiest Python Shell' % VERSION
intro += '\nSponsored by Orbtech.com - Your Source For Python Development Services' intro += '\nSponsored by Orbtech - Your Source For Python Development Services'
self.CreateStatusBar() self.CreateStatusBar()
self.SetStatusText(intro.replace('\n', ', ')) self.SetStatusText(intro.replace('\n', ', '))
if wxPlatform == '__WXMSW__': if wxPlatform == '__WXMSW__':