New PyCrust from Patrick O'Brien

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@15058 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2002-04-09 21:57:35 +00:00
parent aacd7bb647
commit 31fa82942c
10 changed files with 223 additions and 167 deletions

View File

@@ -3,18 +3,18 @@
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__version__ = "$Revision$"[11:-2]
__revision__ = "$Revision$"[11:-2]
from wxPython.wx import *
from crust import CrustFrame
from PyCrust.crust import CrustFrame
class App(wxApp):
"""PyCrust standalone application."""
def OnInit(self):
locals = {'__app__': 'PyCrust Standalone Application'}
self.crustFrame = CrustFrame(locals=locals, size=(800,600))
self.crustFrame = CrustFrame(locals=locals)
self.crustFrame.Show(true)
self.SetTopWindow(self.crustFrame)
# Add the application object to the sys module's namespace.
@@ -33,4 +33,4 @@ def main():
if __name__ == '__main__':
main()

View File

@@ -3,19 +3,19 @@
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__version__ = "$Revision$"[11:-2]
__revision__ = "$Revision$"[11:-2]
# We use this object to get more introspection when run standalone.
application = None
import filling
from PyCrust import filling
# These are imported just to have something interesting to inspect.
import crust
import interpreter
import introspect
import pseudo
import shell
from PyCrust import crust
from PyCrust import interpreter
from PyCrust import introspect
from PyCrust import pseudo
from PyCrust import shell
import sys
from wxPython import wx
@@ -32,4 +32,4 @@ def main():
if __name__ == '__main__':
main()

View File

@@ -23,12 +23,6 @@ Have you ever tried to bake a pie without one? Well, you
shouldn't build a Python program without a PyCrust either.
Where can I get the latest release of PyCrust?
----------------------------------------------
The latest PyCrust releases are available at:
http://sourceforge.net/project/showfiles.php?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.
@@ -37,32 +31,36 @@ Python is available at http://www.python.org/.
wxPython is available at http://www.wxpython.org/.
Where can I get the latest version of PyCrust?
----------------------------------------------
The latest production version ships with wxPython.
The latest developer version is available in CVS at:
http://sourceforge.net/cvs/?group_id=31263
Where is the PyCrust project hosted?
------------------------------------
At SourceForge, of course. The SourceForge summary page:
http://sourceforge.net/projects/pycrust/
I found a bug in PyCrust, what do I do with it?
-----------------------------------------------
You can send it to me at pobrien@orbtech.com.
I want a new feature added to PyCrust. Will you do it?
------------------------------------------------------
Flattery and money will get you anything. Short of that, you
can send me a request and I'll see what I can do.
Does PyCrust have a mailing list full of wonderful people?
----------------------------------------------------------
As a matter of fact, we do. Join the PyCrust mailing lists at:
http://sourceforge.net/mail/?group_id=31263
I found a bug in PyCrust, what do I do with it?
-----------------------------------------------
You can send it to me at pobrien@orbtech.com, or, preferably,
submit a bug report on our bug tracker at SourceForge:
http://sourceforge.net/tracker/?group_id=31263
I want a new feature added to PyCrust. Will you do it?
------------------------------------------------------
Flattery and money will get you anything. Short of that, you
can try posting a request on our feature tracker at SourceForge:
http://sourceforge.net/tracker/?group_id=31263
What is the CVS information for this README file?
-------------------------------------------------
$Date$

View File

@@ -2,7 +2,7 @@
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__version__ = "$Revision$"[11:-2]
__revision__ = "$Revision$"[11:-2]
from wxPython.wx import *
from shell import Shell
@@ -14,7 +14,7 @@ class Crust(wxSplitterWindow):
"""PyCrust Crust based on wxSplitterWindow."""
name = 'PyCrust Crust'
revision = __version__
revision = __revision__
def __init__(self, parent, id=-1, pos=wxDefaultPosition, \
size=wxDefaultSize, style=wxSP_3D, name='Crust Window', \
@@ -42,7 +42,7 @@ class CrustFrame(wxFrame, ShellMenu):
"""Frame containing all the PyCrust components."""
name = 'PyCrust Frame'
revision = __version__
revision = __revision__
def __init__(self, parent=None, id=-1, title='PyCrust', \
pos=wxDefaultPosition, size=wxDefaultSize, \
@@ -52,7 +52,7 @@ class CrustFrame(wxFrame, ShellMenu):
"""Create a PyCrust CrustFrame 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 Development Services'
intro += '\nSponsored by Orbtech - Specializing in Python Application Development'
self.CreateStatusBar()
self.SetStatusText(intro.replace('\n', ', '))
if wxPlatform == '__WXMSW__':

View File

@@ -3,7 +3,7 @@ the local namespace or any object."""
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__version__ = "$Revision$"[11:-2]
__revision__ = "$Revision$"[11:-2]
from wxPython.wx import *
from wxPython.stc import *
@@ -19,7 +19,7 @@ class FillingTree(wxTreeCtrl):
"""PyCrust FillingTree based on wxTreeCtrl."""
name = 'PyCrust Filling Tree'
revision = __version__
revision = __revision__
def __init__(self, parent, id=-1, pos=wxDefaultPosition, \
size=wxDefaultSize, style=wxTR_HAS_BUTTONS, \
@@ -203,7 +203,7 @@ class FillingText(wxStyledTextCtrl):
"""PyCrust FillingText based on wxStyledTextCtrl."""
name = 'PyCrust Filling Text'
revision = __version__
revision = __revision__
def __init__(self, parent, id=-1, pos=wxDefaultPosition, \
size=wxDefaultSize, style=wxCLIP_CHILDREN):
@@ -265,7 +265,7 @@ class Filling(wxSplitterWindow):
"""PyCrust Filling based on wxSplitterWindow."""
name = 'PyCrust Filling'
revision = __version__
revision = __revision__
def __init__(self, parent, id=-1, pos=wxDefaultPosition, \
size=wxDefaultSize, style=wxSP_3D, name='Filling Window', \
@@ -288,7 +288,7 @@ class FillingFrame(wxFrame):
"""Frame containing the PyCrust filling, or namespace tree component."""
name = 'PyCrust Filling Frame'
revision = __version__
revision = __revision__
def __init__(self, parent=None, id=-1, title='PyFilling', \
pos=wxDefaultPosition, size=wxDefaultSize, \

View File

@@ -2,7 +2,7 @@
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__version__ = "$Revision$"[11:-2]
__revision__ = "$Revision$"[11:-2]
import os
import sys
@@ -13,7 +13,7 @@ import introspect
class Interpreter(InteractiveInterpreter):
"""PyCrust Interpreter based on code.InteractiveInterpreter."""
revision = __version__
revision = __revision__
def __init__(self, locals=None, rawin=None, \
stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr):
@@ -102,4 +102,4 @@ class InterpreterAlaCarte(Interpreter):
sys.ps1 = ps1
sys.ps2 = ps2

View File

@@ -3,7 +3,7 @@ like call tips and command auto completion."""
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__version__ = "$Revision$"[11:-2]
__revision__ = "$Revision$"[11:-2]
import inspect
import string
@@ -14,117 +14,128 @@ def getAutoCompleteList(command='', locals=None, includeMagic=1, \
"""Return list of auto-completion options for command.
The list of options will be based on the locals namespace."""
attributes = []
# Get the proper chunk of code from the command.
root = getRoot(command, terminator='.')
try:
object = eval(root, locals)
if locals is not None:
object = eval(root, locals)
else:
object = eval(root)
except:
pass
else:
attributes = getAttributeNames(object, includeMagic, \
includeSingle, includeDouble)
return attributes
except:
return []
return attributes
def getAttributeNames(object, includeMagic=1, includeSingle=1, includeDouble=1):
"""Return list of unique attributes, including inherited, for an object."""
attributes = []
dict = {}
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', \
'func_dict', 'func_doc', 'func_globals', 'func_name']
if includeMagic:
try: attributes += object._getAttributeNames()
except: pass
# Get all attribute names, removing duplicates from the attribute list.
for item in getAllAttributeNames(object):
# Get all attribute names.
attrdict = getAllAttributeNames(object)
for attrlist in attrdict.values():
attributes += attrlist
# Remove duplicates from the attribute list.
for item in attributes:
dict[item] = None
attributes += dict.keys()
attributes = dict.keys()
attributes.sort(lambda x, y: cmp(x.upper(), y.upper()))
if not includeSingle:
attributes = filter(lambda item: item[0]!='_' or item[1]=='_', attributes)
attributes = filter(lambda item: item[0]!='_' \
or item[1]=='_', attributes)
if not includeDouble:
attributes = filter(lambda item: item[:2]!='__', attributes)
# Make sure we haven't picked up any bogus attributes somehow.
attributes = [attribute for attribute in attributes if hasattr(object, attribute)]
attributes = [attribute for attribute in attributes \
if hasattr(object, attribute)]
return attributes
def hasattrAlwaysReturnsTrue(object):
return hasattr(object, 'bogu5_123_aTTri8ute')
def getAllAttributeNames(object):
"""Return list of all attributes, including inherited, for an object.
"""Return mapping of all attributes, including inherited, for an object.
Recursively walk through a class and all base classes.
"""
attrdict = {} # (object, technique, count): [list of attributes]
# !!!
# !!! Do Not use hasattr() as a test anywhere in this function,
# !!! because it is unreliable with remote objects - xmlrpc, soap, etc.
# !!! They always return true for hasattr().
# !!!
attributes = []
key = str(object)
# Wake up sleepy objects - a hack for ZODB objects in "ghost" state.
wakeupcall = dir(object)
del wakeupcall
# Get attributes available through the normal convention.
attributes += dir(object)
attributes = dir(object)
attrdict[(key, 'dir', len(attributes))] = attributes
# Get attributes from the object's dictionary, if it has one.
try:
keys = object.__dict__.keys()
except:
attributes = object.__dict__.keys()
attributes.sort()
except: # Must catch all because object might have __getattr__.
pass
else:
attributes += keys
attrdict[(key, '__dict__', len(attributes))] = attributes
# For a class instance, get the attributes for the class.
if hasattr(object, '__class__'):
# Break a circular reference. This happens with extension classes.
if object.__class__ is object:
try:
klass = object.__class__
except: # Must catch all because object might have __getattr__.
pass
else:
if klass is object:
# Break a circular reference. This happens with extension classes.
pass
else:
attributes += getAllAttributeNames(object.__class__)
attrdict.update(getAllAttributeNames(klass))
# Also get attributes from any and all parent classes.
try:
bases = object.__bases__
except:
except: # Must catch all because object might have __getattr__.
pass
else:
if isinstance(bases, type(())):
if isinstance(bases, types.TupleType):
for base in bases:
if type(base) is types.TypeType:
# Break a circular reference. Happens in Python 2.2.
pass
else:
attributes += getAllAttributeNames(base)
return attributes
attrdict.update(getAllAttributeNames(base))
return attrdict
def getCallTip(command='', locals=None):
"""Return call tip text for a command.
"""For a command, return a tuple of object name, argspec, tip text.
The call tip information will be based on the locals namespace."""
calltip = ('', '', '')
calltip = ('', '', '') # object name, argspec, tip text.
# Get the proper chunk of code from the command.
root = getRoot(command, terminator='(')
try:
object = eval(root, locals)
if locals is not None:
object = eval(root, locals)
else:
object = eval(root)
except:
return calltip
name = ''
dropSelf = 1
# Switch to the object that has the information we need.
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()
# complains that the object isn't a Python function.
object = object.im_func
elif inspect.isclass(object):
# Get the __init__ method function for the class.
constructor = getConstructor(object)
if constructor is not None:
object = constructor
elif callable(object):
# Get the __call__ method instead.
try:
object = object.__call__.im_func
except:
dropSelf = 0
else:
dropSelf = 0
if hasattr(object, '__name__'):
object, dropSelf = getBaseObject(object)
try:
name = object.__name__
except AttributeError:
pass
tip1 = ''
argspec = ''
if inspect.isbuiltin(object):
@@ -134,8 +145,8 @@ def getCallTip(command='', locals=None):
# tip1 is a string like: "getCallTip(command='', locals=None)"
argspec = apply(inspect.formatargspec, inspect.getargspec(object))
if dropSelf:
# The first parameter to a method is a reference to the
# instance, usually coded as "self", and is passed
# The first parameter to a method is a reference to an
# instance, usually coded as "self", and is usually passed
# automatically by Python and therefore we want to drop it.
temp = argspec.split(',')
if len(temp) == 1: # No other arguments.
@@ -158,6 +169,89 @@ def getCallTip(command='', locals=None):
calltip = (name, argspec[1:-1], tip.strip())
return calltip
def getRoot(command, terminator=None):
"""Return the rightmost root portion of an arbitrary Python command.
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
def rtrimTerminus(command, terminator=None):
"""Return command minus the final terminator and anything that follows."""
if terminator:
pieces = command.split(terminator)
if len(pieces) > 1:
command = terminator.join(pieces[:-1])
return command
def getBaseObject(object):
"""Return base object and dropSelf indicator for an object."""
if inspect.isbuiltin(object):
# Builtin functions don't have an argspec that we can get.
dropSelf = 0
elif inspect.ismethod(object):
# Get the function from the object otherwise inspect.getargspec()
# complains that the object isn't a Python function.
try:
if object.im_self is None:
# This is an unbound method so we do not drop self from the
# argspec, since an instance must be passed as the first arg.
dropSelf = 0
else:
dropSelf = 1
object = object.im_func
except AttributeError:
dropSelf = 0
elif inspect.isclass(object):
# Get the __init__ method function for the class.
constructor = getConstructor(object)
if constructor is not None:
object = constructor
dropSelf = 1
else:
dropSelf = 0
elif callable(object):
# Get the __call__ method instead.
try:
object = object.__call__.im_func
dropSelf = 1
except AttributeError:
dropSelf = 0
else:
dropSelf = 0
return object, dropSelf
def getConstructor(object):
"""Return constructor for class object, or None if there isn't one."""
try:
@@ -169,40 +263,5 @@ def getConstructor(object):
return constructor
return None
def getRoot(command, terminator=None):
"""Return the rightmost root portion of an arbitrary Python command.
The command would normally terminate with a "(" or ".". Anything after
the terminator will be dropped, allowing you to get back to the root.
Return only the root portion that can be eval()'d without side effects.
"""
root = ''
validChars = "._" + string.uppercase + string.lowercase + string.digits
if terminator:
# Drop the final terminator and anything that follows.
pieces = command.split(terminator)
if len(pieces) > 1:
command = terminator.join(pieces[:-1])
if len(command) == 0:
root = ''
elif 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 ("'", '"'):
# We're 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

View File

@@ -2,7 +2,7 @@
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__version__ = "$Revision$"[11:-2]
__revision__ = "$Revision$"[11:-2]
class PseudoKeyword:
"""A callable class that calls a method passed as a parameter.
@@ -27,8 +27,10 @@ class PseudoKeyword:
def __init__(self, method):
"""Create a callable object that executes method when called."""
# XXX Should probably check that this is a callable object.
self.method = method
if callable(method):
self.method = method
else:
raise ValueError, 'method must be callable'
def __call__(self, *args, **kwds):
self.method(*args, **kwds)
@@ -63,7 +65,10 @@ class PseudoFile:
class PseudoFileIn(PseudoFile):
def __init__(self, readline):
self.readline = readline
if callable(readline):
self.readline = readline
else:
raise ValueError, 'readline must be callable'
def isatty(self):
return 1
@@ -72,7 +77,10 @@ class PseudoFileIn(PseudoFile):
class PseudoFileOut(PseudoFile):
def __init__(self, write):
self.write = write
if callable(write):
self.write = write
else:
raise ValueError, 'write must be callable'
def isatty(self):
return 1
@@ -81,10 +89,13 @@ class PseudoFileOut(PseudoFile):
class PseudoFileErr(PseudoFile):
def __init__(self, write):
self.write = write
if callable(write):
self.write = write
else:
raise ValueError, 'write must be callable'
def isatty(self):
return 1

View File

@@ -6,7 +6,7 @@ Sponsored by Orbtech - Your Source For Python Development Services"""
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__version__ = "$Revision$"[11:-2]
__revision__ = "$Revision$"[11:-2]
from wxPython.wx import *
from wxPython.stc import *
@@ -52,7 +52,7 @@ class ShellFacade:
still accessible, even though only some are visible to the user."""
name = 'PyCrust Shell Interface'
revision = __version__
revision = __revision__
def __init__(self, other):
"""Create a ShellFacade instance."""
@@ -128,7 +128,7 @@ class Shell(wxStyledTextCtrl):
"""PyCrust Shell based on wxStyledTextCtrl."""
name = 'PyCrust Shell'
revision = __version__
revision = __revision__
def __init__(self, parent, id=-1, pos=wxDefaultPosition, \
size=wxDefaultSize, style=wxCLIP_CHILDREN, introText='', \
@@ -231,18 +231,9 @@ class Shell(wxStyledTextCtrl):
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.
This simply sets "close", "exit" and "quit" to a helpful string.
"""
## POB: This is having some weird side-effects so I'm taking it out.
## import __builtin__
## from pseudo import PseudoKeyword
## __builtin__.close = __builtin__.exit = __builtin__.quit = \
## PseudoKeyword(self.quit)
import __builtin__
from pseudo import PseudoKeyword
__builtin__.close = __builtin__.exit = __builtin__.quit = \
'Click on the close button to leave the application.'
@@ -322,6 +313,7 @@ class Shell(wxStyledTextCtrl):
if self.AutoCompActive(): self.AutoCompCancel()
# Get the command between the prompt and the cursor.
# Add the '(' to the end of the command.
self.ReplaceSelection('')
command = self.GetTextRange(stoppos, currpos) + '('
self.write('(')
if self.autoCallTip: self.autoCallTipShow(command)
@@ -939,21 +931,17 @@ class ShellMenu:
m = self.autocompMenu = wxMenu()
m.Append(ID_AUTOCOMP_SHOW, 'Show Auto Completion', \
'Show auto completion during dot syntax', \
kind=wxITEM_CHECK)
'Show auto completion during dot syntax', 1)
m.Append(ID_AUTOCOMP_INCLUDE_MAGIC, 'Include Magic Attributes', \
'Include attributes visible to __getattr__ and __setattr__', \
kind=wxITEM_CHECK)
'Include attributes visible to __getattr__ and __setattr__', 1)
m.Append(ID_AUTOCOMP_INCLUDE_SINGLE, 'Include Single Underscores', \
'Include attibutes prefixed by a single underscore', \
kind=wxITEM_CHECK)
'Include attibutes prefixed by a single underscore', 1)
m.Append(ID_AUTOCOMP_INCLUDE_DOUBLE, 'Include Double Underscores', \
'Include attibutes prefixed by a double underscore', \
kind=wxITEM_CHECK)
'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', kind=wxITEM_CHECK)
'Show call tips with argument specifications', 1)
m = self.optionsMenu = wxMenu()
m.AppendMenu(ID_AUTOCOMP, '&Auto Completion', self.autocompMenu, \
@@ -1091,7 +1079,7 @@ class ShellFrame(wxFrame, ShellMenu):
"""Frame containing the PyCrust shell component."""
name = 'PyCrust Shell Frame'
revision = __version__
revision = __revision__
def __init__(self, parent=None, id=-1, title='PyShell', \
pos=wxDefaultPosition, size=wxDefaultSize, \
@@ -1100,7 +1088,7 @@ class ShellFrame(wxFrame, ShellMenu):
"""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 Development Services'
intro += '\nSponsored by Orbtech - Specializing in Python Application Development'
self.CreateStatusBar()
self.SetStatusText(intro.replace('\n', ', '))
if wxPlatform == '__WXMSW__':

View File

@@ -4,7 +4,7 @@ interpreter, each have a revision property based on the CVS Revision."""
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id$"
__version__ = "$Revision$"[11:-2]
__revision__ = "$Revision$"[11:-2]
VERSION = '0.7.1'
VERSION = '0.7.2'