Updated PyCrust

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_4_BRANCH@18266 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2002-12-16 18:19:42 +00:00
parent 3cc4d9a7c3
commit 63e0edead7
10 changed files with 607 additions and 466 deletions

View File

@@ -4,20 +4,21 @@ __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__revision__ = "$Revision$"[11:-2]
from wxPython.wx import *
from wxPython import wx
from crust import CrustFrame
True, False = 1, 0
class App(wxApp):
class App(wx.wxApp):
"""PyCrust standalone application."""
def OnInit(self):
wxInitAllImageHandlers()
wx.wxInitAllImageHandlers()
locals = {'__app__': 'PyCrust Standalone Application'}
self.crustFrame = CrustFrame(locals=locals)
self.crustFrame.SetSize((750, 525))
self.crustFrame.Show(true)
self.crustFrame.crust.shell.SetFocus()
self.crustFrame.Show(True)
self.SetTopWindow(self.crustFrame)
# Add the application object to the sys module's namespace.
# This allows a shell user to do:
@@ -25,11 +26,11 @@ class App(wxApp):
# >>> sys.application.whatever
import sys
sys.application = self
return true
return True
def main():
application = App(1)
application = App(0)
application.MainLoop()
if __name__ == '__main__':

View File

@@ -4,32 +4,34 @@ __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__revision__ = "$Revision$"[11:-2]
from wxPython.wx import *
from wxPython import wx
from shell import ShellFrame
True, False = 1, 0
class App(wxApp):
class App(wx.wxApp):
"""PyShell standalone application."""
def OnInit(self):
wxInitAllImageHandlers()
wx.wxInitAllImageHandlers()
locals = {'__app__': 'PyShell Standalone Application'}
self.shellFrame = ShellFrame(locals=locals)
self.shellFrame.SetSize((750, 525))
self.shellFrame.Show(true)
self.shellFrame.shell.SetFocus()
self.shellFrame.Show(True)
self.SetTopWindow(self.shellFrame)
self.shellFrame.shell.SetFocus()
# Add the application object to the sys module's namespace.
# This allows a shell user to do:
# >>> import sys
# >>> sys.application.whatever
import sys
sys.application = self
return true
return True
def main():
application = App(1)
application = App(0)
application.MainLoop()
if __name__ == '__main__':

View File

@@ -4,31 +4,32 @@ __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__revision__ = "$Revision$"[11:-2]
from wxPython.wx import *
from shell import Shell
from wxPython import wx
from filling import Filling
from version import VERSION
import os
from shell import Shell
from shellmenu import ShellMenu
from version import VERSION
class Crust(wxSplitterWindow):
class Crust(wx.wxSplitterWindow):
"""PyCrust Crust based on wxSplitterWindow."""
name = 'PyCrust Crust'
revision = __revision__
def __init__(self, parent, id=-1, pos=wxDefaultPosition, \
size=wxDefaultSize, style=wxSP_3D, name='Crust Window', \
rootObject=None, rootLabel=None, rootIsNamespace=1, \
intro='', locals=None, \
def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition,
size=wx.wxDefaultSize, style=wx.wxSP_3D, name='Crust Window',
rootObject=None, rootLabel=None, rootIsNamespace=1,
intro='', locals=None,
InterpClass=None, *args, **kwds):
"""Create a PyCrust Crust instance."""
wxSplitterWindow.__init__(self, parent, id, pos, size, style, name)
self.shell = Shell(parent=self, introText=intro, \
locals=locals, InterpClass=InterpClass, \
wx.wxSplitterWindow.__init__(self, parent, id, pos, size, style, name)
self.shell = Shell(parent=self, introText=intro,
locals=locals, InterpClass=InterpClass,
*args, **kwds)
self.filling = Filling(parent=self, \
rootObject=self.shell.interp.locals, \
self.filling = Filling(parent=self,
rootObject=self.shell.interp.locals,
rootLabel=rootLabel, rootIsNamespace=1)
"""Add 'filling' to the interpreter's locals."""
self.shell.interp.locals['filling'] = self.filling
@@ -36,33 +37,30 @@ class Crust(wxSplitterWindow):
self.SetMinimumPaneSize(1)
# Temporary hack to share menus between PyCrust and PyShell.
from shell import ShellMenu
class CrustFrame(wxFrame, ShellMenu):
class CrustFrame(wx.wxFrame, ShellMenu):
"""Frame containing all the PyCrust components."""
name = 'PyCrust Frame'
revision = __revision__
def __init__(self, parent=None, id=-1, title='PyCrust', \
pos=wxDefaultPosition, size=wxDefaultSize, \
style=wxDEFAULT_FRAME_STYLE, \
rootObject=None, rootLabel=None, rootIsNamespace=1, \
def __init__(self, parent=None, id=-1, title='PyCrust',
pos=wx.wxDefaultPosition, size=wx.wxDefaultSize,
style=wx.wxDEFAULT_FRAME_STYLE,
rootObject=None, rootLabel=None, rootIsNamespace=1,
locals=None, InterpClass=None, *args, **kwds):
"""Create a PyCrust CrustFrame instance."""
wxFrame.__init__(self, parent, id, title, pos, size, style)
wx.wxFrame.__init__(self, parent, id, title, pos, size, style)
intro = 'Welcome To PyCrust %s - The Flakiest Python Shell' % VERSION
intro += '\nSponsored by Orbtech - Your source for Python programming expertise.'
self.CreateStatusBar()
self.SetStatusText(intro.replace('\n', ', '))
import images
self.SetIcon(images.getPyCrustIcon())
self.crust = Crust(parent=self, intro=intro, \
rootObject=rootObject, \
rootLabel=rootLabel, \
rootIsNamespace=rootIsNamespace, \
locals=locals, \
self.crust = Crust(parent=self, intro=intro,
rootObject=rootObject,
rootLabel=rootLabel,
rootIsNamespace=rootIsNamespace,
locals=locals,
InterpClass=InterpClass, *args, **kwds)
# Override the filling so that status messages go to the status bar.
self.crust.filling.fillingTree.setStatusText = self.SetStatusText
@@ -75,7 +73,7 @@ class CrustFrame(wxFrame, ShellMenu):
# Temporary hack to share menus between PyCrust and PyShell.
self.shell = self.crust.shell
self.createMenus()
EVT_CLOSE(self, self.OnCloseWindow)
wx.EVT_CLOSE(self, self.OnCloseWindow)
def OnCloseWindow(self, event):
self.crust.shell.destroy()

View File

@@ -5,8 +5,8 @@ __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__revision__ = "$Revision$"[11:-2]
from wxPython.wx import *
from wxPython.stc import *
from wxPython import wx
from wxPython import stc
from version import VERSION
import inspect
import introspect
@@ -14,6 +14,8 @@ import keyword
import sys
import types
True, False = 1, 0
COMMONTYPES = [getattr(types, t) for t in dir(types) \
if not t.startswith('_') \
and t not in ('ClassType', 'InstanceType', 'ModuleType')]
@@ -23,40 +25,40 @@ except AttributeError:
pass
class FillingTree(wxTreeCtrl):
class FillingTree(wx.wxTreeCtrl):
"""PyCrust FillingTree based on wxTreeCtrl."""
name = 'PyCrust Filling Tree'
revision = __revision__
def __init__(self, parent, id=-1, pos=wxDefaultPosition, \
size=wxDefaultSize, style=wxTR_HAS_BUTTONS, \
def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition,
size=wx.wxDefaultSize, style=wx.wxTR_HAS_BUTTONS,
rootObject=None, rootLabel=None, rootIsNamespace=0):
"""Create a PyCrust FillingTree instance."""
wxTreeCtrl.__init__(self, parent, id, pos, size)
wx.wxTreeCtrl.__init__(self, parent, id, pos, size)
self.rootIsNamespace = rootIsNamespace
if not rootObject:
import __main__
rootObject = __main__
self.rootIsNamespace = 1
if not rootLabel: rootLabel = 'Ingredients'
rootData = wxTreeItemData(rootObject)
rootData = wx.wxTreeItemData(rootObject)
self.root = self.AddRoot(rootLabel, -1, -1, rootData)
self.SetItemHasChildren(self.root, self.hasChildren(self.root))
EVT_TREE_ITEM_EXPANDING(self, self.GetId(), self.OnItemExpanding)
EVT_TREE_ITEM_COLLAPSED(self, self.GetId(), self.OnItemCollapsed)
EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged)
wx.EVT_TREE_ITEM_EXPANDING(self, self.GetId(), self.OnItemExpanding)
wx.EVT_TREE_ITEM_COLLAPSED(self, self.GetId(), self.OnItemCollapsed)
wx.EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged)
def hasChildren(self, o):
"""Return true if object has children."""
if self.getChildren(o):
return true
return True
else:
return false
return False
def getChildren(self, o):
"""Return a dictionary with the attributes or contents of object."""
busy = wxBusyCursor()
busy = wx.wxBusyCursor()
otype = type(o)
if (otype is types.DictType) \
or str(otype)[17:23] == 'BTrees' and hasattr(o, 'keys'):
@@ -78,7 +80,7 @@ class FillingTree(wxTreeCtrl):
return d
def OnItemExpanding(self, event):
busy = wxBusyCursor()
busy = wx.wxBusyCursor()
selection = event.GetItem()
if self.IsExpanded(selection):
return
@@ -100,18 +102,18 @@ class FillingTree(wxTreeCtrl):
and (selection != self.root \
or (selection == self.root and not self.rootIsNamespace)):
itemtext = repr(item)
child = self.AppendItem(selection, itemtext, -1, -1, \
wxTreeItemData(children[item]))
child = self.AppendItem(selection, itemtext, -1, -1,
wx.wxTreeItemData(children[item]))
self.SetItemHasChildren(child, self.hasChildren(children[item]))
def OnItemCollapsed(self, event):
"""Remove all children from the item."""
busy = wxBusyCursor()
busy = wx.wxBusyCursor()
item = event.GetItem()
self.DeleteChildren(item)
def OnSelChanged(self, event):
busy = wxBusyCursor()
busy = wx.wxBusyCursor()
item = event.GetItem()
if item == self.root:
self.setText('')
@@ -189,7 +191,7 @@ class FillingTree(wxTreeCtrl):
print text
if wxPlatform == '__WXMSW__':
if wx.wxPlatform == '__WXMSW__':
faces = { 'times' : 'Times New Roman',
'mono' : 'Courier New',
'helv' : 'Lucida Console',
@@ -201,8 +203,8 @@ if wxPlatform == '__WXMSW__':
}
# Versions of wxPython prior to 2.3.2 had a sizing bug on Win platform.
# The font was 2 points too large. So we need to reduce the font size.
if ((wxMAJOR_VERSION, wxMINOR_VERSION) == (2, 3) and wxRELEASE_NUMBER < 2) \
or (wxMAJOR_VERSION <= 2 and wxMINOR_VERSION <= 2):
if ((wx.wxMAJOR_VERSION, wx.wxMINOR_VERSION) == (2, 3) and wx.wxRELEASE_NUMBER < 2) \
or (wx.wxMAJOR_VERSION <= 2 and wx.wxMINOR_VERSION <= 2):
faces['size'] -= 2
faces['lnsize'] -= 2
else: # GTK
@@ -216,16 +218,16 @@ else: # GTK
}
class FillingText(wxStyledTextCtrl):
class FillingText(stc.wxStyledTextCtrl):
"""PyCrust FillingText based on wxStyledTextCtrl."""
name = 'PyCrust Filling Text'
revision = __revision__
def __init__(self, parent, id=-1, pos=wxDefaultPosition, \
size=wxDefaultSize, style=wxCLIP_CHILDREN):
def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition,
size=wx.wxDefaultSize, style=wx.wxCLIP_CHILDREN):
"""Create a PyCrust FillingText instance."""
wxStyledTextCtrl.__init__(self, parent, id, pos, size, style)
stc.wxStyledTextCtrl.__init__(self, parent, id, pos, size, style)
# Configure various defaults and user preferences.
self.config()
@@ -233,7 +235,7 @@ class FillingText(wxStyledTextCtrl):
"""Configure shell based on user preferences."""
self.SetMarginWidth(1, 0)
self.SetLexer(wxSTC_LEX_PYTHON)
self.SetLexer(stc.wxSTC_LEX_PYTHON)
self.SetKeyWords(0, ' '.join(keyword.kwlist))
self.setStyles(faces)
@@ -250,51 +252,51 @@ class FillingText(wxStyledTextCtrl):
"""Configure font size, typeface and color for lexer."""
# Default style
self.StyleSetSpec(wxSTC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces)
self.StyleSetSpec(stc.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")
self.StyleSetSpec(stc.wxSTC_STYLE_LINENUMBER, "back:#C0C0C0,face:%(mono)s,size:%(lnsize)d" % faces)
self.StyleSetSpec(stc.wxSTC_STYLE_CONTROLCHAR, "face:%(mono)s" % faces)
self.StyleSetSpec(stc.wxSTC_STYLE_BRACELIGHT, "fore:#0000FF,back:#FFFF88")
self.StyleSetSpec(stc.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)
self.StyleSetSpec(stc.wxSTC_P_DEFAULT, "face:%(mono)s" % faces)
self.StyleSetSpec(stc.wxSTC_P_COMMENTLINE, "fore:#007F00,face:%(mono)s" % faces)
self.StyleSetSpec(stc.wxSTC_P_NUMBER, "")
self.StyleSetSpec(stc.wxSTC_P_STRING, "fore:#7F007F,face:%(mono)s" % faces)
self.StyleSetSpec(stc.wxSTC_P_CHARACTER, "fore:#7F007F,face:%(mono)s" % faces)
self.StyleSetSpec(stc.wxSTC_P_WORD, "fore:#00007F,bold")
self.StyleSetSpec(stc.wxSTC_P_TRIPLE, "fore:#7F0000")
self.StyleSetSpec(stc.wxSTC_P_TRIPLEDOUBLE, "fore:#000033,back:#FFFFE8")
self.StyleSetSpec(stc.wxSTC_P_CLASSNAME, "fore:#0000FF,bold")
self.StyleSetSpec(stc.wxSTC_P_DEFNAME, "fore:#007F7F,bold")
self.StyleSetSpec(stc.wxSTC_P_OPERATOR, "")
self.StyleSetSpec(stc.wxSTC_P_IDENTIFIER, "")
self.StyleSetSpec(stc.wxSTC_P_COMMENTBLOCK, "fore:#7F7F7F")
self.StyleSetSpec(stc.wxSTC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eolfilled" % faces)
def SetText(self, *args, **kwds):
self.SetReadOnly(0)
wxStyledTextCtrl.SetText(self, *args, **kwds)
stc.wxStyledTextCtrl.SetText(self, *args, **kwds)
self.SetReadOnly(1)
class Filling(wxSplitterWindow):
class Filling(wx.wxSplitterWindow):
"""PyCrust Filling based on wxSplitterWindow."""
name = 'PyCrust Filling'
revision = __revision__
def __init__(self, parent, id=-1, pos=wxDefaultPosition, \
size=wxDefaultSize, style=wxSP_3D, name='Filling Window', \
def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition,
size=wx.wxDefaultSize, style=wx.wxSP_3D, name='Filling Window',
rootObject=None, rootLabel=None, rootIsNamespace=0):
"""Create a PyCrust Filling instance."""
wxSplitterWindow.__init__(self, parent, id, pos, size, style, name)
self.fillingTree = FillingTree(parent=self, rootObject=rootObject, \
rootLabel=rootLabel, \
wx.wxSplitterWindow.__init__(self, parent, id, pos, size, style, name)
self.fillingTree = FillingTree(parent=self, rootObject=rootObject,
rootLabel=rootLabel,
rootIsNamespace=rootIsNamespace)
self.fillingText = FillingText(parent=self)
self.SplitVertically(self.fillingTree, self.fillingText, 200)
@@ -305,41 +307,41 @@ class Filling(wxSplitterWindow):
self.fillingTree.SelectItem(self.fillingTree.root)
class FillingFrame(wxFrame):
class FillingFrame(wx.wxFrame):
"""Frame containing the PyCrust filling, or namespace tree component."""
name = 'PyCrust Filling Frame'
revision = __revision__
def __init__(self, parent=None, id=-1, title='PyFilling', \
pos=wxDefaultPosition, size=wxDefaultSize, \
style=wxDEFAULT_FRAME_STYLE, rootObject=None, \
def __init__(self, parent=None, id=-1, title='PyFilling',
pos=wx.wxDefaultPosition, size=wx.wxDefaultSize,
style=wx.wxDEFAULT_FRAME_STYLE, rootObject=None,
rootLabel=None, rootIsNamespace=0):
"""Create a PyCrust FillingFrame instance."""
wxFrame.__init__(self, parent, id, title, pos, size, style)
wx.wxFrame.__init__(self, parent, id, title, pos, size, style)
intro = 'Welcome To PyFilling - The Tastiest Namespace Inspector'
self.CreateStatusBar()
self.SetStatusText(intro)
if wxPlatform == '__WXMSW__':
if wx.wxPlatform == '__WXMSW__':
import os
filename = os.path.join(os.path.dirname(__file__), 'PyCrust.ico')
icon = wxIcon(filename, wxBITMAP_TYPE_ICO)
icon = wx.wxIcon(filename, wx.wxBITMAP_TYPE_ICO)
self.SetIcon(icon)
self.filling = Filling(parent=self, rootObject=rootObject, \
rootLabel=rootLabel, \
self.filling = Filling(parent=self, rootObject=rootObject,
rootLabel=rootLabel,
rootIsNamespace=rootIsNamespace)
# Override the filling so that status messages go to the status bar.
self.filling.fillingTree.setStatusText = self.SetStatusText
class App(wxApp):
class App(wx.wxApp):
"""PyFilling standalone application."""
def OnInit(self):
self.fillingFrame = FillingFrame()
self.fillingFrame.Show(true)
self.fillingFrame.Show(True)
self.SetTopWindow(self.fillingFrame)
return true
return True

View File

@@ -15,7 +15,7 @@ class Interpreter(InteractiveInterpreter):
revision = __revision__
def __init__(self, locals=None, rawin=None, \
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)
@@ -27,7 +27,7 @@ class Interpreter(InteractiveInterpreter):
__builtin__.raw_input = rawin
del __builtin__
copyright = \
'Type "copyright", "credits" or "license" for more information.'
'Type "help", "copyright", "credits" or "license" for more information.'
self.introText = 'Python %s on %s%s%s' % \
(sys.version, sys.platform, os.linesep, copyright)
try:
@@ -98,10 +98,10 @@ class Interpreter(InteractiveInterpreter):
class InterpreterAlaCarte(Interpreter):
"""PyCrustAlaCarte Demo Interpreter."""
def __init__(self, locals, rawin, stdin, stdout, stderr, \
def __init__(self, locals, rawin, stdin, stdout, stderr,
ps1='main prompt', ps2='continuation prompt'):
"""Create an interactive interpreter object."""
Interpreter.__init__(self, locals=locals, rawin=rawin, \
Interpreter.__init__(self, locals=locals, rawin=rawin,
stdin=stdin, stdout=stdout, stderr=stderr)
sys.ps1 = ps1
sys.ps2 = ps2

View File

@@ -5,11 +5,15 @@ __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__revision__ = "$Revision$"[11:-2]
from __future__ import nested_scopes
import cStringIO
import inspect
import string
import tokenize
import types
def getAutoCompleteList(command='', locals=None, includeMagic=1, \
def getAutoCompleteList(command='', locals=None, includeMagic=1,
includeSingle=1, includeDouble=1):
"""Return list of auto-completion options for command.
@@ -25,7 +29,7 @@ def getAutoCompleteList(command='', locals=None, includeMagic=1, \
except:
pass
else:
attributes = getAttributeNames(object, includeMagic, \
attributes = getAttributeNames(object, includeMagic,
includeSingle, includeDouble)
return attributes
@@ -36,8 +40,8 @@ def getAttributeNames(object, includeMagic=1, includeSingle=1, includeDouble=1):
if not hasattrAlwaysReturnsTrue(object):
# Add some attributes that don't always get picked up.
# If they don't apply, they'll get filtered out at the end.
attributes += ['__bases__', '__class__', '__dict__', '__name__', \
'func_closure', 'func_code', 'func_defaults', \
attributes += ['__bases__', '__class__', '__dict__', '__name__',
'func_closure', 'func_code', 'func_defaults',
'func_dict', 'func_doc', 'func_globals', 'func_name']
if includeMagic:
try: attributes += object._getAttributeNames()
@@ -182,37 +186,83 @@ def getRoot(command, terminator=None):
Return only the root portion that can be eval()'d without side effects.
The command would normally terminate with a "(" or ".". The terminator
and anything after the terminator will be dropped."""
root = ''
validChars = "._" + string.uppercase + string.lowercase + string.digits
emptyTypes = ("''", '""', '""""""', "''''''", '[]', '()', '{}')
validSeparators = string.whitespace + ',+-*/=%<>&|^~:([{'
# Drop the final terminator and anything that follows.
command = rtrimTerminus(command, terminator)
if len(command) == 0:
root = ''
elif command in emptyTypes and terminator in ('.', '', None):
# Let empty type delimiter pairs go through.
root = command
else:
# Go backward through the command until we hit an "invalid" character.
i = len(command)
while i and command[i-1] in validChars:
i -= 1
# Default to everything from the "invalid" character to the end.
root = command[i:]
# Override certain exceptions.
if i > 0 and command[i-1] in ("'", '"'):
# Detect situations where we are in the middle of a string.
# This code catches the simplest case, but needs to catch others.
root = ''
elif ((2 <= i < len(command) and command[i] == '.') \
or (2 <= i <= len(command) and terminator in ('.', '', None))) \
and command[i-2:i] in emptyTypes:
# Allow empty types to get through.
# Don't confuse an empty tupple with an argumentless callable.
if i == 2 or (i >= 3 and command[i-3] in validSeparators):
root = command[i-2:]
return root
command = command.rstrip()
tokens = getTokens(command)
tokens.reverse()
line = ''
start = None
prefix = ''
laststring = '.'
emptyTypes = ('[]', '()', '{}')
for token in tokens:
tokentype = token[0]
tokenstring = token[1]
line = token[4]
if tokentype is tokenize.ENDMARKER:
continue
if tokentype in (tokenize.NAME, tokenize.STRING, tokenize.NUMBER) \
and laststring != '.':
# We've reached something that's not part of the root.
if prefix and line[token[3][1]] != ' ':
# If it doesn't have a space after it, remove the prefix.
prefix = ''
break
if tokentype in (tokenize.NAME, tokenize.STRING, tokenize.NUMBER) \
or (tokentype is tokenize.OP and tokenstring == '.'):
if prefix:
# The prefix isn't valid because it comes after a dot.
prefix = ''
break
else:
# start represents the last known good point in the line.
start = token[2][1]
elif tokenstring in ('[({])}'):
# Remember, we're working backwords.
# So prefix += tokenstring would be wrong.
if prefix in emptyTypes and tokenstring in ('[({'):
# We've already got an empty type identified so now we are in
# a nested situation and we can break out with what we've got.
break
else:
prefix = tokenstring + prefix
else:
# We've reached something that's not part of the root.
break
laststring = tokenstring
if start is None:
start = len(line)
root = line[start:]
if prefix in emptyTypes:
# Empty types are safe to be eval()'d and introspected.
root = prefix + root
return root
def getTokens(command):
"""Return list of token tuples for command."""
command = str(command) # In case the command is unicode, which won't work.
f = cStringIO.StringIO(command)
# tokens is a list of token tuples, each looking like:
# (type, string, (srow, scol), (erow, ecol), line)
tokens = []
# Can't use list comprehension:
# tokens = [token for token in tokenize.generate_tokens(f.readline)]
# because of need to append as much as possible before TokenError.
try:
## This code wasn't backward compatible with Python 2.1.3.
##
## for token in tokenize.generate_tokens(f.readline):
## tokens.append(token)
# This works with Python 2.1.3 (with nested_scopes).
def eater(*args):
tokens.append(args)
tokenize.tokenize_loop(f.readline, eater)
except tokenize.TokenError:
# This is due to a premature EOF, which we expect since
# we are feeding in fragments of Python code.
pass
return tokens
def rtrimTerminus(command, terminator=None):
"""Return command minus the final terminator and anything that follows."""

View File

@@ -64,11 +64,13 @@ class PseudoFile:
class PseudoFileIn(PseudoFile):
def __init__(self, readline):
def __init__(self, readline, readlines=None):
if callable(readline):
self.readline = readline
else:
raise ValueError, 'readline must be callable'
if callable(readlines):
self.readlines = readlines
def isatty(self):
return 1

View File

@@ -8,21 +8,26 @@ __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__revision__ = "$Revision$"[11:-2]
from wxPython.wx import *
from wxPython.stc import *
from wxPython import wx
from wxPython import stc
import keyword
import os
import sys
import time
from pseudo import PseudoFileIn
from pseudo import PseudoFileOut
from pseudo import PseudoFileErr
from shellmenu import ShellMenu
from version import VERSION
True, False = 1, 0
sys.ps3 = '<-- ' # Input prompt.
NAVKEYS = (WXK_END, WXK_LEFT, WXK_RIGHT, WXK_UP, WXK_DOWN, WXK_PRIOR, WXK_NEXT)
NAVKEYS = (wx.WXK_END, wx.WXK_LEFT, wx.WXK_RIGHT,
wx.WXK_UP, wx.WXK_DOWN, wx.WXK_PRIOR, wx.WXK_NEXT)
if wxPlatform == '__WXMSW__':
if wx.wxPlatform == '__WXMSW__':
faces = { 'times' : 'Times New Roman',
'mono' : 'Courier New',
'helv' : 'Lucida Console',
@@ -32,11 +37,6 @@ if wxPlatform == '__WXMSW__':
'lnsize' : 9,
'backcol': '#FFFFFF',
}
# Versions of wxPython prior to 2.3.2 had a sizing bug on Win platform.
# The font was 2 points too large. So we need to reduce the font size.
if (wxMAJOR_VERSION, wxMINOR_VERSION, wxRELEASE_NUMBER) < (2, 3, 2):
faces['size'] -= 2
faces['lnsize'] -= 2
else: # GTK
faces = { 'times' : 'Times',
'mono' : 'Courier',
@@ -48,10 +48,42 @@ else: # GTK
}
class ShellFrame(wx.wxFrame, ShellMenu):
"""Frame containing the PyCrust shell component."""
name = 'PyCrust Shell Frame'
revision = __revision__
def __init__(self, parent=None, id=-1, title='PyShell',
pos=wx.wxDefaultPosition, size=wx.wxDefaultSize,
style=wx.wxDEFAULT_FRAME_STYLE, locals=None,
InterpClass=None, *args, **kwds):
"""Create a PyCrust ShellFrame instance."""
wx.wxFrame.__init__(self, parent, id, title, pos, size, style)
intro = 'Welcome To PyCrust %s - The Flakiest Python Shell' % VERSION
intro += '\nSponsored by Orbtech - Your source for Python programming expertise.'
self.CreateStatusBar()
self.SetStatusText(intro.replace('\n', ', '))
filename = os.path.join(os.path.dirname(__file__), 'PyCrust.ico')
icon = wx.wxIcon(filename, wx.wxBITMAP_TYPE_ICO)
self.SetIcon(icon)
self.shell = Shell(parent=self, id=-1, introText=intro,
locals=locals, InterpClass=InterpClass,
*args, **kwds)
# Override the shell so that status messages go to the status bar.
self.shell.setStatusText = self.SetStatusText
self.createMenus()
wx.EVT_CLOSE(self, self.OnCloseWindow)
def OnCloseWindow(self, event):
self.shell.destroy()
self.Destroy()
class ShellFacade:
"""Simplified interface to all shell-related functionality.
This is a semi-transparent facade, in that all attributes of other are
This is a semi-transparent facade, in that all attributes of other are
still accessible, even though only some are visible to the user."""
name = 'PyCrust Shell Interface'
@@ -59,19 +91,21 @@ class ShellFacade:
def __init__(self, other):
"""Create a ShellFacade instance."""
methods = ['ask',
'clear',
'pause',
'prompt',
'quit',
'redirectStderr',
'redirectStdin',
'redirectStdout',
'run',
'runfile',
'wrap',
'zoom',
]
methods = [
'about',
'ask',
'clear',
'pause',
'prompt',
'quit',
'redirectStderr',
'redirectStdin',
'redirectStdout',
'run',
'runfile',
'wrap',
'zoom',
]
for method in methods:
self.__dict__[method] = getattr(other, method)
d = self.__dict__
@@ -129,17 +163,17 @@ F8 Command-completion of History item.
return list
class Shell(wxStyledTextCtrl):
class Shell(stc.wxStyledTextCtrl):
"""PyCrust Shell based on wxStyledTextCtrl."""
name = 'PyCrust Shell'
revision = __revision__
def __init__(self, parent, id=-1, pos=wxDefaultPosition, \
size=wxDefaultSize, style=wxCLIP_CHILDREN, introText='', \
def __init__(self, parent, id=-1, pos=wx.wxDefaultPosition,
size=wx.wxDefaultSize, style=wx.wxCLIP_CHILDREN, introText='',
locals=None, InterpClass=None, *args, **kwds):
"""Create a PyCrust Shell instance."""
wxStyledTextCtrl.__init__(self, parent, id, pos, size, style)
stc.wxStyledTextCtrl.__init__(self, parent, id, pos, size, style)
# Grab these so they can be restored by self.redirect* methods.
self.stdin = sys.stdin
self.stdout = sys.stdout
@@ -160,15 +194,15 @@ class Shell(wxStyledTextCtrl):
if locals:
shellLocals.update(locals)
# Create a replacement for stdin.
self.reader = PseudoFileIn(self.readline)
self.reader = PseudoFileIn(self.readline, self.readlines)
self.reader.input = ''
self.reader.isreading = 0
# Set up the interpreter.
self.interp = Interpreter(locals=shellLocals, \
rawin=self.raw_input, \
stdin=self.reader, \
stdout=PseudoFileOut(self.writeOut), \
stderr=PseudoFileErr(self.writeErr), \
self.interp = Interpreter(locals=shellLocals,
rawin=self.raw_input,
stdin=self.reader,
stdout=PseudoFileOut(self.writeOut),
stderr=PseudoFileErr(self.writeErr),
*args, **kwds)
# Find out for which keycodes the interpreter will autocomplete.
self.autoCompleteKeys = self.interp.getAutoCompleteKeys()
@@ -186,10 +220,13 @@ class Shell(wxStyledTextCtrl):
self.history = []
self.historyIndex = -1
# Assign handlers for keyboard events.
EVT_KEY_DOWN(self, self.OnKeyDown)
EVT_CHAR(self, self.OnChar)
wx.EVT_KEY_DOWN(self, self.OnKeyDown)
wx.EVT_CHAR(self, self.OnChar)
# Assign handlers for wxSTC events.
EVT_STC_UPDATEUI(self, id, self.OnUpdateUI)
stc.EVT_STC_UPDATEUI(self, id, self.OnUpdateUI)
# Assign handler for idle time.
self.waiting = False
wx.EVT_IDLE(self, self.OnIdle)
# Configure various defaults and user preferences.
self.config()
# Display the introductory banner information.
@@ -212,10 +249,10 @@ class Shell(wxStyledTextCtrl):
def config(self):
"""Configure shell based on user preferences."""
self.SetMarginType(1, wxSTC_MARGIN_NUMBER)
self.SetMarginType(1, stc.wxSTC_MARGIN_NUMBER)
self.SetMarginWidth(1, 40)
self.SetLexer(wxSTC_LEX_PYTHON)
self.SetLexer(stc.wxSTC_LEX_PYTHON)
self.SetKeyWords(0, ' '.join(keyword.kwlist))
self.setStyles(faces)
@@ -231,10 +268,10 @@ class Shell(wxStyledTextCtrl):
self.AutoCompSetIgnoreCase(self.autoCompleteCaseInsensitive)
# Do we want to automatically pop up command argument help?
self.autoCallTip = 1
self.CallTipSetBackground(wxColour(255, 255, 232))
self.CallTipSetBackground(wx.wxColour(255, 255, 232))
self.wrap()
try:
self.SetEndAtLastLine(false)
self.SetEndAtLastLine(False)
except AttributeError:
pass
@@ -247,7 +284,7 @@ class Shell(wxStyledTextCtrl):
self.write(self.interp.introText)
except AttributeError:
pass
wxCallAfter(self.ScrollToLine, 0)
wx.wxCallAfter(self.ScrollToLine, 0)
def setBuiltinKeywords(self):
"""Create pseudo keywords as part of builtins.
@@ -286,33 +323,50 @@ class Shell(wxStyledTextCtrl):
"""Configure font size, typeface and color for lexer."""
# Default style
self.StyleSetSpec(wxSTC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d,back:%(backcol)s" % faces)
self.StyleSetSpec(stc.wxSTC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d,back:%(backcol)s" % 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")
self.StyleSetSpec(stc.wxSTC_STYLE_LINENUMBER, "back:#C0C0C0,face:%(mono)s,size:%(lnsize)d" % faces)
self.StyleSetSpec(stc.wxSTC_STYLE_CONTROLCHAR, "face:%(mono)s" % faces)
self.StyleSetSpec(stc.wxSTC_STYLE_BRACELIGHT, "fore:#0000FF,back:#FFFF88")
self.StyleSetSpec(stc.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)
self.StyleSetSpec(stc.wxSTC_P_DEFAULT, "face:%(mono)s" % faces)
self.StyleSetSpec(stc.wxSTC_P_COMMENTLINE, "fore:#007F00,face:%(mono)s" % faces)
self.StyleSetSpec(stc.wxSTC_P_NUMBER, "")
self.StyleSetSpec(stc.wxSTC_P_STRING, "fore:#7F007F,face:%(mono)s" % faces)
self.StyleSetSpec(stc.wxSTC_P_CHARACTER, "fore:#7F007F,face:%(mono)s" % faces)
self.StyleSetSpec(stc.wxSTC_P_WORD, "fore:#00007F,bold")
self.StyleSetSpec(stc.wxSTC_P_TRIPLE, "fore:#7F0000")
self.StyleSetSpec(stc.wxSTC_P_TRIPLEDOUBLE, "fore:#000033,back:#FFFFE8")
self.StyleSetSpec(stc.wxSTC_P_CLASSNAME, "fore:#0000FF,bold")
self.StyleSetSpec(stc.wxSTC_P_DEFNAME, "fore:#007F7F,bold")
self.StyleSetSpec(stc.wxSTC_P_OPERATOR, "")
self.StyleSetSpec(stc.wxSTC_P_IDENTIFIER, "")
self.StyleSetSpec(stc.wxSTC_P_COMMENTBLOCK, "fore:#7F7F7F")
self.StyleSetSpec(stc.wxSTC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eolfilled" % faces)
def OnUpdateUI(self, evt):
def about(self):
"""Display information about PyCrust."""
text = """
PyCrust Version: %s
Shell Revision: %s
Interpreter Revision: %s
Python Version: %s
wxPython Version: %s
Platform: %s""" % (VERSION, self.revision, self.interp.revision,
sys.version.split()[0], wx.__version__, sys.platform)
self.write(text.strip())
def OnIdle(self, event):
"""Free the CPU to do other things."""
if self.waiting:
time.sleep(0.1)
def OnUpdateUI(self, event):
"""Check for matching braces."""
braceAtCaret = -1
braceOpposite = -1
@@ -328,7 +382,7 @@ class Shell(wxStyledTextCtrl):
# Check before.
if charBefore and chr(charBefore) in '[]{}()' \
and styleBefore == wxSTC_P_OPERATOR:
and styleBefore == stc.wxSTC_P_OPERATOR:
braceAtCaret = caretPos - 1
# Check after.
@@ -340,7 +394,7 @@ class Shell(wxStyledTextCtrl):
#***
styleAfter = self.GetStyleAt(caretPos)
if charAfter and chr(charAfter) in '[]{}()' \
and styleAfter == wxSTC_P_OPERATOR:
and styleAfter == stc.wxSTC_P_OPERATOR:
braceAtCaret = caretPos
if braceAtCaret >= 0:
@@ -353,7 +407,7 @@ class Shell(wxStyledTextCtrl):
def OnChar(self, event):
"""Keypress event handler.
Only receives an event if OnKeyDown calls event.Skip() for
the corresponding event."""
@@ -364,7 +418,7 @@ class Shell(wxStyledTextCtrl):
currpos = self.GetCurrentPos()
stoppos = self.promptPosEnd
# Return (Enter) needs to be ignored in this handler.
if key == WXK_RETURN:
if key == wx.WXK_RETURN:
pass
elif key in self.autoCompleteKeys:
# Usually the dot (period) key activates auto completion.
@@ -399,12 +453,12 @@ class Shell(wxStyledTextCtrl):
endpos = self.GetTextLength()
selecting = self.GetSelectionStart() != self.GetSelectionEnd()
# Return (Enter) is used to submit a command to the interpreter.
if not controlDown and key == WXK_RETURN:
if not controlDown and key == wx.WXK_RETURN:
if self.AutoCompActive(): self.AutoCompCancel()
if self.CallTipActive(): self.CallTipCancel()
self.processLine()
# Ctrl+Return (Cntrl+Enter) is used to insert a line break.
elif controlDown and key == WXK_RETURN:
elif controlDown and key == wx.WXK_RETURN:
if self.AutoCompActive(): self.AutoCompCancel()
if self.CallTipActive(): self.CallTipCancel()
if currpos == endpos:
@@ -418,25 +472,25 @@ class Shell(wxStyledTextCtrl):
elif controlDown and altDown:
event.Skip()
# Clear the current, unexecuted command.
elif key == WXK_ESCAPE:
elif key == wx.WXK_ESCAPE:
if self.CallTipActive():
event.Skip()
else:
self.clearCommand()
# Cut to the clipboard.
elif (controlDown and key in (ord('X'), ord('x'))) \
or (shiftDown and key == WXK_DELETE):
or (shiftDown and key == wx.WXK_DELETE):
self.Cut()
# Copy to the clipboard.
elif controlDown and not shiftDown \
and key in (ord('C'), ord('c'), WXK_INSERT):
and key in (ord('C'), ord('c'), wx.WXK_INSERT):
self.Copy()
# Copy to the clipboard, including prompts.
elif controlDown and shiftDown \
and key in (ord('C'), ord('c'), WXK_INSERT):
and key in (ord('C'), ord('c'), wx.WXK_INSERT):
self.CopyWithPrompts()
# Home needs to be aware of the prompt.
elif key == WXK_HOME:
elif key == wx.WXK_HOME:
home = self.promptPosEnd
if currpos > home:
self.SetCurrentPos(home)
@@ -455,41 +509,41 @@ class Shell(wxStyledTextCtrl):
# Paste from the clipboard.
elif (controlDown and not shiftDown \
and key in (ord('V'), ord('v'))) \
or (shiftDown and not controlDown and key == WXK_INSERT):
or (shiftDown and not controlDown and key == wx.WXK_INSERT):
self.Paste()
# 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 == wx.WXK_UP) \
or (altDown and key in (ord('P'), ord('p'))):
self.OnHistoryReplace(step=+1)
# Replace with the next command from the history buffer.
elif (controlDown and key == WXK_DOWN) \
elif (controlDown and key == wx.WXK_DOWN) \
or (altDown and key in (ord('N'), ord('n'))):
self.OnHistoryReplace(step=-1)
# Insert the previous command from the history buffer.
elif (shiftDown and key == WXK_UP) and self.CanEdit():
elif (shiftDown and key == wx.WXK_UP) and self.CanEdit():
self.OnHistoryInsert(step=+1)
# Insert the next command from the history buffer.
elif (shiftDown and key == WXK_DOWN) and self.CanEdit():
elif (shiftDown and key == wx.WXK_DOWN) and self.CanEdit():
self.OnHistoryInsert(step=-1)
# Search up the history for the text in front of the cursor.
elif key == WXK_F8:
elif key == wx.WXK_F8:
self.OnHistorySearch()
# Don't backspace over the latest non-continuation prompt.
elif key == WXK_BACK:
elif key == wx.WXK_BACK:
if selecting and self.CanEdit():
event.Skip()
elif currpos > self.promptPosEnd:
event.Skip()
# Only allow these keys after the latest prompt.
elif key in (WXK_TAB, WXK_DELETE):
elif key in (wx.WXK_TAB, wx.WXK_DELETE):
if self.CanEdit():
event.Skip()
# Don't toggle between insert mode and overwrite mode.
elif key == WXK_INSERT:
elif key == wx.WXK_INSERT:
pass
# Don't allow line deletion.
elif controlDown and key in (ord('L'), ord('l')):
@@ -590,7 +644,7 @@ class Shell(wxStyledTextCtrl):
# The user hit ENTER and we need to decide what to do. They could be
# sitting on any line in the shell.
thepos = self.GetCurrentPos()
thepos = self.GetCurrentPos()
startpos = self.promptPosEnd
endpos = self.GetTextLength()
# If they hit RETURN inside the current command, execute the command.
@@ -685,12 +739,14 @@ class Shell(wxStyledTextCtrl):
elif text[:ps2size] == ps2:
text = text[ps2size:]
return text
def push(self, command):
"""Send command to the interpreter for execution."""
self.write(os.linesep)
busy = wxBusyCursor()
busy = wx.wxBusyCursor()
self.waiting = True
self.more = self.interp.push(command)
self.waiting = False
del busy
if not self.more:
self.addHistory(command.rstrip())
@@ -765,13 +821,34 @@ class Shell(wxStyledTextCtrl):
self.prompt()
try:
while not reader.input:
wxYield()
time.sleep(0.1) # Free up the CPU.
wx.wxYield()
input = reader.input
finally:
reader.input = ''
reader.isreading = 0
return input
def readlines(self):
"""Replacement for stdin.readlines()."""
lines = []
input = ''
reader = self.reader
reader.isreading = 1
try:
while lines[-1:] != ['\n']:
self.prompt()
while not reader.input:
time.sleep(0.1) # Free up the CPU.
wx.wxYield()
input = reader.input
lines.append(input)
reader.input = ''
finally:
reader.input = ''
reader.isreading = 0
return lines
def raw_input(self, prompt=''):
"""Return string based on user input."""
if prompt:
@@ -780,10 +857,10 @@ class Shell(wxStyledTextCtrl):
def ask(self, prompt='Please enter your response:'):
"""Get response from the user using a dialog box."""
dialog = wxTextEntryDialog(None, prompt, \
'Input Dialog (Raw)', '')
dialog = wx.wxTextEntryDialog(None, prompt,
'Input Dialog (Raw)', '')
try:
if dialog.ShowModal() == wxID_OK:
if dialog.ShowModal() == wx.wxID_OK:
text = dialog.GetValue()
return text
finally:
@@ -803,11 +880,11 @@ class Shell(wxStyledTextCtrl):
>>> shell.run('print "this"')
>>> print "this"
this
>>>
>>>
"""
# Go to the very bottom of the text.
endpos = self.GetTextLength()
self.SetCurrentPos(endpos)
self.SetCurrentPos(endpos)
command = command.rstrip()
if prompt: self.prompt()
if verbose: self.write(command)
@@ -828,9 +905,9 @@ class Shell(wxStyledTextCtrl):
def autoCompleteShow(self, command):
"""Display auto-completion popup list."""
list = self.interp.getAutoCompleteList(command,
includeMagic=self.autoCompleteIncludeMagic,
includeSingle=self.autoCompleteIncludeSingle,
list = self.interp.getAutoCompleteList(command,
includeMagic=self.autoCompleteIncludeMagic,
includeSingle=self.autoCompleteIncludeSingle,
includeDouble=self.autoCompleteIncludeDouble)
if list:
options = ' '.join(list)
@@ -898,7 +975,7 @@ class Shell(wxStyledTextCtrl):
def CanPaste(self):
"""Return true if a paste should succeed."""
if self.CanEdit() and wxStyledTextCtrl.CanPaste(self):
if self.CanEdit() and stc.wxStyledTextCtrl.CanPaste(self):
return 1
else:
return 0
@@ -929,26 +1006,28 @@ class Shell(wxStyledTextCtrl):
command = command.replace(os.linesep + sys.ps2, os.linesep)
command = command.replace(os.linesep + sys.ps1, os.linesep)
command = self.lstripPrompt(text=command)
data = wxTextDataObject(command)
if wxTheClipboard.Open():
wxTheClipboard.SetData(data)
wxTheClipboard.Close()
data = wx.wxTextDataObject(command)
if wx.wxTheClipboard.Open():
wx.wxTheClipboard.UsePrimarySelection(False)
wx.wxTheClipboard.SetData(data)
wx.wxTheClipboard.Close()
def CopyWithPrompts(self):
"""Copy selection, including prompts, and place it on the clipboard."""
if self.CanCopy():
command = self.GetSelectedText()
data = wxTextDataObject(command)
if wxTheClipboard.Open():
wxTheClipboard.SetData(data)
wxTheClipboard.Close()
data = wx.wxTextDataObject(command)
if wx.wxTheClipboard.Open():
wx.wxTheClipboard.UsePrimarySelection(False)
wx.wxTheClipboard.SetData(data)
wx.wxTheClipboard.Close()
def Paste(self):
"""Replace selection with clipboard contents."""
if self.CanPaste() and wxTheClipboard.Open():
if wxTheClipboard.IsSupported(wxDataFormat(wxDF_TEXT)):
data = wxTextDataObject()
if wxTheClipboard.GetData(data):
if self.CanPaste() and wx.wxTheClipboard.Open():
if wx.wxTheClipboard.IsSupported(wx.wxDataFormat(wx.wxDF_TEXT)):
data = wx.wxTextDataObject()
if wx.wxTheClipboard.GetData(data):
self.ReplaceSelection('')
command = data.GetText()
command = command.rstrip()
@@ -958,14 +1037,14 @@ class Shell(wxStyledTextCtrl):
command = command.replace(os.linesep, '\n')
command = command.replace('\n', os.linesep + sys.ps2)
self.write(command)
wxTheClipboard.Close()
wx.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):
if wx.wxTheClipboard.Open():
if wx.wxTheClipboard.IsSupported(wx.wxDataFormat(wx.wxDF_TEXT)):
data = wx.wxTextDataObject()
if wx.wxTheClipboard.GetData(data):
endpos = self.GetTextLength()
self.SetCurrentPos(endpos)
startpos = self.promptPosEnd
@@ -994,11 +1073,11 @@ class Shell(wxStyledTextCtrl):
command += '\n'
command += line
commands.append(command)
for command in commands:
for command in commands:
command = command.replace('\n', os.linesep + sys.ps2)
self.write(command)
self.processLine()
wxTheClipboard.Close()
wx.wxTheClipboard.Close()
def wrap(self, wrap=1):
"""Sets whether text is word wrapped."""
@@ -1009,214 +1088,8 @@ class Shell(wxStyledTextCtrl):
def zoom(self, points=0):
"""Set the zoom level.
This number of points is added to the size of all fonts.
It may be positive to magnify or negative to reduce."""
self.SetZoom(points)
wxID_SELECTALL = NewId() # This *should* be defined by wxPython.
ID_AUTOCOMP = NewId()
ID_AUTOCOMP_SHOW = NewId()
ID_AUTOCOMP_INCLUDE_MAGIC = NewId()
ID_AUTOCOMP_INCLUDE_SINGLE = NewId()
ID_AUTOCOMP_INCLUDE_DOUBLE = NewId()
ID_CALLTIPS = NewId()
ID_CALLTIPS_SHOW = NewId()
class ShellMenu:
"""Mixin class to add standard menu items."""
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', 'Delete the selection')
m.Append(wxID_SELECTALL, 'Select A&ll', 'Select all text')
m = self.autocompMenu = wxMenu()
m.Append(ID_AUTOCOMP_SHOW, 'Show Auto Completion', \
'Show auto completion during dot syntax', 1)
m.Append(ID_AUTOCOMP_INCLUDE_MAGIC, 'Include Magic Attributes', \
'Include attributes visible to __getattr__ and __setattr__', 1)
m.Append(ID_AUTOCOMP_INCLUDE_SINGLE, 'Include Single Underscores', \
'Include attibutes prefixed by a single underscore', 1)
m.Append(ID_AUTOCOMP_INCLUDE_DOUBLE, 'Include Double Underscores', \
'Include attibutes prefixed by a double underscore', 1)
m = self.calltipsMenu = wxMenu()
m.Append(ID_CALLTIPS_SHOW, 'Show Call Tips', \
'Show call tips with argument specifications', 1)
m = self.optionsMenu = wxMenu()
m.AppendMenu(ID_AUTOCOMP, '&Auto Completion', self.autocompMenu, \
'Auto Completion Options')
m.AppendMenu(ID_CALLTIPS, '&Call Tips', self.calltipsMenu, \
'Call Tip Options')
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.optionsMenu, '&Options')
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_MENU(self, ID_AUTOCOMP_SHOW, \
self.OnAutoCompleteShow)
EVT_MENU(self, ID_AUTOCOMP_INCLUDE_MAGIC, \
self.OnAutoCompleteIncludeMagic)
EVT_MENU(self, ID_AUTOCOMP_INCLUDE_SINGLE, \
self.OnAutoCompleteIncludeSingle)
EVT_MENU(self, ID_AUTOCOMP_INCLUDE_DOUBLE, \
self.OnAutoCompleteIncludeDouble)
EVT_MENU(self, ID_CALLTIPS_SHOW, \
self.OnCallTipsShow)
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)
EVT_UPDATE_UI(self, ID_AUTOCOMP_SHOW, self.OnUpdateMenu)
EVT_UPDATE_UI(self, ID_AUTOCOMP_INCLUDE_MAGIC, self.OnUpdateMenu)
EVT_UPDATE_UI(self, ID_AUTOCOMP_INCLUDE_SINGLE, self.OnUpdateMenu)
EVT_UPDATE_UI(self, ID_AUTOCOMP_INCLUDE_DOUBLE, self.OnUpdateMenu)
EVT_UPDATE_UI(self, ID_CALLTIPS_SHOW, self.OnUpdateMenu)
def OnExit(self, event):
self.Close(true)
def OnUndo(self, event):
self.shell.Undo()
def OnRedo(self, event):
self.shell.Redo()
def OnCut(self, event):
self.shell.Cut()
def OnCopy(self, event):
self.shell.Copy()
def OnPaste(self, event):
self.shell.Paste()
def OnClear(self, event):
self.shell.Clear()
def OnSelectAll(self, event):
self.shell.SelectAll()
def OnAbout(self, event):
"""Display an About PyCrust window."""
import sys
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.\n\n' + \
'Shell Revision: %s\n' % self.shell.revision + \
'Interpreter Revision: %s\n\n' % self.shell.interp.revision + \
'Python Version: %s\n' % sys.version.split()[0] + \
'wxPython Version: %s\n' % wx.__version__ + \
'Platform: %s\n' % sys.platform
dialog = wxMessageDialog(self, text, title, wxOK | wxICON_INFORMATION)
dialog.ShowModal()
dialog.Destroy()
def OnAutoCompleteShow(self, event):
self.shell.autoComplete = event.IsChecked()
def OnAutoCompleteIncludeMagic(self, event):
self.shell.autoCompleteIncludeMagic = event.IsChecked()
def OnAutoCompleteIncludeSingle(self, event):
self.shell.autoCompleteIncludeSingle = event.IsChecked()
def OnAutoCompleteIncludeDouble(self, event):
self.shell.autoCompleteIncludeDouble = event.IsChecked()
def OnCallTipsShow(self, event):
self.shell.autoCallTip = event.IsChecked()
def OnUpdateMenu(self, event):
"""Update menu items based on current status."""
id = event.GetId()
if id == wxID_UNDO:
event.Enable(self.shell.CanUndo())
elif id == wxID_REDO:
event.Enable(self.shell.CanRedo())
elif id == wxID_CUT:
event.Enable(self.shell.CanCut())
elif id == wxID_COPY:
event.Enable(self.shell.CanCopy())
elif id == wxID_PASTE:
event.Enable(self.shell.CanPaste())
elif id == wxID_CLEAR:
event.Enable(self.shell.CanCut())
elif id == ID_AUTOCOMP_SHOW:
event.Check(self.shell.autoComplete)
elif id == ID_AUTOCOMP_INCLUDE_MAGIC:
event.Check(self.shell.autoCompleteIncludeMagic)
elif id == ID_AUTOCOMP_INCLUDE_SINGLE:
event.Check(self.shell.autoCompleteIncludeSingle)
elif id == ID_AUTOCOMP_INCLUDE_DOUBLE:
event.Check(self.shell.autoCompleteIncludeDouble)
elif id == ID_CALLTIPS_SHOW:
event.Check(self.shell.autoCallTip)
class ShellFrame(wxFrame, ShellMenu):
"""Frame containing the PyCrust shell component."""
name = 'PyCrust Shell Frame'
revision = __revision__
def __init__(self, parent=None, id=-1, title='PyShell', \
pos=wxDefaultPosition, size=wxDefaultSize, \
style=wxDEFAULT_FRAME_STYLE, locals=None, \
InterpClass=None, *args, **kwds):
"""Create a PyCrust ShellFrame instance."""
wxFrame.__init__(self, parent, id, title, pos, size, style)
intro = 'Welcome To PyCrust %s - The Flakiest Python Shell' % VERSION
intro += '\nSponsored by Orbtech - Your source for Python programming expertise.'
self.CreateStatusBar()
self.SetStatusText(intro.replace('\n', ', '))
import images
self.SetIcon(images.getPyCrustIcon())
self.shell = Shell(parent=self, id=-1, introText=intro, \
locals=locals, InterpClass=InterpClass, \
*args, **kwds)
# Override the shell so that status messages go to the status bar.
self.shell.setStatusText = self.SetStatusText
self.createMenus()
EVT_CLOSE(self, self.OnCloseWindow)
def OnCloseWindow(self, event):
self.shell.destroy()
self.Destroy()

View File

@@ -0,0 +1,213 @@
"""Shell menu mixin shared by shell and crust."""
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__revision__ = "$Revision$"[11:-2]
from wxPython import wx
import sys
from version import VERSION
True, False = 1, 0
ID_AUTOCOMP = wx.NewId()
ID_AUTOCOMP_SHOW = wx.NewId()
ID_AUTOCOMP_INCLUDE_MAGIC = wx.NewId()
ID_AUTOCOMP_INCLUDE_SINGLE = wx.NewId()
ID_AUTOCOMP_INCLUDE_DOUBLE = wx.NewId()
ID_CALLTIPS = wx.NewId()
ID_CALLTIPS_SHOW = wx.NewId()
ID_COPY_PLUS = wx.NewId()
ID_PASTE_PLUS = wx.NewId()
ID_WRAP = wx.NewId()
class ShellMenu:
"""Mixin class to add standard menu items."""
def createMenus(self):
m = self.fileMenu = wx.wxMenu()
m.AppendSeparator()
m.Append(wx.wxID_EXIT, 'E&xit', 'Exit PyCrust')
m = self.editMenu = wx.wxMenu()
m.Append(wx.wxID_UNDO, '&Undo \tCtrl+Z', 'Undo the last action')
m.Append(wx.wxID_REDO, '&Redo \tCtrl+Y', 'Redo the last undone action')
m.AppendSeparator()
m.Append(wx.wxID_CUT, 'Cu&t \tCtrl+X', 'Cut the selection')
m.Append(wx.wxID_COPY, '&Copy \tCtrl+C', 'Copy the selection - removing prompts')
m.Append(ID_COPY_PLUS, 'Cop&y Plus \tCtrl+Shift+C', 'Copy the selection - retaining prompts')
m.Append(wx.wxID_PASTE, '&Paste \tCtrl+V', 'Paste')
m.Append(ID_PASTE_PLUS, 'Past&e Plus \tCtrl+Shift+V', 'Paste and run commands')
m.AppendSeparator()
m.Append(wx.wxID_CLEAR, 'Cle&ar', 'Delete the selection')
m.Append(wx.wxID_SELECTALL, 'Select A&ll \tCtrl+A', 'Select all text')
m = self.autocompMenu = wx.wxMenu()
m.Append(ID_AUTOCOMP_SHOW, 'Show Auto Completion',
'Show auto completion during dot syntax', 1)
m.Append(ID_AUTOCOMP_INCLUDE_MAGIC, 'Include Magic Attributes',
'Include attributes visible to __getattr__ and __setattr__', 1)
m.Append(ID_AUTOCOMP_INCLUDE_SINGLE, 'Include Single Underscores',
'Include attibutes prefixed by a single underscore', 1)
m.Append(ID_AUTOCOMP_INCLUDE_DOUBLE, 'Include Double Underscores',
'Include attibutes prefixed by a double underscore', 1)
m = self.calltipsMenu = wx.wxMenu()
m.Append(ID_CALLTIPS_SHOW, 'Show Call Tips',
'Show call tips with argument specifications', 1)
m = self.optionsMenu = wx.wxMenu()
m.AppendMenu(ID_AUTOCOMP, '&Auto Completion', self.autocompMenu,
'Auto Completion Options')
m.AppendMenu(ID_CALLTIPS, '&Call Tips', self.calltipsMenu,
'Call Tip Options')
m.Append(ID_WRAP, '&Wrap Lines',
'Wrap lines at right edge', 1)
m = self.helpMenu = wx.wxMenu()
m.AppendSeparator()
m.Append(wx.wxID_ABOUT, '&About...', 'About PyCrust')
b = self.menuBar = wx.wxMenuBar()
b.Append(self.fileMenu, '&File')
b.Append(self.editMenu, '&Edit')
b.Append(self.optionsMenu, '&Options')
b.Append(self.helpMenu, '&Help')
self.SetMenuBar(b)
wx.EVT_MENU(self, wx.wxID_EXIT, self.OnExit)
wx.EVT_MENU(self, wx.wxID_UNDO, self.OnUndo)
wx.EVT_MENU(self, wx.wxID_REDO, self.OnRedo)
wx.EVT_MENU(self, wx.wxID_CUT, self.OnCut)
wx.EVT_MENU(self, wx.wxID_COPY, self.OnCopy)
wx.EVT_MENU(self, ID_COPY_PLUS, self.OnCopyPlus)
wx.EVT_MENU(self, wx.wxID_PASTE, self.OnPaste)
wx.EVT_MENU(self, ID_PASTE_PLUS, self.OnPastePlus)
wx.EVT_MENU(self, wx.wxID_CLEAR, self.OnClear)
wx.EVT_MENU(self, wx.wxID_SELECTALL, self.OnSelectAll)
wx.EVT_MENU(self, wx.wxID_ABOUT, self.OnAbout)
wx.EVT_MENU(self, ID_AUTOCOMP_SHOW,
self.OnAutoCompleteShow)
wx.EVT_MENU(self, ID_AUTOCOMP_INCLUDE_MAGIC,
self.OnAutoCompleteIncludeMagic)
wx.EVT_MENU(self, ID_AUTOCOMP_INCLUDE_SINGLE,
self.OnAutoCompleteIncludeSingle)
wx.EVT_MENU(self, ID_AUTOCOMP_INCLUDE_DOUBLE,
self.OnAutoCompleteIncludeDouble)
wx.EVT_MENU(self, ID_CALLTIPS_SHOW,
self.OnCallTipsShow)
wx.EVT_MENU(self, ID_WRAP, self.OnWrap)
wx.EVT_UPDATE_UI(self, wx.wxID_UNDO, self.OnUpdateMenu)
wx.EVT_UPDATE_UI(self, wx.wxID_REDO, self.OnUpdateMenu)
wx.EVT_UPDATE_UI(self, wx.wxID_CUT, self.OnUpdateMenu)
wx.EVT_UPDATE_UI(self, wx.wxID_COPY, self.OnUpdateMenu)
wx.EVT_UPDATE_UI(self, ID_COPY_PLUS, self.OnUpdateMenu)
wx.EVT_UPDATE_UI(self, wx.wxID_PASTE, self.OnUpdateMenu)
wx.EVT_UPDATE_UI(self, ID_PASTE_PLUS, self.OnUpdateMenu)
wx.EVT_UPDATE_UI(self, wx.wxID_CLEAR, self.OnUpdateMenu)
wx.EVT_UPDATE_UI(self, ID_AUTOCOMP_SHOW, self.OnUpdateMenu)
wx.EVT_UPDATE_UI(self, ID_AUTOCOMP_INCLUDE_MAGIC, self.OnUpdateMenu)
wx.EVT_UPDATE_UI(self, ID_AUTOCOMP_INCLUDE_SINGLE, self.OnUpdateMenu)
wx.EVT_UPDATE_UI(self, ID_AUTOCOMP_INCLUDE_DOUBLE, self.OnUpdateMenu)
wx.EVT_UPDATE_UI(self, ID_CALLTIPS_SHOW, self.OnUpdateMenu)
wx.EVT_UPDATE_UI(self, ID_WRAP, self.OnUpdateMenu)
def OnExit(self, event):
self.Close(True)
def OnUndo(self, event):
self.shell.Undo()
def OnRedo(self, event):
self.shell.Redo()
def OnCut(self, event):
self.shell.Cut()
def OnCopy(self, event):
self.shell.Copy()
def OnCopyPlus(self, event):
self.shell.CopyWithPrompts()
def OnPaste(self, event):
self.shell.Paste()
def OnPastePlus(self, event):
self.shell.PasteAndRun()
def OnClear(self, event):
self.shell.Clear()
def OnSelectAll(self, event):
self.shell.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.\n\n' + \
'Shell Revision: %s\n' % self.shell.revision + \
'Interpreter Revision: %s\n\n' % self.shell.interp.revision + \
'Python Version: %s\n' % sys.version.split()[0] + \
'wxPython Version: %s\n' % wx.__version__ + \
'Platform: %s\n' % sys.platform
dialog = wx.wxMessageDialog(self, text, title, wx.wxOK | wx.wxICON_INFORMATION)
dialog.ShowModal()
dialog.Destroy()
def OnAutoCompleteShow(self, event):
self.shell.autoComplete = event.IsChecked()
def OnAutoCompleteIncludeMagic(self, event):
self.shell.autoCompleteIncludeMagic = event.IsChecked()
def OnAutoCompleteIncludeSingle(self, event):
self.shell.autoCompleteIncludeSingle = event.IsChecked()
def OnAutoCompleteIncludeDouble(self, event):
self.shell.autoCompleteIncludeDouble = event.IsChecked()
def OnCallTipsShow(self, event):
self.shell.autoCallTip = event.IsChecked()
def OnWrap(self, event):
self.shell.SetWrapMode(event.IsChecked())
def OnUpdateMenu(self, event):
"""Update menu items based on current status."""
id = event.GetId()
if id == wx.wxID_UNDO:
event.Enable(self.shell.CanUndo())
elif id == wx.wxID_REDO:
event.Enable(self.shell.CanRedo())
elif id == wx.wxID_CUT:
event.Enable(self.shell.CanCut())
elif id == wx.wxID_COPY:
event.Enable(self.shell.CanCopy())
elif id == ID_COPY_PLUS:
event.Enable(self.shell.CanCopy())
elif id == wx.wxID_PASTE:
event.Enable(self.shell.CanPaste())
elif id == ID_PASTE_PLUS:
event.Enable(self.shell.CanPaste())
elif id == wx.wxID_CLEAR:
event.Enable(self.shell.CanCut())
elif id == ID_AUTOCOMP_SHOW:
event.Check(self.shell.autoComplete)
elif id == ID_AUTOCOMP_INCLUDE_MAGIC:
event.Check(self.shell.autoCompleteIncludeMagic)
elif id == ID_AUTOCOMP_INCLUDE_SINGLE:
event.Check(self.shell.autoCompleteIncludeSingle)
elif id == ID_AUTOCOMP_INCLUDE_DOUBLE:
event.Check(self.shell.autoCompleteIncludeDouble)
elif id == ID_CALLTIPS_SHOW:
event.Check(self.shell.autoCallTip)
elif id == ID_WRAP:
event.Check(self.shell.GetWrapMode())

View File

@@ -6,5 +6,5 @@ __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__revision__ = "$Revision$"[11:-2]
VERSION = '0.7.2'
VERSION = '0.8'