new version of PyCrust
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@11395 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
3
wxPython/wxPython/lib/PyCrust/.cvsignore
Normal file
3
wxPython/wxPython/lib/PyCrust/.cvsignore
Normal file
@@ -0,0 +1,3 @@
|
||||
*.pyc
|
||||
*.BAK
|
||||
*.$$$
|
BIN
wxPython/wxPython/lib/PyCrust/PyCrust.ico
Normal file
BIN
wxPython/wxPython/lib/PyCrust/PyCrust.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 766 B |
@@ -9,8 +9,16 @@ __version__ = "$Revision$"[11:-2]
|
||||
|
||||
from wxPython.wx import *
|
||||
|
||||
from PyCrustVersion import version
|
||||
from PyCrustShell import Shell
|
||||
from version import VERSION
|
||||
from shell import Shell
|
||||
|
||||
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 Frame(wxFrame):
|
||||
@@ -18,15 +26,17 @@ class Frame(wxFrame):
|
||||
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
|
||||
intro = 'Welcome To PyCrust %s - The Flakiest Python Shell' % VERSION
|
||||
self.CreateStatusBar()
|
||||
self.SetStatusText(intro)
|
||||
self.icon = wxIcon('PyCrust.ico', wxBITMAP_TYPE_ICO)
|
||||
self.SetIcon(self.icon)
|
||||
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
|
||||
# Create the shell, which will create a default interpreter.
|
||||
locals = {'__app__': 'PyCrust Application'}
|
||||
self.shell = Shell(parent=self, id=-1, introText=intro, locals=locals)
|
||||
# Override the shell so that status messages go to the status bar.
|
||||
self.shell.setStatusText = self.SetStatusText
|
||||
|
||||
def createMenus(self):
|
||||
m = self.fileMenu = wxMenu()
|
||||
@@ -44,6 +54,26 @@ class Frame(wxFrame):
|
||||
m.Append(wxID_CLEAR, 'Cle&ar \tDel', 'Delete the selection')
|
||||
m.Append(wxID_SELECTALL, 'Select A&ll \tCtrl+A', 'Select all text')
|
||||
|
||||
m = self.autocompMenu = wxMenu()
|
||||
m.Append(ID_AUTOCOMP_SHOW, 'Show Auto Completion', \
|
||||
'Show auto completion during dot syntax', checkable=1)
|
||||
m.Append(ID_AUTOCOMP_INCLUDE_MAGIC, 'Include Magic Attributes', \
|
||||
'Include attributes visible to __getattr__ and __setattr__', checkable=1)
|
||||
m.Append(ID_AUTOCOMP_INCLUDE_SINGLE, 'Include Single Underscores', \
|
||||
'Include attibutes prefixed by a single underscore', checkable=1)
|
||||
m.Append(ID_AUTOCOMP_INCLUDE_DOUBLE, 'Include Double Underscores', \
|
||||
'Include attibutes prefixed by a double underscore', checkable=1)
|
||||
|
||||
m = self.calltipsMenu = wxMenu()
|
||||
m.Append(ID_CALLTIPS_SHOW, 'Show Call Tips', \
|
||||
'Show call tips with argument specifications', checkable=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')
|
||||
@@ -51,6 +81,7 @@ class Frame(wxFrame):
|
||||
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)
|
||||
|
||||
@@ -63,6 +94,11 @@ class Frame(wxFrame):
|
||||
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)
|
||||
@@ -70,58 +106,90 @@ class Frame(wxFrame):
|
||||
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.editor.Undo()
|
||||
self.shell.Undo()
|
||||
|
||||
def OnRedo(self, event):
|
||||
self.shell.editor.Redo()
|
||||
self.shell.Redo()
|
||||
|
||||
def OnCut(self, event):
|
||||
self.shell.editor.Cut()
|
||||
self.shell.Cut()
|
||||
|
||||
def OnCopy(self, event):
|
||||
self.shell.editor.Copy()
|
||||
self.shell.Copy()
|
||||
|
||||
def OnPaste(self, event):
|
||||
self.shell.editor.Paste()
|
||||
self.shell.Paste()
|
||||
|
||||
def OnClear(self, event):
|
||||
self.shell.editor.Clear()
|
||||
self.shell.Clear()
|
||||
|
||||
def OnSelectAll(self, event):
|
||||
self.shell.editor.SelectAll()
|
||||
self.shell.SelectAll()
|
||||
|
||||
def OnAbout(self, event):
|
||||
"""Display an About PyCrust window."""
|
||||
title = 'About PyCrust'
|
||||
text = 'PyCrust %s\n\n' % version + \
|
||||
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.'
|
||||
'the other half is still in the oven.\n\n' + \
|
||||
'Shell Revision: %s\n' % self.shell.revision + \
|
||||
'Interpreter Revision: %s\n' % self.shell.interp.revision
|
||||
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 which should be enabled/disabled."""
|
||||
"""Update menu items based on current status."""
|
||||
id = event.GetId()
|
||||
if id == wxID_UNDO:
|
||||
event.Enable(self.shell.editor.CanUndo())
|
||||
event.Enable(self.shell.CanUndo())
|
||||
elif id == wxID_REDO:
|
||||
event.Enable(self.shell.editor.CanRedo())
|
||||
event.Enable(self.shell.CanRedo())
|
||||
elif id == wxID_CUT:
|
||||
event.Enable(self.shell.editor.CanCut())
|
||||
event.Enable(self.shell.CanCut())
|
||||
elif id == wxID_COPY:
|
||||
event.Enable(self.shell.editor.CanCopy())
|
||||
event.Enable(self.shell.CanCopy())
|
||||
elif id == wxID_PASTE:
|
||||
event.Enable(self.shell.editor.CanPaste())
|
||||
event.Enable(self.shell.CanPaste())
|
||||
elif id == wxID_CLEAR:
|
||||
event.Enable(self.shell.editor.CanCut())
|
||||
|
||||
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 App(wxApp):
|
||||
def OnInit(self):
|
||||
|
@@ -1,180 +0,0 @@
|
||||
"""
|
||||
"""
|
||||
__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()
|
||||
|
||||
|
43
wxPython/wxPython/lib/PyCrust/README.txt
Normal file
43
wxPython/wxPython/lib/PyCrust/README.txt
Normal file
@@ -0,0 +1,43 @@
|
||||
PyCrust - The Flakiest Python Shell
|
||||
Half-baked by Patrick K. O'Brien (pobrien@orbtech.com)
|
||||
======================================================
|
||||
|
||||
What is PyCrust?
|
||||
----------------
|
||||
|
||||
PyCrust is is an interactive Python Shell written in Python.
|
||||
PyCrust can be run standalone or integrated into other development
|
||||
environments or other Python applications.
|
||||
|
||||
|
||||
Where can I get the latest files and join the mailing lists?
|
||||
------------------------------------------------------------
|
||||
|
||||
Latest PyCrust releases:
|
||||
http://sourceforge.net/project/showfiles.php?group_id=31263
|
||||
|
||||
PyCrust home page:
|
||||
http://pycrust.sourceforge.net/
|
||||
|
||||
SourceForge summary page:
|
||||
http://sourceforge.net/projects/pycrust/
|
||||
|
||||
PyCrust Mailing lists:
|
||||
http://sourceforge.net/mail/?group_id=31263
|
||||
|
||||
|
||||
What else do I need to use PyCrust?
|
||||
-----------------------------------
|
||||
|
||||
PyCrust requires Python 2.1 or later, and wxPython 2.3.1 or later.
|
||||
PyCrust uses wxPython and the Scintilla wrapper class (wxStyledTextCtrl).
|
||||
Python is available at http://www.python.org/.
|
||||
wxPython is available at http://www.wxpython.org/.
|
||||
|
||||
|
||||
What is the CVS information for this README file?
|
||||
-------------------------------------------------
|
||||
|
||||
$Date$
|
||||
$Revision$
|
||||
$Id$
|
@@ -1,3 +1,5 @@
|
||||
"""PyCrust Interpreter executes Python commands.
|
||||
"""
|
||||
|
||||
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
|
||||
__cvsid__ = "$Id$"
|
||||
@@ -67,15 +69,15 @@ class Interpreter(InteractiveInterpreter):
|
||||
sys.stdin, sys.stdout, sys.stderr = stdin, stdout, stderr
|
||||
return more
|
||||
|
||||
def getAutoCompleteList(self, command=''):
|
||||
def getAutoCompleteList(self, command='', *args, **kwds):
|
||||
"""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)
|
||||
return introspect.getAutoCompleteList(command, self.locals, *args, **kwds)
|
||||
|
||||
def getCallTip(self, command=''):
|
||||
def getCallTip(self, command='', *args, **kwds):
|
||||
"""Return call tip text for a command.
|
||||
|
||||
The call tip information will be based on the locals namespace."""
|
||||
return introspect.getCallTip(command, self.locals)
|
||||
return introspect.getCallTip(command, self.locals, *args, **kwds)
|
||||
|
@@ -9,7 +9,8 @@ __version__ = "$Revision$"[11:-2]
|
||||
import inspect
|
||||
import string
|
||||
|
||||
def getAutoCompleteList(command='', locals=None):
|
||||
def getAutoCompleteList(command='', locals=None, includeMagic=1, \
|
||||
includeSingle=1, includeDouble=1):
|
||||
"""Return list of auto-completion options for command.
|
||||
|
||||
The list of options will be based on the locals namespace."""
|
||||
@@ -18,37 +19,45 @@ def getAutoCompleteList(command='', locals=None):
|
||||
root = getRoot(command, terminator='.')
|
||||
try:
|
||||
object = eval(root, locals)
|
||||
attributes = getAttributes(object)
|
||||
attributes = getAttributeNames(object)
|
||||
if includeMagic:
|
||||
try: attributes += object._getAttributeNames()
|
||||
except: pass
|
||||
if not includeSingle:
|
||||
attributes = filter(lambda item: item[0]!='_' or item[1]=='_', attributes)
|
||||
if not includeDouble:
|
||||
attributes = filter(lambda item: item[:2]!='__', attributes)
|
||||
return attributes
|
||||
except:
|
||||
return []
|
||||
|
||||
def getAttributes(object):
|
||||
def getAttributeNames(object):
|
||||
"""Return list of unique attributes, including inherited, for an object."""
|
||||
attributes = []
|
||||
dict = {}
|
||||
# Remove duplicates from the attribute list.
|
||||
for item in getAllAttributes(object):
|
||||
for item in getAllAttributeNames(object):
|
||||
dict[item] = None
|
||||
attributes += dict.keys()
|
||||
attributes.sort()
|
||||
return attributes
|
||||
|
||||
def getAllAttributes(object):
|
||||
def getAllAttributeNames(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
|
||||
# Get attributes available through the normal convention.
|
||||
attributes += dir(object)
|
||||
# For a class instance, get the attributes for the class.
|
||||
if hasattr(object, '__class__'):
|
||||
attributes += getAllAttributeNames(object.__class__)
|
||||
# Also get attributes from any and all parent classes.
|
||||
if hasattr(object, '__bases__'):
|
||||
for base in object.__bases__:
|
||||
attributes += getAllAttributeNames(base)
|
||||
return attributes
|
||||
|
||||
def getCallTip(command='', locals=None):
|
||||
"""Return call tip text for a command.
|
||||
@@ -119,22 +128,31 @@ def getRoot(command, terminator=None):
|
||||
|
||||
The command would normally terminate with a "(" or ".". Anything after
|
||||
the terminator will be dropped, allowing you to get back to the root.
|
||||
Return only the root portion that can be eval()'d without side effect.
|
||||
"""
|
||||
root = ''
|
||||
validChars = "._" + string.uppercase + string.lowercase + string.digits
|
||||
# Remove all whitespace from the command.
|
||||
command = ''.join(command.split())
|
||||
# Deal with the terminator.
|
||||
if terminator:
|
||||
# Drop the final terminator and anything that follows.
|
||||
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:]
|
||||
if command in ("''", '""', '""""""', '[]', '()', '{}'):
|
||||
# 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
|
||||
# Detect situations where we are in the middle of a string.
|
||||
# This code catches the simplest case, but needs to catch others.
|
||||
if command[i-1] in ("'", '"'):
|
||||
# Were in the middle of a string so we aren't dealing with an
|
||||
# object and it would be misleading to return anything here.
|
||||
root = ''
|
||||
else:
|
||||
# Grab everything from the "invalid" character to the end.
|
||||
root = command[i:]
|
||||
return root
|
||||
|
||||
|
@@ -1,3 +1,7 @@
|
||||
"""PyCrust Shell is the gui text control in which a user interacts and types
|
||||
in commands to be sent to the interpreter. This particular shell is based on
|
||||
wxPython's wxStyledTextCtrl.
|
||||
"""
|
||||
|
||||
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
|
||||
__cvsid__ = "$Id$"
|
||||
@@ -8,8 +12,10 @@ from wxPython.wx import *
|
||||
from wxPython.stc import *
|
||||
|
||||
import keyword
|
||||
import os
|
||||
import sys
|
||||
|
||||
from version import VERSION
|
||||
|
||||
if wxPlatform == '__WXMSW__':
|
||||
faces = { 'times' : 'Times New Roman',
|
||||
@@ -32,26 +38,77 @@ else: # GTK
|
||||
}
|
||||
|
||||
|
||||
class Editor(wxStyledTextCtrl):
|
||||
"""PyCrust Editor based on wxStyledTextCtrl."""
|
||||
class Shell(wxStyledTextCtrl):
|
||||
"""PyCrust Shell based on wxStyledTextCtrl."""
|
||||
name = 'PyCrust Shell'
|
||||
revision = __version__
|
||||
def __init__(self, parent, id):
|
||||
"""Create a PyCrust editor object based on wxStyledTextCtrl."""
|
||||
def __init__(self, parent, id, introText='', locals=None, interp=None):
|
||||
"""Create a PyCrust Shell object."""
|
||||
wxStyledTextCtrl.__init__(self, parent, id, style=wxCLIP_CHILDREN)
|
||||
# Commands get pushed to a method determined by the outer shell.
|
||||
#self.shellPush = pushMethod
|
||||
self.introText = introText
|
||||
# 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)
|
||||
# Create a default interpreter if one isn't provided.
|
||||
if interp == None:
|
||||
from interpreter import Interpreter
|
||||
from pseudo import PseudoFileIn, PseudoFileOut, PseudoFileErr
|
||||
self.stdin = PseudoFileIn(self.readIn)
|
||||
self.stdout = PseudoFileOut(self.writeOut)
|
||||
self.stderr = PseudoFileErr(self.writeErr)
|
||||
# Override the default locals so we have something interesting.
|
||||
self.locals = {'__name__': 'PyCrust',
|
||||
'__doc__': 'PyCrust, The Python Shell.',
|
||||
'__version__': VERSION,
|
||||
}
|
||||
# Add the dictionary that was passed in.
|
||||
if locals:
|
||||
self.locals.update(locals)
|
||||
self.interp = Interpreter(locals=self.locals,
|
||||
rawin=self.readRaw,
|
||||
stdin=self.stdin,
|
||||
stdout=self.stdout,
|
||||
stderr=self.stderr)
|
||||
else:
|
||||
self.interp = interp
|
||||
|
||||
# Configure various defaults and user preferences.
|
||||
self.config()
|
||||
|
||||
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.stdin
|
||||
del self.stdout
|
||||
del self.stderr
|
||||
del self.interp
|
||||
|
||||
def config(self):
|
||||
"""Configure editor based on user preferences."""
|
||||
"""Configure shell based on user preferences."""
|
||||
self.SetMarginType(1, wxSTC_MARGIN_NUMBER)
|
||||
self.SetMarginWidth(1, 40)
|
||||
|
||||
@@ -64,12 +121,62 @@ class Editor(wxStyledTextCtrl):
|
||||
self.SetUseTabs(0)
|
||||
# Do we want to automatically pop up command completion options?
|
||||
self.autoComplete = 1
|
||||
self.autoCompleteIncludeMagic = 1
|
||||
self.autoCompleteIncludeSingle = 1
|
||||
self.autoCompleteIncludeDouble = 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 showIntro(self, text=''):
|
||||
"""Display introductory text in the shell."""
|
||||
if text:
|
||||
if text[-1] != '\n': text += '\n'
|
||||
self.write(text)
|
||||
try:
|
||||
self.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.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.push('print %s;execfile(%s)' % \
|
||||
(`startupText`, `startupScript`))
|
||||
else:
|
||||
self.push('')
|
||||
|
||||
def setStyles(self, faces):
|
||||
"""Configure font size, typeface and color for lexer."""
|
||||
|
||||
@@ -180,48 +287,18 @@ class Editor(wxStyledTextCtrl):
|
||||
# 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.
|
||||
# sitting on any line in the shell.
|
||||
|
||||
# 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.
|
||||
# Go to the very bottom of the text.
|
||||
endpos = self.GetTextLength()
|
||||
self.SetCurrentPos(endpos)
|
||||
endline = self.GetCurrentLine()
|
||||
@@ -266,19 +343,21 @@ class Editor(wxStyledTextCtrl):
|
||||
return command
|
||||
|
||||
def push(self, command):
|
||||
"""Start a new line, send command to the shell, display a prompt."""
|
||||
"""Send command to the interpreter for execution."""
|
||||
self.write('\n')
|
||||
self.more = self.shellPush(command)
|
||||
self.more = self.interp.push(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 write(self, text):
|
||||
"""Display text in the shell."""
|
||||
self.AddText(text)
|
||||
self.EnsureCaretVisible()
|
||||
#self.ScrollToColumn(0)
|
||||
|
||||
def prompt(self):
|
||||
"""Display appropriate prompt for the context, either ps1 or ps2.
|
||||
|
||||
@@ -325,12 +404,62 @@ class Editor(wxStyledTextCtrl):
|
||||
dialog.Destroy()
|
||||
return ''
|
||||
|
||||
def write(self, text):
|
||||
"""Display text in the editor."""
|
||||
self.AddText(text)
|
||||
self.EnsureCaretVisible()
|
||||
#self.ScrollToColumn(0)
|
||||
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."""
|
||||
self.ClearAll()
|
||||
|
||||
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.prompt()
|
||||
if verbose: self.write(command)
|
||||
self.push(command)
|
||||
|
||||
def runfile(self, filename):
|
||||
"""Execute all commands in file as if they were typed into the shell."""
|
||||
file = open(filename)
|
||||
try:
|
||||
self.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 autoCompleteShow(self, command):
|
||||
"""Display auto-completion popup list."""
|
||||
list = self.interp.getAutoCompleteList(command, \
|
||||
includeMagic=self.autoCompleteIncludeMagic, \
|
||||
includeSingle=self.autoCompleteIncludeSingle, \
|
||||
includeDouble=self.autoCompleteIncludeDouble)
|
||||
if list:
|
||||
options = ' '.join(list)
|
||||
offset = 0
|
||||
self.AutoCompShow(offset, options)
|
||||
|
||||
def autoCallTipShow(self, command):
|
||||
"""Display argument spec and docstring in a popup bubble thingie."""
|
||||
if self.CallTipActive: self.CallTipCancel()
|
||||
tip = self.interp.getCallTip(command)
|
||||
if tip:
|
||||
offset = self.GetCurrentPos()
|
||||
self.CallTipShow(offset, tip)
|
||||
|
||||
def writeOut(self, text):
|
||||
"""Replacement for stdout."""
|
||||
self.write(text)
|
@@ -1,10 +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$."""
|
||||
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'
|
||||
VERSION = '0.5.2'
|
Reference in New Issue
Block a user