RemotePyInterpreter.py :  » Development » PyObjC » trunk » pyobjc » pyobjc-core » Examples » NonFunctional » RemotePyInterpreter » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Development » PyObjC 
PyObjC » trunk » pyobjc » pyobjc core » Examples » NonFunctional » RemotePyInterpreter » RemotePyInterpreter.py
import sys
import traceback
import sets
import keyword
import time
from Foundation import *
from AppKit import *
from PyObjCTools import NibClassBuilder,AppHelper

NibClassBuilder.extractClasses("RemotePyInterpreterDocument.nib")

from AsyncPythonInterpreter import *
from ConsoleReactor import *
from netrepr import RemoteObjectReference

def ensure_unicode(s):
    if not isinstance(s, unicode):
        s = unicode(s, 'utf-8', 'replace')
    return s

class RemotePyInterpreterReactor(NibClassBuilder.AutoBaseClass):
    def handleExpectCommand_(self, command):
        print command
        seq = command[0]
        name = command[1]
        args = command[2:]
        netrepr = self.netReprCenter.netrepr
        rval = None
        code = None
        if name == 'RemoteConsole.raw_input':
            prompt = ensure_unicode(args[0])
            def input_received(line):
                self.sendResult_sequence_(line, seq)
            self.delegate.expectCodeInput_withPrompt_(input_received, prompt)
        elif name == 'RemoteConsole.write':
            args = [ensure_unicode(args[0]), u'code']
            self.doCallback_sequence_args_(self.delegate.writeString_forOutput_, seq, args)
        elif name == 'RemoteConsole.displayhook':
            obj = args[0]
            def displayhook_respond(reprobject):
                self.delegate.writeString_forOutput_(ensure_unicode(reprobject) + u'\n', u'code')
            def displayhook_local(obj):
                if obj is not None:
                    displayhook_respond(repr(obj))
            if isinstance(obj, RemoteObjectReference):
                self.deferCallback_sequence_value_(displayhook_respond, seq, 'repr(%s)' % (netrepr(obj),))
            else:
                self.doCallback_sequence_args_(displayhook_local, seq, args)
        elif name.startswith('RemoteFileLike.'):
            method = name[len('RemoteFileLike.'):]
            if method == 'write':
                style, msg = map(ensure_unicode, args)
                args = [msg, style]
                self.doCallback_sequence_args_(self.delegate.writeString_forOutput_, seq, args)

            elif method == 'readline':
                def input_received(line):
                    self.sendResult_sequence_(line, seq)
                self.delegate.expectCodeInput_withPrompt_(input_received, '')

            else:
                self.doCallback_sequence_args_(NSLog, seq, [u'%r does not respond to expect %r' % (self, command,)])
        elif name == 'RemoteConsole.initialize':
            def gotTitle(repr_versioninfo, executable, pid):
                self.delegate.setVersion_executable_pid_(
                    u'.'.join(map(unicode, self.netEval_(repr_versioninfo)[:3])),
                    ensure_unicode(executable),
                    pid,
                )
            self.doCallback_sequence_args_(gotTitle, seq, args)
        #    fh = getattr(sys, args[0])
        #    meth = getattr(fh, name[len('RemoteFileLike.'):])
        #    self.doCallback_sequence_args_(meth, seq, args[1:])
        else:
            self.doCallback_sequence_args_(NSLog, seq, [u'%r does not respond to expect %r' % (self, command,)])
    
    def close(self):
        super(RemotePyInterpreterReactor, self).close()
        self.delegate = None


class PseudoUTF8Input(object):
    softspace = 0
    def __init__(self, readlinemethod):
        self._buffer = u''
        self._readline = readlinemethod

    def read(self, chars=None):
        if chars is None:
            if self._buffer:
                rval = self._buffer
                self._buffer = u''
                if rval.endswith(u'\r'):
                    rval = rval[:-1]+u'\n'
                return rval.encode('utf-8')
            else:
                return self._readline(u'\x04')[:-1].encode('utf-8')
        else:
            while len(self._buffer) < chars:
                self._buffer += self._readline(u'\x04\r')
                if self._buffer.endswith('\x04'):
                    self._buffer = self._buffer[:-1]
                    break
            rval, self._buffer = self._buffer[:chars], self._buffer[chars:]
            return rval.encode('utf-8').replace('\r','\n')

    def readline(self):
        if u'\r' not in self._buffer:
            self._buffer += self._readline(u'\x04\r')
        if self._buffer.endswith('\x04'):
            rval = self._buffer[:-1].encode('utf-8')
        elif self._buffer.endswith('\r'):
            rval = self._buffer[:-1].encode('utf-8')+'\n'
        self._buffer = u''

        return rval


DEBUG_DELEGATE = 0
PASSTHROUGH = (
   'deleteBackward:',
   'complete:',
   'moveRight:',
   'moveLeft:',
)

class RemotePyInterpreterDocument(NibClassBuilder.AutoBaseClass):
    """
    PyInterpreter is a delegate/controller for a NSTextView,
    turning it into a full featured interactive Python interpreter.
    """

    def expectCodeInput_withPrompt_(self, callback, prompt):
        self.writeString_forOutput_(prompt, u'code')
        self.setCharacterIndexForInput_(self.lengthOfTextView())
        self.p_input_callbacks.append(callback)
        self.flushCallbacks()

    def flushCallbacks(self):
        while self.p_input_lines and self.p_input_callbacks:
            self.p_input_callbacks.pop(0)(self.p_input_lines.pop(0))

    def setupTextView(self):
        self.textView.setFont_(self.font())
        self.textView.setContinuousSpellCheckingEnabled_(False)
        self.textView.setRichText_(False)
        self.setCharacterIndexForInput_(0)

    def setVersion_executable_pid_(self, version, executable, pid):
        self.version = version
        self.pid = pid
        self.executable = executable
        self.setFileName_(executable)

    def displayName(self):
        if not hasattr(self, 'version'):
            return u'Starting...'
        return u'Python %s - %s - %s' % (self.version, self.executable, self.pid)
    
    def updateChangeCount_(self, val):
        return

    def windowWillClose_(self, window):
        if self.commandReactor is not None:
            self.commandReactor.close()
            self.commandReactor = None
        if self.interpreter is not None:
            self.interpreter.close()
            self.interpreter = None
    
    def windowNibName(self):
        return u'RemotePyInterpreterDocument'
    
    def isDocumentEdited(self):
        return False
    
    def awakeFromNib(self):
        # XXX - should this be done later?
        self.setFont_(NSFont.userFixedPitchFontOfSize_(10))
        self.p_colors = {
            u'stderr': NSColor.redColor(),
            u'stdout': NSColor.blueColor(),
            u'code': NSColor.blackColor(),
        }
        self.setHistoryLength_(50)
        self.setHistoryView_(0)
        self.setInteracting_(False)
        self.setAutoScroll_(True)
        self.setSingleLineInteraction_(False)
        self.p_history = [u'']
        self.p_input_callbacks = []
        self.p_input_lines = []
        self.setupTextView()
        self.interpreter.connect()

    #
    #  Modal input dialog support
    #

    #def p_nestedRunLoopReaderUntilEOLchars_(self, eolchars):
    #    """
    #    This makes the baby jesus cry.

    #    I want co-routines.
    #    """
    #    app = NSApplication.sharedApplication()
    #    window = self.textView.window()
    #    self.setCharacterIndexForInput_(self.lengthOfTextView())
    #    # change the color.. eh
    #    self.textView.setTypingAttributes_({
    #        NSFontAttributeName: self.font(),
    #        NSForegroundColorAttributeName: self.colorForName_(u'code'),
    #    })
    #    while True:
    #        event = app.nextEventMatchingMask_untilDate_inMode_dequeue_(
    #            NSAnyEventMask,
    #            NSDate.distantFuture(),
    #            NSDefaultRunLoopMode,
    #            True)
    #        if (event.type() == NSKeyDown) and (event.window() is window):
    #            eol = event.characters()
    #            if eol in eolchars:
    #                break
    #        app.sendEvent_(event)
    #    cl = self.currentLine()
    #    if eol == u'\r':
    #        self.writeNewLine()
    #    return cl + eol

    def executeLine_(self, line):
        self.addHistoryLine_(line)
        self.p_input_lines.append(line)
        self.flushCallbacks()
        self.p_history = filter(None, self.p_history)
        self.p_history.append(u'')
        self.setHistoryView_(len(self.p_history) - 1)

    def executeInteractiveLine_(self, line):
        self.setInteracting_(True)
        try:
            self.executeLine_(line)
        finally:
            self.setInteracting_(False)

    def replaceLineWithCode_(self, s):
        idx = self.characterIndexForInput()
        ts = self.textView.textStorage()
        s = self.formatString_forOutput_(s, u'code')
        ts.replaceCharactersInRange_withAttributedString_(
            (idx, len(ts.mutableString())-idx),
            s,
        )

    #
    #  History functions
    #

    def addHistoryLine_(self, line):
        line = line.rstrip(u'\n')
        if self.p_history[-1] == line:
            return False
        if not line:
            return False
        self.p_history.append(line)
        if len(self.p_history) > self.historyLength():
            self.p_history.pop(0)
        return True

    def historyDown_(self, sender):
        if self.p_historyView == (len(self.p_history) - 1):
            return
        self.p_history[self.p_historyView] = self.currentLine()
        self.p_historyView += 1
        self.replaceLineWithCode_(self.p_history[self.p_historyView])
        self.moveToEndOfLine_(self)

    def historyUp_(self, sender):
        if self.p_historyView == 0:
            return
        self.p_history[self.p_historyView] = self.currentLine()
        self.p_historyView -= 1
        self.replaceLineWithCode_(self.p_history[self.p_historyView])
        self.moveToEndOfLine_(self)

    #
    #  Convenience methods to create/write decorated text
    #

    def formatString_forOutput_(self, s, name):
        return NSAttributedString.alloc().initWithString_attributes_(
            s,
            {
                NSFontAttributeName: self.font(),
                NSForegroundColorAttributeName: self.colorForName_(name),
            },
        )

    def writeString_forOutput_(self, s, name):
        s = self.formatString_forOutput_(s, name)
        self.textView.textStorage().appendAttributedString_(s)
        if self.isAutoScroll():
            self.textView.scrollRangeToVisible_((self.lengthOfTextView(), 0))

    def writeNewLine(self):
        self.writeString_forOutput_(u'\n', u'code')

    def colorForName_(self, name):
        return self.p_colors[name]

    def setColor_forName_(self, color, name):
        self.p_colors[name] = color
    
    #
    #  Convenience methods for manipulating the NSTextView
    #

    def currentLine(self):
        return self.textView.textStorage().mutableString()[self.characterIndexForInput():]

    def moveAndScrollToIndex_(self, idx):
        self.textView.scrollRangeToVisible_((idx, 0))
        self.textView.setSelectedRange_((idx, 0))

    def lengthOfTextView(self):
        return len(self.textView.textStorage().mutableString())

    #
    #  NSTextViewDelegate methods
    #

    def textView_completions_forPartialWordRange_indexOfSelectedItem_(self, aTextView, completions, (begin, length), index):
        # XXX 
        # this will probably have to be tricky in order to be asynchronous..
        # either by:
        #     nesting a run loop (bleh)
        #     polling the subprocess (bleh)
        #     returning nothing and calling self.textView.complete_ later
        return None, 0

        if False:
            txt = self.textView.textStorage().mutableString()
            end = begin+length
            while (begin>0) and (txt[begin].isalnum() or txt[begin] in u'._'):
                begin -= 1
            while not txt[begin].isalnum():
                begin += 1
            return self.p_console.recommendCompletionsFor(txt[begin:end])

    def textView_shouldChangeTextInRange_replacementString_(self, aTextView, aRange, newString):
        begin, length = aRange
        lastLocation = self.characterIndexForInput()
        if begin < lastLocation:
            # no editing anywhere but the interactive line
            return False
        newString = newString.replace(u'\r', u'\n')
        if u'\n' in newString:
            if begin != lastLocation:
                # no pasting multiline unless you're at the end
                # of the interactive line
                return False
            # multiline paste support
            #self.clearLine()
            newString = self.currentLine() + newString
            for s in newString.strip().split(u'\n'):
                self.writeString_forOutput_(s + u'\n', u'code')
                self.executeLine_(s)
            return False
        return True

    def textView_willChangeSelectionFromCharacterRange_toCharacterRange_(self, aTextView, fromRange, toRange):
        begin, length = toRange
        if self.singleLineInteraction() and length == 0 and begin < self.characterIndexForInput():
            # no cursor movement off the interactive line
            return fromRange
        else:
            return toRange

    def textView_doCommandBySelector_(self, aTextView, aSelector):
        # deleteForward: is ctrl-d
        if self.isInteracting():
            if aSelector == 'insertNewline:':
                self.writeNewLine()
            return False
        # XXX - this is ugly
        responder = getattr(self, aSelector.replace(':','_'), None)
        if responder is not None:
            responder(aTextView)
            return True
        else:
            if DEBUG_DELEGATE and aSelector not in PASSTHROUGH:
                print aSelector
            return False

    #
    #  doCommandBySelector "posers" on the textView
    #

    def insertTabIgnoringFieldEditor_(self, sender):
        # this isn't terribly necessary, b/c F5 and opt-esc do completion
        # but why not
        sender.complete_(self)

    def moveToBeginningOfLine_(self, sender):
        self.moveAndScrollToIndex_(self.characterIndexForInput())

    def moveToEndOfLine_(self, sender):
        self.moveAndScrollToIndex_(self.lengthOfTextView())

    def moveToBeginningOfLineAndModifySelection_(self, sender):
        begin, length = self.textView.selectedRange()
        pos = self.characterIndexForInput()
        if begin + length > pos:
            self.textView.setSelectedRange_((pos, begin + length - pos))
        else:
            self.moveToBeginningOfLine_(sender)

    def moveToEndOfLineAndModifySelection_(self, sender):
        begin, length = self.textView.selectedRange()
        pos = max(self.characterIndexForInput(), begin)
        self.textView.setSelectedRange_((pos, self.lengthOfTextView()))

    def insertNewline_(self, sender):
        line = self.currentLine()
        self.writeNewLine()
        self.executeInteractiveLine_(line)

    moveToBeginningOfParagraph_ = moveToBeginningOfLine_
    moveToEndOfParagraph_ = moveToEndOfLine_
    insertNewlineIgnoringFieldEditor_ = insertNewline_
    moveDown_ = historyDown_
    moveUp_ = historyUp_

    #
    #  Accessors
    #

    def historyLength(self):
        return self.p_historyLength

    def setHistoryLength_(self, length):
        self.p_historyLength = length

    def font(self):
        return self.p_font

    def setFont_(self, font):
        self.p_font = font

    def isInteracting(self):
        return self.p_interacting

    def setInteracting_(self, v):
        self.p_interacting = v

    def isAutoScroll(self):
        return self.p_autoScroll

    def setAutoScroll_(self, v):
        self.p_autoScroll = v

    def characterIndexForInput(self):
        return self.p_characterIndexForInput

    def setCharacterIndexForInput_(self, idx):
        self.p_characterIndexForInput = idx
        self.moveAndScrollToIndex_(idx)

    def historyView(self):
        return self.p_historyView

    def setHistoryView_(self, v):
        self.p_historyView = v

    def singleLineInteraction(self):
        return self.p_singleLineInteraction

    def setSingleLineInteraction_(self, v):
        self.p_singleLineInteraction = v
    
        

if __name__ == '__main__':
    AppHelper.runEventLoop(installInterrupt=True)
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.