From d7b6ed268e22dfa5d04d5c21b7e33bdf3248d8d1 Mon Sep 17 00:00:00 2001 From: "Steven M. Gava" Date: Mon, 25 Jun 2001 07:23:57 +0000 Subject: [PATCH] Initial revision --- Lib/idlelib/RemoteInterp.py | 342 ++++++++++++++++++++++++++++++++++++ Lib/idlelib/idle | 12 ++ Lib/idlelib/setup.py | 81 +++++++++ 3 files changed, 435 insertions(+) create mode 100644 Lib/idlelib/RemoteInterp.py create mode 100755 Lib/idlelib/idle create mode 100644 Lib/idlelib/setup.py diff --git a/Lib/idlelib/RemoteInterp.py b/Lib/idlelib/RemoteInterp.py new file mode 100644 index 00000000000..724997c0103 --- /dev/null +++ b/Lib/idlelib/RemoteInterp.py @@ -0,0 +1,342 @@ +import select +import socket +import struct +import sys +import types + +VERBOSE = None + +class SocketProtocol: + """A simple protocol for sending strings across a socket""" + BUF_SIZE = 8192 + + def __init__(self, sock): + self.sock = sock + self._buffer = '' + self._closed = 0 + + def close(self): + self._closed = 1 + self.sock.close() + + def send(self, buf): + """Encode buf and write it on the socket""" + if VERBOSE: + VERBOSE.write('send %d:%s\n' % (len(buf), `buf`)) + self.sock.send('%d:%s' % (len(buf), buf)) + + def receive(self, timeout=0): + """Get next complete string from socket or return None + + Raise EOFError on EOF + """ + buf = self._read_from_buffer() + if buf is not None: + return buf + recvbuf = self._read_from_socket(timeout) + if recvbuf is None: + return None + if recvbuf == '' and self._buffer == '': + raise EOFError + if VERBOSE: + VERBOSE.write('recv %s\n' % `recvbuf`) + self._buffer = self._buffer + recvbuf + r = self._read_from_buffer() + return r + + def _read_from_socket(self, timeout): + """Does not block""" + if self._closed: + return '' + if timeout is not None: + r, w, x = select.select([self.sock], [], [], timeout) + if timeout is None or r: + return self.sock.recv(self.BUF_SIZE) + else: + return None + + def _read_from_buffer(self): + buf = self._buffer + i = buf.find(':') + if i == -1: + return None + buflen = int(buf[:i]) + enclen = i + 1 + buflen + if len(buf) >= enclen: + s = buf[i+1:enclen] + self._buffer = buf[enclen:] + return s + else: + self._buffer = buf + return None + +# helpers for registerHandler method below + +def get_methods(obj): + methods = [] + for name in dir(obj): + attr = getattr(obj, name) + if callable(attr): + methods.append(name) + if type(obj) == types.InstanceType: + methods = methods + get_methods(obj.__class__) + if type(obj) == types.ClassType: + for super in obj.__bases__: + methods = methods + get_methods(super) + return methods + +class CommandProtocol: + def __init__(self, sockp): + self.sockp = sockp + self.seqno = 0 + self.handlers = {} + + def close(self): + self.sockp.close() + self.handlers.clear() + + def registerHandler(self, handler): + """A Handler is an object with handle_XXX methods""" + for methname in get_methods(handler): + if methname[:7] == "handle_": + name = methname[7:] + self.handlers[name] = getattr(handler, methname) + + def send(self, cmd, arg='', seqno=None): + if arg: + msg = "%s %s" % (cmd, arg) + else: + msg = cmd + if seqno is None: + seqno = self.get_seqno() + msgbuf = self.encode_seqno(seqno) + msg + self.sockp.send(msgbuf) + if cmd == "reply": + return + reply = self.sockp.receive(timeout=None) + r_cmd, r_arg, r_seqno = self._decode_msg(reply) + assert r_seqno == seqno and r_cmd == "reply", "bad reply" + return r_arg + + def _decode_msg(self, msg): + seqno = self.decode_seqno(msg[:self.SEQNO_ENC_LEN]) + msg = msg[self.SEQNO_ENC_LEN:] + parts = msg.split(" ", 2) + if len(parts) == 1: + cmd = msg + arg = '' + else: + cmd = parts[0] + arg = parts[1] + return cmd, arg, seqno + + def dispatch(self): + msg = self.sockp.receive() + if msg is None: + return + cmd, arg, seqno = self._decode_msg(msg) + self._current_reply = seqno + h = self.handlers.get(cmd, self.default_handler) + try: + r = h(arg) + except TypeError, msg: + raise TypeError, "handle_%s: %s" % (cmd, msg) + if self._current_reply is None: + if r is not None: + sys.stderr.write("ignoring %s return value type %s\n" % \ + (cmd, type(r).__name__)) + return + if r is None: + r = '' + if type(r) != types.StringType: + raise ValueError, "invalid return type for %s" % cmd + self.send("reply", r, seqno=seqno) + + def reply(self, arg=''): + """Send a reply immediately + + otherwise reply will be sent when handler returns + """ + self.send("reply", arg, self._current_reply) + self._current_reply = None + + def default_handler(self, arg): + sys.stderr.write("WARNING: unhandled message %s\n" % arg) + return '' + + SEQNO_ENC_LEN = 4 + + def get_seqno(self): + seqno = self.seqno + self.seqno = seqno + 1 + return seqno + + def encode_seqno(self, seqno): + return struct.pack("I", seqno) + + def decode_seqno(self, buf): + return struct.unpack("I", buf)[0] + + +class StdioRedirector: + """Redirect sys.std{in,out,err} to a set of file-like objects""" + + def __init__(self, stdin, stdout, stderr): + self.stdin = stdin + self.stdout = stdout + self.stderr = stderr + + def redirect(self): + self.save() + sys.stdin = self.stdin + sys.stdout = self.stdout + sys.stderr = self.stderr + + def save(self): + self._stdin = sys.stdin + self._stdout = sys.stdout + self._stderr = sys.stderr + + def restore(self): + sys.stdin = self._stdin + sys.stdout = self._stdout + sys.stderr = self._stderr + +class IOWrapper: + """Send output from a file-like object across a SocketProtocol + + XXX Should this be more tightly integrated with the CommandProtocol? + """ + + def __init__(self, name, cmdp): + self.name = name + self.cmdp = cmdp + self.buffer = [] + +class InputWrapper(IOWrapper): + def write(self, buf): + # XXX what should this do on Windows? + raise IOError, (9, '[Errno 9] Bad file descriptor') + + def read(self, arg=None): + if arg is not None: + if arg <= 0: + return '' + else: + arg = 0 + return self.cmdp.send(self.name, "read,%s" % arg) + + def readline(self): + return self.cmdp.send(self.name, "readline") + +class OutputWrapper(IOWrapper): + def write(self, buf): + self.cmdp.send(self.name, buf) + + def read(self, arg=None): + return '' + +class RemoteInterp: + def __init__(self, sock): + self._sock = SocketProtocol(sock) + self._cmd = CommandProtocol(self._sock) + self._cmd.registerHandler(self) + + def run(self): + try: + while 1: + self._cmd.dispatch() + except EOFError: + pass + + def handle_execfile(self, arg): + self._cmd.reply() + io = StdioRedirector(InputWrapper("stdin", self._cmd), + OutputWrapper("stdout", self._cmd), + OutputWrapper("stderr", self._cmd)) + io.redirect() + execfile(arg, {'__name__':'__main__'}) + io.restore() + self._cmd.send("terminated") + + def handle_quit(self, arg): + self._cmd.reply() + self._cmd.close() + +def startRemoteInterp(id): + import os + # UNIX domain sockets are simpler for starters + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.bind("/var/tmp/ri.%s" % id) + try: + sock.listen(1) + cli, addr = sock.accept() + rinterp = RemoteInterp(cli) + rinterp.run() + finally: + os.unlink("/var/tmp/ri.%s" % id) + +class RIClient: + """Client of the remote interpreter""" + def __init__(self, sock): + self._sock = SocketProtocol(sock) + self._cmd = CommandProtocol(self._sock) + self._cmd.registerHandler(self) + + def execfile(self, file): + self._cmd.send("execfile", file) + + def run(self): + try: + while 1: + self._cmd.dispatch() + except EOFError: + pass + + def handle_stdout(self, buf): + sys.stdout.write(buf) +## sys.stdout.flush() + + def handle_stderr(self, buf): + sys.stderr.write(buf) + + def handle_stdin(self, arg): + if arg == "readline": + return sys.stdin.readline() + i = arg.find(",") + 1 + bytes = int(arg[i:]) + if bytes == 0: + return sys.stdin.read() + else: + return sys.stdin.read(bytes) + + def handle_terminated(self, arg): + self._cmd.reply() + self._cmd.send("quit") + self._cmd.close() + +def riExec(id, file): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect("/var/tmp/ri.%s" % id) + cli = RIClient(sock) + cli.execfile(file) + cli.run() + +if __name__ == "__main__": + import sys + import getopt + + SERVER = 1 + opts, args = getopt.getopt(sys.argv[1:], 'cv') + for o, v in opts: + if o == '-c': + SERVER = 0 + elif o == '-v': + VERBOSE = sys.stderr + id = args[0] + + if SERVER: + startRemoteInterp(id) + else: + file = args[1] + riExec(id, file) diff --git a/Lib/idlelib/idle b/Lib/idlelib/idle new file mode 100755 index 00000000000..2a854978a5d --- /dev/null +++ b/Lib/idlelib/idle @@ -0,0 +1,12 @@ +#! /usr/bin/env python + +import os +import sys +from idlelib import IdleConf + +idle_dir = os.path.dirname(IdleConf.__file__) +IdleConf.load(idle_dir) + +# defer importing Pyshell until IdleConf is loaded +from idlelib import PyShell +PyShell.main() diff --git a/Lib/idlelib/setup.py b/Lib/idlelib/setup.py new file mode 100644 index 00000000000..73f3a810c18 --- /dev/null +++ b/Lib/idlelib/setup.py @@ -0,0 +1,81 @@ +import os,glob +from distutils.core import setup +from distutils.command.build_py import build_py +from distutils.command.install_lib import install_lib +import idlever + +# name of idle package +idlelib = "idlelib" + +# the normal build_py would not incorporate the .txt files +txt_files = ['config-unix.txt','config-win.txt','config.txt'] +Icons = glob.glob1("Icons","*.gif") +class idle_build_py(build_py): + def get_plain_outfile(self, build_dir, package, file): + # like get_module_outfile, but does not append .py + outfile_path = [build_dir] + list(package) + [file] + return apply(os.path.join, outfile_path) + + def run(self): + # Copies all .py files, then also copies the txt and gif files + build_py.run(self) + assert self.packages == [idlelib] + for name in txt_files: + outfile = self.get_plain_outfile(self.build_lib, [idlelib], name) + dir = os.path.dirname(outfile) + self.mkpath(dir) + self.copy_file(name, outfile, preserve_mode = 0) + for name in Icons: + outfile = self.get_plain_outfile(self.build_lib, + [idlelib,"Icons"], name) + dir = os.path.dirname(outfile) + self.mkpath(dir) + self.copy_file(os.path.join("Icons",name), + outfile, preserve_mode = 0) + + def get_source_files(self): + # returns the .py files, the .txt files, and the icons + icons = [os.path.join("Icons",name) for name in Icons] + return build_py.get_source_files(self)+txt_files+icons + + def get_outputs(self, include_bytecode=1): + # returns the built files + outputs = build_py.get_outputs(self, include_bytecode) + if not include_bytecode: + return outputs + for name in txt_files: + filename = self.get_plain_outfile(self.build_lib, + [idlelib], name) + outputs.append(filename) + for name in Icons: + filename = self.get_plain_outfile(self.build_lib, + [idlelib,"Icons"], name) + outputs.append(filename) + return outputs + +# Arghhh. install_lib thinks that all files returned from build_py's +# get_outputs are bytecode files +class idle_install_lib(install_lib): + def _bytecode_filenames(self, files): + files = [n for n in files if n.endswith('.py')] + return install_lib._bytecode_filenames(self,files) + + +setup(name="IDLE", + version = idlever.IDLE_VERSION, + description = "IDLE, the Python IDE", + author = "Guido van Rossum", + author_email = "guido@python.org", + #url = + long_description = +"""IDLE is a Tkinter based IDE for Python. It is written in 100% pure +Python and works both on Windows and Unix. It features a multi-window +text editor with multiple undo, Python colorizing, and many other things, +as well as a Python shell window and a debugger.""", + + cmdclass = {'build_py':idle_build_py, + 'install_lib':idle_install_lib}, + package_dir = {idlelib:'.'}, + packages = [idlelib], + scripts = ['idle'] + )