[Idle-dev] CVS: idle RemoteObjectBrowser.py,NONE,1.1 RemoteDebugger.py,NONE,1.1 rpc.py,NONE,1.1 run.py,NONE,1.1 ScriptBinding.py,1.4,1.5 Debugger.py,1.4,1.5 PyShell.py,1.13,1.14

Chui Tey teyc@users.sourceforge.net
Sun, 26 May 2002 06:36:44 -0700


Update of /cvsroot/idlefork/idle
In directory usw-pr-cvs1:/tmp/cvs-serv5459

Modified Files:
	ScriptBinding.py Debugger.py PyShell.py 
Added Files:
	RemoteObjectBrowser.py RemoteDebugger.py rpc.py run.py 
Log Message:
GvR's rpc patch


--- NEW FILE: RemoteObjectBrowser.py ---
import rpc

def remote_object_tree_item(item):
    wrapper = WrappedObjectTreeItem(item)
    oid = id(wrapper)
    rpc.objecttable[oid] = wrapper
    return oid

class WrappedObjectTreeItem:
    # Lives in PYTHON subprocess

    def __init__(self, item):
        self.__item = item

    def __getattr__(self, name):
        value = getattr(self.__item, name)
        return value

    def _GetSubList(self):
        list = self.__item._GetSubList()
        return map(remote_object_tree_item, list)

class StubObjectTreeItem:
    # Lives in IDLE process

    def __init__(self, sockio, oid):
        self.sockio = sockio
        self.oid = oid

    def __getattr__(self, name):
        value = rpc.MethodProxy(self.sockio, self.oid, name)
        return value

    def _GetSubList(self):
        list = self.sockio.remotecall(self.oid, "_GetSubList", (), {})
        return [StubObjectTreeItem(self.sockio, oid) for oid in list]

--- NEW FILE: RemoteDebugger.py ---
"""Support for remote Python debugging.

Some ASCII art to describe the structure:

       IN PYTHON SUBPROCESS          #             IN IDLE PROCESS
                                     #
                                     #        oid='gui_adapter'
                 +----------+        #       +------------+          +-----+
                 | GUIProxy |--remote#call-->| GUIAdapter |--calls-->| GUI |
+-----+--calls-->+----------+        #       +------------+          +-----+
| Idb |                               #                             /
+-----+<-calls--+------------+         #      +----------+<--calls-/
                | IdbAdapter |<--remote#call--| IdbProxy |
                +------------+         #      +----------+
                oid='idb_adapter'      #

The purpose of the Proxy and Adapter classes is to translate certain
arguments and return values that cannot be transported through the RPC
barrier, in particular frame and traceback objects.

"""

import sys
import rpc
import Debugger

# In the PYTHON subprocess

frametable = {}
dicttable = {}
codetable = {}

def wrap_frame(frame):
    fid = id(frame)
    frametable[fid] = frame
    return fid

def wrap_info(info):
    if info is None:
        return None
    else:
        return None # XXX for now

class GUIProxy:

    def __init__(self, conn, oid):
        self.conn = conn
        self.oid = oid

    def interaction(self, message, frame, info=None):
        self.conn.remotecall(self.oid, "interaction",
                             (message, wrap_frame(frame), wrap_info(info)),
                             {})

class IdbAdapter:

    def __init__(self, idb):
        self.idb = idb

    def set_step(self):
        self.idb.set_step()

    def set_quit(self):
        self.idb.set_quit()

    def set_continue(self):
        self.idb.set_continue()

    def set_next(self, fid):
        frame = frametable[fid]
        self.idb.set_next(frame)

    def set_return(self, fid):
        frame = frametable[fid]
        self.idb.set_return(frame)

    def get_stack(self, fid, tbid):
        ##print >>sys.__stderr__, "get_stack(%s, %s)" % (`fid`, `tbid`)
        frame = frametable[fid]
        tb = None # XXX for now
        stack, i = self.idb.get_stack(frame, tb)
        ##print >>sys.__stderr__, "get_stack() ->", stack
        stack = [(wrap_frame(frame), k) for frame, k in stack]
        ##print >>sys.__stderr__, "get_stack() ->", stack
        return stack, i

    def run(self, cmd):
        import __main__
        self.idb.run(cmd, __main__.__dict__)

    def frame_attr(self, fid, name):
        frame = frametable[fid]
        return getattr(frame, name)

    def frame_globals(self, fid):
        frame = frametable[fid]
        dict = frame.f_globals
        did = id(dict)
        dicttable[did] = dict
        return did

    def frame_locals(self, fid):
        frame = frametable[fid]
        dict = frame.f_locals
        did = id(dict)
        dicttable[did] = dict
        return did

    def frame_code(self, fid):
        frame = frametable[fid]
        code = frame.f_code
        cid = id(code)
        codetable[cid] = code
        return cid

    def code_name(self, cid):
        code = codetable[cid]
        return code.co_name

    def code_filename(self, cid):
        code = codetable[cid]
        return code.co_filename

    def dict_keys(self, did):
        dict = dicttable[did]
        return dict.keys()

    def dict_item(self, did, key):
        dict = dicttable[did]
        value = dict[key]
        try:
            # Test for picklability
            import cPickle
            cPickle.dumps(value)
        except:
            value = None
        return value

def start_debugger(conn, gui_oid):
    #
    # launched in the python subprocess
    #
    gui = GUIProxy(conn, gui_oid)
    idb = Debugger.Idb(gui)
    ada = IdbAdapter(idb)
    ada_oid = "idb_adapter"
    conn.register(ada_oid, ada)
    return ada_oid

# In the IDLE process

class FrameProxy:

    def __init__(self, conn, fid):
        self._conn = conn
        self._fid = fid
        self._oid = "idb_adapter"
        self._dictcache = {}

    def __getattr__(self, name):
        if name[:1] == "_":
            raise AttributeError, name
        if name == "f_code":
            return self._get_f_code()
        if name == "f_globals":
            return self._get_f_globals()
        if name == "f_locals":
            return self._get_f_locals()
        return self._conn.remotecall(self._oid, "frame_attr",
                                     (self._fid, name), {})

    def _get_f_code(self):
        cid = self._conn.remotecall(self._oid, "frame_code", (self._fid,), {})
        return CodeProxy(self._conn, self._oid, cid)

    def _get_f_globals(self):
        did = self._conn.remotecall(self._oid, "frame_globals",
                                    (self._fid,), {})
        return self._get_dict_proxy(did)

    def _get_f_locals(self):
        did = self._conn.remotecall(self._oid, "frame_locals",
                                    (self._fid,), {})
        return self._get_dict_proxy(did)

    def _get_dict_proxy(self, did):
        if self._dictcache.has_key(did):
            return self._dictcache[did]
        dp = DictProxy(self._conn, self._oid, did)
        self._dictcache[did] = dp
        return dp

class CodeProxy:

    def __init__(self, conn, oid, cid):
        self._conn = conn
        self._oid = oid
        self._cid = cid

    def __getattr__(self, name):
        if name == "co_name":
            return self._conn.remotecall(self._oid, "code_name",
                                         (self._cid,), {})
        if name == "co_filename":
            return self._conn.remotecall(self._oid, "code_filename",
                                         (self._cid,), {})

class DictProxy:

    def __init__(self, conn, oid, did):
        self._conn = conn
        self._oid = oid
        self._did = did

    def keys(self):
        return self._conn.remotecall(self._oid, "dict_keys", (self._did,), {})

    def __getitem__(self, key):
        return self._conn.remotecall(self._oid, "dict_item",
                                     (self._did, key), {})

    def __getattr__(self, name):
        ##print >>sys.__stderr__, "failed DictProxy.__getattr__:", name
        raise AttributeError, name

class GUIAdaper:

    def __init__(self, conn, gui):
        self.conn = conn
        self.gui = gui

    def interaction(self, message, fid, iid):
        print "interaction(%s, %s, %s)" % (`message`, `fid`, `iid`)
        frame = FrameProxy(self.conn, fid)
        info = None # XXX for now
        self.gui.interaction(message, frame, info)

class IdbProxy:

    def __init__(self, conn, oid):
        self.oid = oid
        self.conn = conn

    def call(self, methodname, *args, **kwargs):
        ##print "call %s %s %s" % (methodname, args, kwargs)
        value = self.conn.remotecall(self.oid, methodname, args, kwargs)
        ##print "return %s" % `value`
        return value

    def run(self, cmd, locals):
        # Ignores locals on purpose!
        self.call("run", cmd)

    def get_stack(self, frame, tb):
        stack, i = self.call("get_stack", frame._fid, None)
        stack = [(FrameProxy(self.conn, fid), k) for fid, k in stack]
        return stack, i

    def set_continue(self):
        self.call("set_continue")

    def set_step(self):
        self.call("set_step")

    def set_next(self, frame):
        self.call("set_next", frame._fid)

    def set_return(self, frame):
        self.call("set_return", frame._fid)

    def set_quit(self):
        self.call("set_quit")

def start_remote_debugger(conn, pyshell):
    #
    # instruct the (remote) subprocess to create
    # a debugger instance, and lets it know that 
    # the local GUIAdapter called "gui_adapter"
    # is waiting notification of debugging events
    #
    ada_oid = "gui_adapter"
    idb_oid = conn.remotecall("exec", "start_debugger", (ada_oid,), {})
    idb = IdbProxy(conn, idb_oid)
    gui = Debugger.Debugger(pyshell, idb)
    ada = GUIAdaper(conn, gui)
    conn.register(ada_oid, ada)
    return gui

--- NEW FILE: rpc.py ---
# ASCII-art documentation
#
#  +---------------------------------+ +----------+
#  | SocketServer.BaseRequestHandler | | SocketIO |
#  +---------------------------------+ +----------+
#                  ^                      ^  ^
#                  |                      |  |
#                  | + -------------------+  |
#                  | |                       |
#  +-------------------------+        +-----------------+
#  | RPCHandler              |        | RPCClient       |
#  |-------------------------|        |-----------------|
#  | register()              |        | remotecall()    |
#  | unregister()            |        | register()      | 
#  |                         |        | unregister()    |
#  |                         |        | get_remote_proxy|
#  +-------------------------+        +-----------------+
#
import sys 
import socket
import select
import SocketServer
import struct
import cPickle as pickle
import threading
import traceback
import copy_reg
import types
import marshal

def unpickle_code(ms):
    co = marshal.loads(ms)
    assert isinstance(co, types.CodeType)
    return co

def pickle_code(co):
    assert isinstance(co, types.CodeType)
    ms = marshal.dumps(co)
    return unpickle_code, (ms,)

def unpickle_function(ms):
    return ms

def pickle_function(fn):
    assert isinstance(fn, type.FunctionType)
    return `fn`
 
copy_reg.pickle(types.CodeType, pickle_code, unpickle_code)
copy_reg.pickle(types.FunctionType, pickle_function, unpickle_function)

BUFSIZE = 8*1024

class RPCServer(SocketServer.TCPServer):

    def __init__(self, addr, handlerclass=None):
        if handlerclass is None:
            handlerclass = RPCHandler
        self.objtable = objecttable 
        SocketServer.TCPServer.__init__(self, addr, handlerclass)

    def verify_request(self, request, client_address):
        host, port = client_address
        if host != "127.0.0.1":
            print "Disallowed host:", host
            return 0
        else:
            return 1

    def register(self, oid, object):
        self.objtable[oid] = object

    def unregister(self, oid):
        try:
            del self.objtable[oid]
        except KeyError:
            pass


objecttable = {}

class SocketIO:

    debugging = 0

    def __init__(self, sock, objtable=None, debugging=None):
        self.mainthread = threading.currentThread()
        if debugging is not None:
            self.debugging = debugging
        self.sock = sock
        if objtable is None:
            objtable = objecttable
        self.objtable = objtable
        self.statelock = threading.Lock()
        self.responses = {}
        self.cvars = {}

    def close(self):
        sock = self.sock
        self.sock = None
        if sock is not None:
            sock.close()

    def debug(self, *args):
        if not self.debugging:
            return
        s = str(threading.currentThread().getName())
        for a in args:
            s = s + " " + str(a)
        s = s + "\n"
        sys.__stderr__.write(s)

    def register(self, oid, object):
        self.objtable[oid] = object

    def unregister(self, oid):
        try:
            del self.objtable[oid]
        except KeyError:
            pass

    def localcall(self, request):
        ##self.debug("localcall:", request) 
        try:
            how, (oid, methodname, args, kwargs) = request
        except TypeError:
            return ("ERROR", "Bad request format")
        assert how == "call"
        if not self.objtable.has_key(oid):
            return ("ERROR", "Unknown object id: %s" % `oid`)
        obj = self.objtable[oid]
        if methodname == "__methods__":
            methods = {}
            _getmethods(obj, methods)
            return ("OK", methods)
        if methodname == "__attributes__":
            attributes = {}
            _getattributes(obj, attributes)
            return ("OK", attributes)
        if not hasattr(obj, methodname):
            return ("ERROR", "Unsupported method name: %s" % `methodname`)
        method = getattr(obj, methodname)
        try:
            ret = method(*args, **kwargs)
            if isinstance(ret, RemoteObject):
                ret = remoteref(ret)
            return ("OK", ret)
        except:
            ##traceback.print_exc(file=sys.__stderr__)
            typ, val, tb = info = sys.exc_info()
            sys.last_type, sys.last_value, sys.last_traceback = info
            if isinstance(typ, type(Exception)):
                # Class exceptions
                mod = typ.__module__
                name = typ.__name__
                if issubclass(typ, Exception):
                    args = val.args
                else:
                    args = (str(val),)
            else:
                # String exceptions
                mod = None
                name = typ
                args = (str(val),)
            tb = traceback.extract_tb(tb)
            return ("EXCEPTION", (mod, name, args, tb))

    def remotecall(self, oid, methodname, args, kwargs):
        seq = self.asynccall(oid, methodname, args, kwargs)
        return self.asyncreturn(seq)

    def asynccall(self, oid, methodname, args, kwargs):
        request = ("call", (oid, methodname, args, kwargs))
        seq = self.putrequest(request)
        return seq

    def asyncreturn(self, seq):
        response = self.getresponse(seq)
        return self.decoderesponse(response)

    def decoderesponse(self, response):
        how, what = response
        if how == "OK":
            return what
        if how == "EXCEPTION":
            mod, name, args, tb = what
            self.traceback = tb
            if mod:
                try:
                    __import__(mod)
                    module = sys.modules[mod]
                except ImportError:
                    pass
                else:
                    try:
                        cls = getattr(module, name)
                    except AttributeError:
                        pass
                    else:
                        raise getattr(__import__(mod), name)(*args)
            else:
                if mod:
                    name = mod + "." + name
                raise name, args
        if how == "ERROR":
            raise RuntimeError, what
        raise SystemError, (how, what)

    def mainloop(self):
        try:
            self.getresponse(None)
        except EOFError:
            pass

    def getresponse(self, myseq):
        response = self._getresponse(myseq)
        if response is not None:
            how, what = response
            if how == "OK":
                response = how, self._proxify(what)
        return response

    def _proxify(self, obj):
        if isinstance(obj, RemoteProxy):
            return RPCProxy(self, obj.oid)
        if isinstance(obj, types.ListType):
            return map(self._proxify, obj)
        # XXX Check for other types -- not currently needed
        return obj

    def _getresponse(self, myseq):
        if threading.currentThread() is self.mainthread:
            # Main thread: does all reading of requests and responses
            while 1:
                response = self.pollresponse(myseq, None)
                if response is not None:
                    return response
        else:
            # Auxiliary thread: wait for notification from main thread
            cvar = threading.Condition(self.statelock)
            self.statelock.acquire()
            self.cvars[myseq] = cvar
            while not self.responses.has_key(myseq):
                cvar.wait()
            response = self.responses[myseq]
            del self.responses[myseq]
            del self.cvars[myseq]
            self.statelock.release()
            return response

    def putrequest(self, request):
        seq = self.newseq()
        self.putmessage((seq, request))
        return seq

    nextseq = 0

    def newseq(self):
        self.nextseq = seq = self.nextseq + 2
        return seq

    def putmessage(self, message):
        try:
            s = pickle.dumps(message)
        except:
            print >>sys.__stderr__, "Cannot pickle:", `message`
            raise
        s = struct.pack("<i", len(s)) + s
        while len(s) > 0:
            n = self.sock.send(s)
            s = s[n:]

    def ioready(self, wait=0.0):
        r, w, x = select.select([self.sock.fileno()], [], [], wait)
        return len(r)

    buffer = ""
    bufneed = 4
    bufstate = 0 # meaning: 0 => reading count; 1 => reading data

    def pollpacket(self, wait=0.0):
        self._stage0()
        if len(self.buffer) < self.bufneed:
            if not self.ioready(wait):
                return None
            try:
                s = self.sock.recv(BUFSIZE)
            except socket.error:
                raise EOFError
            if len(s) == 0:
                raise EOFError
            self.buffer += s
            self._stage0()
        return self._stage1()

    def _stage0(self):
        if self.bufstate == 0 and len(self.buffer) >= 4:
            s = self.buffer[:4]
            self.buffer = self.buffer[4:]
            self.bufneed = struct.unpack("<i", s)[0]
            self.bufstate = 1

    def _stage1(self):
        if self.bufstate == 1 and len(self.buffer) >= self.bufneed:
            packet = self.buffer[:self.bufneed]
            self.buffer = self.buffer[self.bufneed:]
            self.bufneed = 4
            self.bufstate = 0
            return packet

    def pollmessage(self, wait=0.0):
        packet = self.pollpacket(wait)
        if packet is None:
            return None
        try:
            message = pickle.loads(packet)
        except:
            print >>sys.__stderr__, "-----------------------"
            print >>sys.__stderr__, "cannot unpickle packet:", `packet`
            traceback.print_stack(file=sys.__stderr__)
            print >>sys.__stderr__, "-----------------------"
            raise
        return message

    def pollresponse(self, myseq, wait=0.0):
        # Loop while there's no more buffered input or until specific response
        while 1:
            message = self.pollmessage(wait)
            if message is None:
                return None
            wait = 0.0
            seq, resq = message
            if resq[0] == "call":
                response = self.localcall(resq)
                self.putmessage((seq, response))
                continue
            elif seq == myseq:
                return resq
            else:
                self.statelock.acquire()
                self.responses[seq] = resq
                cv = self.cvars.get(seq)
                if cv is not None:
                    cv.notify()
                self.statelock.release()
                continue

class RemoteObject:
    # Token mix-in class
    pass

def remoteref(obj):
    oid = id(obj)
    objecttable[oid] = obj
    return RemoteProxy(oid)

class RemoteProxy:

    def __init__(self, oid):
        self.oid = oid

class RPCHandler(SocketServer.BaseRequestHandler, SocketIO):

    debugging = 0

    def __init__(self, sock, addr, svr):
        svr.current_handler = self ## cgt xxx
        SocketIO.__init__(self, sock)
        SocketServer.BaseRequestHandler.__init__(self, sock, addr, svr)

    def setup(self):
        SocketServer.BaseRequestHandler.setup(self)
        print >>sys.__stderr__, "Connection from", self.client_address

    def finish(self):
        print >>sys.__stderr__, "End connection from", self.client_address
        SocketServer.BaseRequestHandler.finish(self)

    def handle(self):
        self.mainloop()

    def get_remote_proxy(self, oid):
        return RPCProxy(self, oid)

class RPCClient(SocketIO):

    nextseq = 1 # Requests coming from the client are odd

    def __init__(self, address, family=socket.AF_INET, type=socket.SOCK_STREAM):
        sock = socket.socket(family, type)
        sock.connect(address)
        SocketIO.__init__(self, sock)

    def get_remote_proxy(self, oid):
        return RPCProxy(self, oid)

class RPCProxy:

    __methods = None
    __attributes = None

    def __init__(self, sockio, oid):
        self.sockio = sockio
        self.oid = oid

    def __getattr__(self, name):
        if self.__methods is None:
            self.__getmethods()
        if self.__methods.get(name):
            return MethodProxy(self.sockio, self.oid, name)
        if self.__attributes is None:
            self.__getattributes()
        if not self.__attributes.has_key(name):
            raise AttributeError, name
    __getattr__.DebuggerStepThrough=1

    def __getattributes(self):
        self.__attributes = self.sockio.remotecall(self.oid,
                                                "__attributes__", (), {})

    def __getmethods(self):
        self.__methods = self.sockio.remotecall(self.oid,
                                                "__methods__", (), {})

def _getmethods(obj, methods):
    # Helper to get a list of methods from an object
    # Adds names to dictionary argument 'methods'
    for name in dir(obj):
        attr = getattr(obj, name)
        if callable(attr):
            methods[name] = 1
    if type(obj) == types.InstanceType:
        _getmethods(obj.__class__, methods)
    if type(obj) == types.ClassType:
        for super in obj.__bases__:
            _getmethods(super, methods)

def _getattributes(obj, attributes):
    for name in dir(obj):
        attr = getattr(obj, name)
        if not callable(attr):
           attributes[name] = 1

class MethodProxy:

    def __init__(self, sockio, oid, name):
        self.sockio = sockio
        self.oid = oid
        self.name = name

    def __call__(self, *args, **kwargs):
        value = self.sockio.remotecall(self.oid, self.name, args, kwargs)
        return value

#
# Self Test
#

def testServer(addr):
    class RemotePerson:
        def __init__(self,name):
            self.name = name 
        def greet(self, name):
            print "(someone called greet)"
            print "Hello %s, I am %s." % (name, self.name)
            print
        def getName(self):
            print "(someone called getName)"
            print 
            return self.name
        def greet_this_guy(self, name):
            print "(someone called greet_this_guy)"
            print "About to greet %s ..." % name
            remote_guy = self.server.current_handler.get_remote_proxy(name)
            remote_guy.greet("Thomas Edison")
            print "Done."
            print
            
    person = RemotePerson("Thomas Edison")
    svr = RPCServer(addr)
    svr.register('thomas', person)
    person.server = svr # only required if callbacks are used

    # svr.serve_forever()
    svr.handle_request()  # process once only

def testClient(addr):

    #
    # demonstrates RPC Client
    #
    import time
    clt=RPCClient(addr)
    thomas = clt.get_remote_proxy("thomas")
    print "The remote person's name is ..."
    print thomas.getName()
    # print clt.remotecall("thomas", "getName", (), {})
    print
    time.sleep(1)
    print "Getting remote thomas to say hi..."
    thomas.greet("Alexander Bell")
    #clt.remotecall("thomas","greet",("Alexander Bell",), {})
    print "Done."
    print 
    time.sleep(2)
    
    # demonstrates remote server calling local instance
    class LocalPerson:
        def __init__(self,name):
            self.name = name 
        def greet(self, name):
            print "You've greeted me!"
        def getName(self):
            return self.name
    person = LocalPerson("Alexander Bell")
    clt.register("alexander",person)
    thomas.greet_this_guy("alexander")
    # clt.remotecall("thomas","greet_this_guy",("alexander",), {})

def test():
    addr=("localhost",8833)
    if len(sys.argv) == 2:
        if sys.argv[1]=='-server':
            testServer(addr)
            return
    testClient(addr)

if __name__ == '__main__':
    test()

        

--- NEW FILE: run.py ---
import sys
import rpc

def main():
    port = 8833
    if sys.argv[1:]:
        port = int(sys.argv[1])
    sys.argv[:] = [""]
    addr = ("localhost", port)
    svr = rpc.RPCServer(addr, MyHandler)
    svr.handle_request() # A single request only

class MyHandler(rpc.RPCHandler):

    def handle(self):
        executive = Executive(self)
        self.register("exec", executive)
        sys.stdin = self.get_remote_proxy("stdin")
        sys.stdout = self.get_remote_proxy("stdout")
        sys.stderr = self.get_remote_proxy("stderr")
        rpc.RPCHandler.handle(self)

class Executive:

    def __init__(self, rpchandler):
        self.conn = rpchandler
        import __main__
        self.locals = __main__.__dict__

    def runcode(self, code):
        exec code in self.locals

    def start_debugger(self, gui_oid):
        import RemoteDebugger
        return RemoteDebugger.start_debugger(self.conn, gui_oid)

    def stackviewer(self, flist_oid=None):
        if not hasattr(sys, "last_traceback"):
            return None
        flist = None
        if flist_oid is not None:
            flist = self.conn.get_remote_proxy(flist_oid)
        import RemoteObjectBrowser
        import StackViewer
        tb = sys.last_traceback
        while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
            tb = tb.tb_next
        item = StackViewer.StackTreeItem(flist, tb)
        return RemoteObjectBrowser.remote_object_tree_item(item)

Index: ScriptBinding.py
===================================================================
RCS file: /cvsroot/idlefork/idle/ScriptBinding.py,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -r1.4 -r1.5
*** ScriptBinding.py	19 Jan 2002 10:41:51 -0000	1.4
--- ScriptBinding.py	26 May 2002 13:36:40 -0000	1.5
***************
*** 15,18 ****
--- 15,26 ----
  code in the __main__ namespace.
  
+ XXX Redesign this interface (yet again) as follows:
+ 
+ - Present a dialog box for ``Run script''
+ 
+ - Allow specify command line arguments in the dialog box
+ 
+ - Restart the interpreter when running a script
+ 
  """
  
***************
*** 26,32 ****
  This means that either:
  
! (1) your indentation is outright incorrect (easy to fix), or
  
! (2) your indentation mixes tabs and spaces in a way that depends on \
  how many spaces a tab is worth.
  
--- 34,40 ----
  This means that either:
  
! 1) your indentation is outright incorrect (easy to fix), or
  
! 2) your indentation mixes tabs and spaces in a way that depends on \
  how many spaces a tab is worth.
  
***************
*** 106,109 ****
--- 114,121 ----
  
      def import_module_event(self, event):
+         flist = self.editwin.flist
+         shell = flist.open_shell()
+         interp = shell.interp
+ 
          filename = self.getfilename()
          if not filename:
***************
*** 111,131 ****
  
          modname, ext = os.path.splitext(os.path.basename(filename))
-         if sys.modules.has_key(modname):
-             mod = sys.modules[modname]
-         else:
-             mod = imp.new_module(modname)
-             sys.modules[modname] = mod
-         mod.__file__ = filename
-         setattr(sys.modules['__main__'], modname, mod)
  
          dir = os.path.dirname(filename)
          dir = os.path.normpath(os.path.abspath(dir))
-         if dir not in sys.path:
-             sys.path.insert(0, dir)
  
!         flist = self.editwin.flist
!         shell = flist.open_shell()
!         interp = shell.interp
!         interp.runcode("reload(%s)" % modname)
  
      def run_script_event(self, event):
--- 123,142 ----
  
          modname, ext = os.path.splitext(os.path.basename(filename))
  
          dir = os.path.dirname(filename)
          dir = os.path.normpath(os.path.abspath(dir))
  
!         interp.runcode("""if 1:
!             import sys as _sys
!             if %s not in _sys.path:
!                 _sys.path.insert(0, %s)
!             if _sys.modules.get(%s):
!                 del _sys
!                 import %s
!                 reload(%s)
!             else:
!                 del _sys
!                 import %s
!                 \n""" % (`dir`, `dir`, `modname`, modname, modname, modname))
  
      def run_script_event(self, event):
***************
*** 137,144 ****
          shell = flist.open_shell()
          interp = shell.interp
!         if (not sys.argv or
!             os.path.basename(sys.argv[0]) != os.path.basename(filename)):
!             # XXX Too often this discards arguments the user just set...
!             sys.argv = [filename]
          interp.execfile(filename)
  
--- 148,161 ----
          shell = flist.open_shell()
          interp = shell.interp
!         # XXX Too often this discards arguments the user just set...
!         interp.runcommand("""if 1:
!             _filename = %s
!             import sys as _sys
!             from os.path import basename as _basename
!             if (not _sys.argv or
!                 _basename(_sys.argv[0]) != _basename(_filename)):
!                 _sys.argv = [_filename]
!             del _filename, _sys, _basename
!                 \n""" % `filename`)
          interp.execfile(filename)
  

Index: Debugger.py
===================================================================
RCS file: /cvsroot/idlefork/idle/Debugger.py,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -r1.4 -r1.5
*** Debugger.py	25 Feb 2002 23:22:08 -0000	1.4
--- Debugger.py	26 May 2002 13:36:40 -0000	1.5
***************
*** 1,4 ****
--- 1,5 ----
  import os
  import bdb
+ import types
  import traceback
  from Tkinter import *
***************
*** 8,25 ****
  
  
! class Debugger(bdb.Bdb):
  
-     interacting = 0
  
      vstack = vsource = vlocals = vglobals = None
  
!     def __init__(self, pyshell):
!         bdb.Bdb.__init__(self)
          self.pyshell = pyshell
          self.make_gui()
  
!     def canonic(self, filename):
!         # Canonicalize filename -- called by Bdb
!         return os.path.normcase(os.path.abspath(filename))
  
      def close(self, event=None):
--- 9,72 ----
  
  
! class Idb(bdb.Bdb):
! 
!     def __init__(self, gui):
!         self.gui = gui
!         bdb.Bdb.__init__(self)
! 
!     def user_line(self, frame):
!         # get the currently executing function
!         co_filename = frame.f_code.co_filename
!         co_name = frame.f_code.co_name 
!         try:
!             func = frame.f_locals[co_name]
!             if getattr(func, "DebuggerStepThrough", 0):
!                 print "XXXX DEBUGGER STEPPING THROUGH"
!                 self.set_step()
!                 return
!         except:
!             pass
!         if co_filename in ('rpc.py', '<string>'):
!             self.set_step()
!             return
!         if co_filename.endswith('threading.py'):
!             self.set_step()
!             return
!         message = self.__frame2message(frame)
!         self.gui.interaction(message, frame)
! 
!     def user_exception(self, frame, info):
!         message = self.__frame2message(frame)
!         self.gui.interaction(message, frame, info)
! 
!     def __frame2message(self, frame):
!         code = frame.f_code
!         filename = code.co_filename
!         lineno = frame.f_lineno
!         basename = os.path.basename(filename)
!         message = "%s:%s" % (basename, lineno)
!         if code.co_name != "?":
!             message = "%s: %s()" % (message, code.co_name)
!         return message
  
  
+ class Debugger:
+ 
+     interacting = 0
      vstack = vsource = vlocals = vglobals = None
  
!     def __init__(self, pyshell, idb=None):
!         if idb is None:
!             idb = Idb(self)
          self.pyshell = pyshell
+         self.idb = idb
          self.make_gui()
  
!     def run(self, *args):
!         try:
!             self.interacting = 1
!             return self.idb.run(*args)
!         finally:
!             self.interacting = 0
  
      def close(self, event=None):
***************
*** 32,53 ****
          self.top.destroy()
  
-     def run(self, *args):
-         try:
-             self.interacting = 1
-             return apply(bdb.Bdb.run, (self,) + args)
-         finally:
-             self.interacting = 0
- 
-     def user_line(self, frame):
-         self.interaction(frame)
- 
-     def user_return(self, frame, rv):
-         # XXX show rv?
-         ##self.interaction(frame)
-         pass
- 
-     def user_exception(self, frame, info):
-         self.interaction(frame, info)
- 
      def make_gui(self):
          pyshell = self.pyshell
--- 79,82 ----
***************
*** 129,142 ****
      frame = None
  
!     def interaction(self, frame, info=None):
          self.frame = frame
-         code = frame.f_code
-         file = code.co_filename
-         base = os.path.basename(file)
-         lineno = frame.f_lineno
-         #
-         message = "%s:%s" % (base, lineno)
-         if code.co_name != "?":
-             message = "%s: %s()" % (message, code.co_name)
          self.status.configure(text=message)
          #
--- 158,163 ----
      frame = None
  
!     def interaction(self, message, frame, info=None):
          self.frame = frame
          self.status.configure(text=message)
          #
***************
*** 161,165 ****
          sv = self.stackviewer
          if sv:
!             stack, i = self.get_stack(self.frame, tb)
              sv.load_stack(stack, i)
          #
--- 182,186 ----
          sv = self.stackviewer
          if sv:
!             stack, i = self.idb.get_stack(self.frame, tb)
              sv.load_stack(stack, i)
          #
***************
*** 185,214 ****
          if not frame:
              return
          code = frame.f_code
!         file = code.co_filename
          lineno = frame.f_lineno
!         if file[:1] + file[-1:] != "<>" and os.path.exists(file):
!             edit = self.flist.open(file)
!             if edit:
!                 edit.gotoline(lineno)
  
      def cont(self):
!         self.set_continue()
          self.root.quit()
  
      def step(self):
!         self.set_step()
          self.root.quit()
  
      def next(self):
!         self.set_next(self.frame)
          self.root.quit()
  
      def ret(self):
!         self.set_return(self.frame)
          self.root.quit()
  
      def quit(self):
!         self.set_quit()
          self.root.quit()
  
--- 206,237 ----
          if not frame:
              return
+         filename, lineno = self.__frame2fileline(frame)
+         if filename[:1] + filename[-1:] != "<>" and os.path.exists(filename):
+             self.flist.gotofileline(filename, lineno)
+ 
+     def __frame2fileline(self, frame):
          code = frame.f_code
!         filename = code.co_filename
          lineno = frame.f_lineno
!         return filename, lineno
  
      def cont(self):
!         self.idb.set_continue()
          self.root.quit()
  
      def step(self):
!         self.idb.set_step()
          self.root.quit()
  
      def next(self):
!         self.idb.set_next(self.frame)
          self.root.quit()
  
      def ret(self):
!         self.idb.set_return(self.frame)
          self.root.quit()
  
      def quit(self):
!         self.idb.set_quit()
          self.root.quit()
  
***************
*** 220,224 ****
                  self.fstack, self.flist, self)
              if self.frame:
!                 stack, i = self.get_stack(self.frame, None)
                  sv.load_stack(stack, i)
          else:
--- 243,247 ----
                  self.fstack, self.flist, self)
              if self.frame:
!                 stack, i = self.idb.get_stack(self.frame, None)
                  sv.load_stack(stack, i)
          else:
***************
*** 234,237 ****
--- 257,261 ----
  
      def show_frame(self, (frame, lineno)):
+         # Called from OldStackViewer
          self.frame = frame
          self.show_variables()
***************
*** 296,309 ****
  
      # A literal copy of Bdb.set_break() without the print statement at the end
!     def set_break(self, filename, lineno, temporary=0, cond = None):
!         import linecache # Import as late as possible
!         filename = self.canonic(filename)
!         line = linecache.getline(filename, lineno)
!         if not line:
!             return 'That line does not exist!'
!         if not self.breaks.has_key(filename):
!             self.breaks[filename] = []
!         list = self.breaks[filename]
!         if not lineno in list:
!             list.append(lineno)
!         bp = bdb.Breakpoint(filename, lineno, temporary, cond)
--- 320,333 ----
  
      # A literal copy of Bdb.set_break() without the print statement at the end
!     #def set_break(self, filename, lineno, temporary=0, cond = None):
!     #    import linecache # Import as late as possible
!     #    filename = self.canonic(filename)
!     #    line = linecache.getline(filename, lineno)
!     #    if not line:
!     #        return 'That line does not exist!'
!     #    if not self.breaks.has_key(filename):
!     #        self.breaks[filename] = []
!     #    list = self.breaks[filename]
!     #    if not lineno in list:
!     #        list.append(lineno)
!     #    bp = bdb.Breakpoint(filename, lineno, temporary, cond)

Index: PyShell.py
===================================================================
RCS file: /cvsroot/idlefork/idle/PyShell.py,v
retrieving revision 1.13
retrieving revision 1.14
diff -C2 -r1.13 -r1.14
*** PyShell.py	27 Mar 2002 00:51:53 -0000	1.13
--- PyShell.py	26 May 2002 13:36:41 -0000	1.14
***************
*** 1,908 ****
! #! /usr/bin/env python
! 
! # changes by dscherer@cmu.edu
! 
! #     The main() function has been replaced by a whole class, in order to
! #     address the constraint that only one process can sit on the port
! #     hard-coded into the loader.
! 
! #     It attempts to load the RPC protocol server and publish itself.  If
! #     that fails, it assumes that some other copy of IDLE is already running
[...1958 lines suppressed...]
!                   interp.execfile(filename)
!       
!           if debug:
!               shell.open_debugger()
!           if cmd:
!               interp.execsource(cmd)
!           elif script:
!              if os.path.isfile(script):
!                  interp.execfile(script)
!              else:
!                  print "No script file: ", script
!           shell.begin()
! 
!         self.idle()
!         root.mainloop()
!         root.destroy()
! 
! 
! if __name__ == "__main__":
!     main()