From 5e08014381335fced8d75bcbc84e5f61f734c09e Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 16 Aug 2001 17:48:52 +0000 Subject: [PATCH] new version of PyCrust git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@11395 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- wxPython/wxPython/lib/PyCrust/.cvsignore | 3 + wxPython/wxPython/lib/PyCrust/PyCrust.ico | Bin 0 -> 766 bytes wxPython/wxPython/lib/PyCrust/PyCrust.py | 118 +++++++-- wxPython/wxPython/lib/PyCrust/PyCrustShell.py | 180 -------------- wxPython/wxPython/lib/PyCrust/README.txt | 43 ++++ .../{PyCrustInterp.py => interpreter.py} | 10 +- wxPython/wxPython/lib/PyCrust/introspect.py | 66 +++-- .../PyCrust/{PyCrustEditor.py => shell.py} | 235 ++++++++++++++---- .../PyCrust/{PyCrustVersion.py => version.py} | 4 +- 9 files changed, 371 insertions(+), 288 deletions(-) create mode 100644 wxPython/wxPython/lib/PyCrust/.cvsignore create mode 100644 wxPython/wxPython/lib/PyCrust/PyCrust.ico delete mode 100644 wxPython/wxPython/lib/PyCrust/PyCrustShell.py create mode 100644 wxPython/wxPython/lib/PyCrust/README.txt rename wxPython/wxPython/lib/PyCrust/{PyCrustInterp.py => interpreter.py} (91%) rename wxPython/wxPython/lib/PyCrust/{PyCrustEditor.py => shell.py} (66%) rename wxPython/wxPython/lib/PyCrust/{PyCrustVersion.py => version.py} (75%) diff --git a/wxPython/wxPython/lib/PyCrust/.cvsignore b/wxPython/wxPython/lib/PyCrust/.cvsignore new file mode 100644 index 0000000000..9020783b5e --- /dev/null +++ b/wxPython/wxPython/lib/PyCrust/.cvsignore @@ -0,0 +1,3 @@ +*.pyc +*.BAK +*.$$$ \ No newline at end of file diff --git a/wxPython/wxPython/lib/PyCrust/PyCrust.ico b/wxPython/wxPython/lib/PyCrust/PyCrust.ico new file mode 100644 index 0000000000000000000000000000000000000000..bb29c8edd7e66d24e5acde41c632ebb805b87891 GIT binary patch literal 766 zcmah{Ic~!+5FEt;0>gz}rHxAeBL12A1hx8wdNzED%9JWo7^Y`-NeVsyue8_f>~eJg z9k0GWK=StieA6GPJ@rKG@JUNy?X?7eH$ren%Md9gdKI$W$XOAUmmry!5-Dq8r!>Z? zBAzxzO$%UbpmkU$zHE6l(2OW(RYUe1r&$ z)Fj+QvAFmA=iIxDWqcdyW1sT2rPo)xMz%;3e5eO~rChb~`%^9A_*2Ywb{KLcM0*hm9^!K!#8l_#G{|IAsMso6S1XX#9x FeFHZg6}12W literal 0 HcmV?d00001 diff --git a/wxPython/wxPython/lib/PyCrust/PyCrust.py b/wxPython/wxPython/lib/PyCrust/PyCrust.py index 43423d65d5..84edad7ca0 100644 --- a/wxPython/wxPython/lib/PyCrust/PyCrust.py +++ b/wxPython/wxPython/lib/PyCrust/PyCrust.py @@ -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): diff --git a/wxPython/wxPython/lib/PyCrust/PyCrustShell.py b/wxPython/wxPython/lib/PyCrust/PyCrustShell.py deleted file mode 100644 index 07c232d156..0000000000 --- a/wxPython/wxPython/lib/PyCrust/PyCrustShell.py +++ /dev/null @@ -1,180 +0,0 @@ -""" -""" -__author__ = "Patrick K. O'Brien " -__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() - - diff --git a/wxPython/wxPython/lib/PyCrust/README.txt b/wxPython/wxPython/lib/PyCrust/README.txt new file mode 100644 index 0000000000..73ad07bc2b --- /dev/null +++ b/wxPython/wxPython/lib/PyCrust/README.txt @@ -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$ diff --git a/wxPython/wxPython/lib/PyCrust/PyCrustInterp.py b/wxPython/wxPython/lib/PyCrust/interpreter.py similarity index 91% rename from wxPython/wxPython/lib/PyCrust/PyCrustInterp.py rename to wxPython/wxPython/lib/PyCrust/interpreter.py index dd606c2138..5f12c01f34 100644 --- a/wxPython/wxPython/lib/PyCrust/PyCrustInterp.py +++ b/wxPython/wxPython/lib/PyCrust/interpreter.py @@ -1,3 +1,5 @@ +"""PyCrust Interpreter executes Python commands. +""" __author__ = "Patrick K. O'Brien " __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) diff --git a/wxPython/wxPython/lib/PyCrust/introspect.py b/wxPython/wxPython/lib/PyCrust/introspect.py index c9342c8a0c..8cf6ae8505 100644 --- a/wxPython/wxPython/lib/PyCrust/introspect.py +++ b/wxPython/wxPython/lib/PyCrust/introspect.py @@ -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 diff --git a/wxPython/wxPython/lib/PyCrust/PyCrustEditor.py b/wxPython/wxPython/lib/PyCrust/shell.py similarity index 66% rename from wxPython/wxPython/lib/PyCrust/PyCrustEditor.py rename to wxPython/wxPython/lib/PyCrust/shell.py index 458e0ad5b1..0c71206bd2 100644 --- a/wxPython/wxPython/lib/PyCrust/PyCrustEditor.py +++ b/wxPython/wxPython/lib/PyCrust/shell.py @@ -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 " __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) diff --git a/wxPython/wxPython/lib/PyCrust/PyCrustVersion.py b/wxPython/wxPython/lib/PyCrust/version.py similarity index 75% rename from wxPython/wxPython/lib/PyCrust/PyCrustVersion.py rename to wxPython/wxPython/lib/PyCrust/version.py index de8a0d0d0f..0c48e4021d 100644 --- a/wxPython/wxPython/lib/PyCrust/PyCrustVersion.py +++ b/wxPython/wxPython/lib/PyCrust/version.py @@ -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 " __cvsid__ = "$Id$" __date__ = "July 1, 2001" __version__ = "$Revision$"[11:-2] -version = '0.5' +VERSION = '0.5.2' -- 2.50.0