[Twisted-Python] twisted-compliant interactive interpreter

Hi, I felt a need for an interactive interpreter that continued to be interactive when the reactor ran. Here is my adaptation of the console class in codes.py. Please comment. Yours, Antony Kummel ____________________________________________________ Start your day with Yahoo! - make it your home page http://www.yahoo.com/r/hs """ Emulation of Python's interactive interpreter that lives in harmony with Twisted's reactor. """ from twisted.python import threadable threadable.init() from twisted.internet import reactor, defer, threads from code import InteractiveConsole as _InteractiveConsole import sys class TwistedInteractiveConsole(_InteractiveConsole): """Closely emulate the behavior of the interactive Python interpreter, but run from within Twisted's reactor! """ def __init__(self, locals=None, filename="<console>"): """Constructor. The optional locals argument will be passed to the InteractiveInterpreter base class. The optional filename argument should specify the (file)name of the input stream; it will show up in tracebacks. """ _InteractiveConsole.__init__(self, locals, filename) def interact(self, banner=None, stopReactor=False): """Closely emulate the interactive Python console. The optional banner argument specify the banner to print before the first interaction; by default it prints a banner similar to the one printed by the real Python interpreter, followed by the current class name in parentheses (so as not to confuse this with the real interpreter -- since it's so close!). The optional stopReactor argument indicates whether to stop the Twisted reactor when the user exits the interpreter (^Z). """ self.stopReactor = stopReactor try: sys.ps1 except AttributeError: sys.ps1 = ">>> " try: sys.ps2 except AttributeError: sys.ps2 = "... " cprt = 'Type "help", "copyright", "credits" or "license" for more information.' if banner is None: self.write("Python %s on %s\n%s\n(%s)\n" % (sys.version, sys.platform, cprt, self.__class__.__name__)) else: self.write("%s\n" % str(banner)) reactor.callLater(0, self._startInteraction) def _startInteraction(self, more=False): if more: prompt = sys.ps2 else: prompt = sys.ps1 d = defer.maybeDeferred(self.raw_input, prompt) d.addCallbacks(self._processInput, self._processInputError) def _processInput(self, line): more = self.push(line) reactor.callLater(0, self._startInteraction, more) def _processInputError(self, failure): failure.trap(EOFError) self.write("\n") if bool(self.stopReactor): reactor.stop() def raw_input(self, prompt=""): """Write a prompt and read a line. The returned line does not include the trailing newline. When the user enters the EOF key sequence, EOFError is raised. The base implementation uses the built-in function raw_input(); a subclass may replace this with a different implementation. """ return threads.deferToThread(raw_input, prompt) def interact(banner=None, readfunc=None, local=None, stopReactor=False): """Closely emulate the interactive Python interpreter. This is a backwards compatible interface to the InteractiveConsole class. When readfunc is not specified, it attempts to import the readline module to enable GNU readline if it is available. Arguments (all optional, all default to None): banner -- passed to InteractiveConsole.interact() readfunc -- if not None, replaces InteractiveConsole.raw_input() local -- passed to InteractiveInterpreter.__init__() stopReactor -- passed to InteractiveConsole.interact() """ console = TwistedInteractiveConsole(local) if readfunc is not None: console.raw_input = readfunc else: try: import readline except ImportError: pass console.interact(banner, stopReactor) if __name__ == '__main__': reactor.callWhenRunning(interact, stopReactor=True) reactor.run()

On Tue, 2005-08-09 at 06:08 -0700, Antony Kummel wrote:
I felt a need for an interactive interpreter that continued to be interactive when the reactor ran. Here is my adaptation of the console class in codes.py. Please comment.
twisted.manhole provides one, and there's probably some way to hook it up to the console using twisted.internet.stdio.

On Tue, 09 Aug 2005 11:07:37 -0400, Itamar Shtull-Trauring <itamar@itamarst.org> wrote:
On Tue, 2005-08-09 at 06:08 -0700, Antony Kummel wrote:
I felt a need for an interactive interpreter that continued to be interactive when the reactor ran. Here is my adaptation of the console class in codes.py. Please comment.
twisted.manhole provides one, and there's probably some way to hook it up to the console using twisted.internet.stdio.
Except it's twisted.conch.manhole. twisted.conch.stdio demonstrates how one much hook it up to stdio. Note in particular the absence of raw_input(), which will segfault as often as not, when invoked in a non-main thread. Jp

Jp Calderone <exarkun@divmod.com> writes:
Note in particular the absence of raw_input(), which will segfault as often as not, when invoked in a non-main thread.
I think this is fixed in Python CVS (and somewhat in 2.4.1)... Cheers, mwh -- We did requirements and task analysis, iterative design, and user testing. You'd almost think programming languages were an interface between people and computers. -- Steven Pemberton (one of the designers of Python's direct ancestor ABC)

Using twisted.manhole sounds like a good idea, but twisted.internet.stdio doesn't support Windows, which I am using. My version with deferToThread(raw_input) works fine in windows. Could it be that there are no problems doing it under windows? Anyway, to be compatible I tried a variety of methods to implement twisted.internet.stdio for windows (attached is a msvcrt version which is probably worthless). I wonder, if raw_inputting in a non-main thread works for windows, if it can be used to do the windows version of twisted.internet.stdio. If it works, how difficult would it be to make a worthwhile manhole client for windows? Thanks, Antony Kummel --- Jp Calderone <exarkun@divmod.com> wrote:
On Tue, 09 Aug 2005 11:07:37 -0400, Itamar Shtull-Trauring <itamar@itamarst.org> wrote:
On Tue, 2005-08-09 at 06:08 -0700, Antony Kummel wrote:
I felt a need for an interactive interpreter that continued to be interactive when the reactor ran. Here is my adaptation of the console class in codes.py. Please comment.
twisted.manhole provides one, and there's probably some way to hook it up to the console using twisted.internet.stdio.
Except it's twisted.conch.manhole. twisted.conch.stdio demonstrates how one much hook it up to stdio.
Note in particular the absence of raw_input(), which will segfault as often as not, when invoked in a non-main thread.
Jp
_______________________________________________ Twisted-Python mailing list Twisted-Python@twistedmatrix.com
http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
__________________________________________________ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com """Standard input/out/err support for Windows! 10/8/05 """ # system imports import msvcrt from zope.interface import implements # Twisted imports from twisted.python import failure # Sibling Imports from twisted.internet import reactor, task, interfaces _stdio_in_use = 0 class StandardIOTransport: implements(interfaces.ITransport) closed = 0 disconnecting = 0 producer = None streamingProducer = 0 def write(self, data): try: for x in data: msvcrt.putch(x) except: self.handleException() # self._checkProducer() def _checkProducer(self): # Cheating; this is called at "idle" times to allow producers to be # found and dealt with if self.producer: self.producer.resumeProducing() def registerProducer(self, producer, streaming): """From abstract.FileDescriptor """ self.producer = producer self.streamingProducer = streaming if not streaming: producer.resumeProducing() def unregisterProducer(self): self.producer = None def stopConsuming(self): self.unregisterProducer() self.loseConnection() def writeSequence(self, iovec): self.write("".join(iovec)) def loseConnection(self): self.closed = 1 def getPeer(self): return 'file', 'file' def getHost(self): return 'file' def handleException(self): pass def resumeProducing(self): # Never sends data anyways pass def pauseProducing(self): # Never sends data anyways pass def stopProducing(self): self.loseConnection() class StandardIO(StandardIOTransport): """I can connect Standard IO to a twisted.protocol I act as a selectable for sys.stdin, and provide a write method that writes to stdout. """ def __init__(self, _protocol): """Create me with a protocol. This will fail if a StandardIO has already been instantiated. """ global _stdio_in_use if _stdio_in_use: raise RuntimeError, "Standard IO already in use." _stdio_in_use = 1 self.protocol = _protocol self.protocol.makeConnection(self) self.reader = task.LoopingCall(self._tryToRead) self.reader.start(0.05) def _tryToRead(self): if msvcrt.kbhit(): c = msvcrt.getch() if c == '\x1a': # ^Z self.protocol.connectionLost(failure.Failure(EOFError())) else: if c == '\r': c = '\r\n' [msvcrt.putch(x) for x in list(c)] # echo self.protocol.dataReceived(c) def loseConnection(self): self.closed = 1 self.reader.stop()

Hi, Here is a version of PyCrust I hooked up that uses Manhole. I'm trying to figure out if there's a way to easily mix together some free tools to make Twisted development on Windows easier. Comments wanted. Antony Kummel __________________________________________________ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com """ PyCrust is a python shell and namespace browser application. tCrust is a version of PyCrust that works with Twisted. """ # The next two lines, and the other code below that makes use of # ``__main__`` and ``original``, serve the purpose of cleaning up the # main namespace to look as much as possible like the regular Python # shell environment. import __main__ original = __main__.__dict__.keys() __original_author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" import wx class App(wx.App): """PyCrust standalone application.""" def OnInit(self): import wx from wx import py wx.InitAllImageHandlers() # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # make some adaptations self.frame = py.crust.CrustFrame(title="Twisted PyCrust!", InterpClass=self.getInterpClass()) def myOnClose(self, event): from twisted.internet import reactor reactor.addSystemEventTrigger('after', 'shutdown', self._OnClose, (None,)) reactor.stop() self.frame._OnClose = self.frame.OnClose self.frame.OnClose = myOnClose # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! self.frame.SetSize((800, 600)) self.frame.Show() self.SetTopWindow(self.frame) return True def getInterpClass(self): import sys from wx import py from twisted.conch.manhole import ManholeInterpreter class ManholeCrustInterpreter(py.interpreter.Interpreter): """A version of the PyCrust interpreter that uses the manhole display hook""" def __init__(self, locals=None, rawin=None, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr): py.interpreter.Interpreter.__init__(self, locals, rawin, stdin, stdout, stderr) self.manhole = ManholeInterpreter(self, locals) def addOutput(self, data, async): self.write(data) def runcode(self, *a, **kw): orighook, sys.displayhook = sys.displayhook, self.manhole.displayhook try: py.interpreter.Interpreter.runcode(self, *a, **kw) finally: sys.displayhook = orighook return ManholeCrustInterpreter ''' The main() function needs to handle being imported, such as with the pycrust script that wxPython installs: #!/usr/bin/env python from wx.py.PyCrust import main main() ''' def main(): """The main function for the PyCrust program.""" # Cleanup the main namespace, leaving the App class. import __main__ md = __main__.__dict__ keepers = original keepers.append('App') for key in md.keys(): if key not in keepers: del md[key] # Create an application instance. app = App(0) # Mimic the contents of the standard Python shell's sys.path. import sys if sys.path[0]: sys.path[0] = '' # Add the application object to the sys module's namespace. # This allows a shell user to do: # >>> import sys # >>> sys.app.whatever sys.app = app del sys # Cleanup the main namespace some more. if md.has_key('App') and md['App'] is App: del md['App'] if md.has_key('__main__') and md['__main__'] is __main__: del md['__main__'] # Start the wxPython event loop. # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # this is to integrate Twisted and wxPython from twisted.internet import threadedselectreactor threadedselectreactor.install() from twisted.internet import reactor import wx reactor.interleave(wx.CallAfter) # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! app.MainLoop() if __name__ == '__main__': main()
participants (4)
-
Antony Kummel
-
Itamar Shtull-Trauring
-
Jp Calderone
-
Michael Hudson