[Idle-dev] CVS: idle PyShell.py,1.20,1.21 rpc.py,1.3,1.4 run.py,1.3,1.4

Kurt B. Kaiser kbk@users.sourceforge.net
Thu, 25 Jul 2002 17:06:45 -0700


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

Modified Files:
	PyShell.py rpc.py run.py 
Log Message:
Reverse the RPC socket connection: Python execution server connects to
Idle client and localhost origin of connection is verified by client.
M PyShell.py
M rpc.py
M run.py


Index: PyShell.py
===================================================================
RCS file: /cvsroot/idlefork/idle/PyShell.py,v
retrieving revision 1.20
retrieving revision 1.21
diff -C2 -r1.20 -r1.21
*** PyShell.py	11 Jul 2002 04:33:41 -0000	1.20
--- PyShell.py	26 Jul 2002 00:06:41 -0000	1.21
***************
*** 194,202 ****
          port = 8833
          addr = ("localhost", port)
          w = ['-W' + s for s in sys.warnoptions]
          args = [sys.executable] + w + ["-c", "__import__('run').main()",
                                         str(port)]
          self.rpcpid = os.spawnv(os.P_NOWAIT, args[0], args)
!         for i in range(5):
              time.sleep(i)
              try:
--- 194,206 ----
          port = 8833
          addr = ("localhost", port)
+         # Spawn the Python execution "server"
          w = ['-W' + s for s in sys.warnoptions]
          args = [sys.executable] + w + ["-c", "__import__('run').main()",
                                         str(port)]
          self.rpcpid = os.spawnv(os.P_NOWAIT, args[0], args)
!         # Idle starts listening for connection on localhost, retry since
!         # Idle may be restarted before port is available for rebinding
!         # XXX 25 July 2002 KBK Find out what is causing the delayed release!
!         for i in range(12):
              time.sleep(i)
              try:
***************
*** 204,215 ****
                  break
              except socket.error, err:
!                 if i > 3:
!                     print >>sys.__stderr__, "Socket error:", err, "; retry..."
          else:
-             # XXX Make this a dialog?  #GvR
-             print >>sys.__stderr__, "Can't spawn subprocess!"
-             # XXX Add Stephen's error msg, resolve the two later... KBK 09Jun02
              display_port_binding_error()
              return
          self.rpcclt.register("stdin", self.tkconsole)
          self.rpcclt.register("stdout", self.tkconsole.stdout)
--- 208,221 ----
                  break
              except socket.error, err:
!                 if i < 5:
!                     print>>sys.__stderr__, ". ",
!                 else:
!                     print>>sys.__stderr__,"\nIdle socket error: " + err[1]\
!                                                     + ", retrying..."
          else:
              display_port_binding_error()
              return
+         # Accept the connection from the Python execution server
+         self.rpcclt.accept()
          self.rpcclt.register("stdin", self.tkconsole)
          self.rpcclt.register("stdout", self.tkconsole.stdout)

Index: rpc.py
===================================================================
RCS file: /cvsroot/idlefork/idle/rpc.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -r1.3 -r1.4
*** rpc.py	26 Jun 2002 02:32:09 -0000	1.3
--- rpc.py	26 Jul 2002 00:06:41 -0000	1.4
***************
*** 1,21 ****
! # ASCII-art documentation
! #
! #  +---------------------------------+ +----------+
! #  | SocketServer.BaseRequestHandler | | SocketIO |
! #  +---------------------------------+ +----------+
! #                  ^                      ^  ^
! #                  |                      |  |
! #                  | + -------------------+  |
! #                  | |                       |
! #  +-------------------------+        +-----------------+
! #  | RPCHandler              |        | RPCClient       |
! #  |-------------------------|        |-----------------|
! #  | register()              |        | remotecall()    |
! #  | unregister()            |        | register()      | 
! #  |                         |        | unregister()    |
! #  |                         |        | get_remote_proxy|
! #  +-------------------------+        +-----------------+
! #
! import sys 
  import socket
  import select
--- 1,32 ----
! """RPC Implemention, originally written for the Python Idle IDE
! 
! For security reasons, GvR requested that Idle's Python execution server process
! connect to the Idle process, which listens for the connection.  Since Idle has
! has only one client per server, this was not a limitation.
! 
!    +---------------------------------+ +-------------+
!    | SocketServer.BaseRequestHandler | | SocketIO    |
!    +---------------------------------+ +-------------+
!                    ^                   | register()  |
!                    |                   | unregister()|
!                    |                   +-------------+
!                    |                      ^  ^
!                    |                      |  |
!                    | + -------------------+  |
!                    | |                       |
!    +-------------------------+        +-----------------+
!    | RPCHandler              |        | RPCClient       |
!    | [attribute of RPCServer]|        |                 |
!    +-------------------------+        +-----------------+
! 
! The RPCServer handler class is expected to provide register/unregister methods.
! RPCHandler inherits the mix-in class SocketIO, which provides these methods.
! 
! See the Idle run.main() docstring for further information on how this was
! accomplished in Idle.
! 
! """
! 
! import sys
  import socket
  import select
***************
*** 56,93 ****
          if handlerclass is None:
              handlerclass = RPCHandler
! # XXX KBK 25Jun02 Not used in Idlefork, see register/unregister note below.
  #        self.objtable = objecttable 
          SocketServer.TCPServer.__init__(self, addr, handlerclass)
  
!     # XXX KBK 25Jun02 Following method is not used (yet)
! #      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
! 
! # XXX KBK 25Jun02 The handlerclass is expected to provide register/unregister
! #                 methods.  In Idle, RPCServer is instantiated with
! #                 handlerclass MyHandler, which in turn inherits the
! #                 register/unregister methods from the mix-in class SocketIO.
! #                 It is true that this is asymmetric with the RPCClient's use
! #                 of register/unregister, but I guess that's how a SocketServer
! #                 is supposed to work.
! 
! #                 Exactly how this gets set up is convoluted.  When the
! #                 TCPServer is instantiated, it creates an instance of
! #                 run.MyHandler and calls its handle() method.  handle()
! #                 instantiates a run.Executive, passing it a reference to the
! #                 MyHandler object.  That reference is saved as an attribute of
! #                 the Executive instance.  The Executive methods have access to
! #                 the reference and can pass it on to entities that they
! #                 command (e.g. RemoteDebugger.Debugger.start_debugger()).  The
! #                 latter, in turn, can call MyHandler(SocketIO)
! #                 register/unregister methods via the reference to register and
! #                 unregister themselves.  Whew.
  
!     # The following two methods are not currently used in Idlefork.
  #      def register(self, oid, object):
  #          self.objtable[oid] = object
--- 67,93 ----
          if handlerclass is None:
              handlerclass = RPCHandler
! # XXX KBK 25Jun02 Not used in Idlefork.
  #        self.objtable = objecttable 
          SocketServer.TCPServer.__init__(self, addr, handlerclass)
  
!     def server_bind(self):
!         "Override TCPServer method, no bind() phase for connecting entity"
!         pass
! 
!     def server_activate(self):
!         """Override TCPServer method, connect() instead of listen()
!         
!         Due to the reversed connection, self.server_address is actually the
!         address of the Idle Client to which we are connecting.
! 
!         """
!         self.socket.connect(self.server_address)
!         
!     def get_request(self):
!         "Override TCPServer method, return already connected socket"
!         return self.socket, self.server_address
!         
  
! # XXX The following two methods are not currently used in Idlefork.
  #      def register(self, oid, object):
  #          self.objtable[oid] = object
***************
*** 365,368 ****
--- 365,370 ----
                  self.statelock.release()
                  continue
+             
+ #----------------- end class SocketIO --------------------
  
  class RemoteObject:
***************
*** 389,401 ****
          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()
  
--- 391,396 ----
          SocketServer.BaseRequestHandler.__init__(self, sock, addr, svr)
  
      def handle(self):
+         "handle() method required by SocketServer"
          self.mainloop()
  
***************
*** 405,414 ****
  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):
--- 400,418 ----
  class RPCClient(SocketIO):
  
!     nextseq = 1 # Requests coming from the client are odd numbered
  
      def __init__(self, address, family=socket.AF_INET, type=socket.SOCK_STREAM):
!         self.sock = socket.socket(family, type)
!         self.sock.bind(address)
!         self.sock.listen(1)
! 
!     def accept(self):
!         newsock, address = self.sock.accept()
!         if address[0] == '127.0.0.1':
!             print>>sys.__stderr__, "Idle accepted connection from ", address
!             SocketIO.__init__(self, newsock)
!         else:
!             print>>sys.__stderr__, "Invalid host: ", address
!             raise socket.error
  
      def get_remote_proxy(self, oid):
***************
*** 478,481 ****
--- 482,486 ----
  
  def testServer(addr):
+     # XXX 25 Jul 02 KBK needs update to use rpc.py register/unregister methods
      class RemotePerson:
          def __init__(self,name):
***************
*** 506,513 ****
  
  def testClient(addr):
! 
!     #
!     # demonstrates RPC Client
!     #
      import time
      clt=RPCClient(addr)
--- 511,516 ----
  
  def testClient(addr):
!     "demonstrates RPC Client"
!     # XXX 25 Jul 02 KBK needs update to use rpc.py register/unregister methods
      import time
      clt=RPCClient(addr)
***************
*** 524,528 ****
      print 
      time.sleep(2)
-     
      # demonstrates remote server calling local instance
      class LocalPerson:
--- 527,530 ----

Index: run.py
===================================================================
RCS file: /cvsroot/idlefork/idle/run.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -r1.3 -r1.4
*** run.py	26 Jun 2002 02:32:09 -0000	1.3
--- run.py	26 Jul 2002 00:06:42 -0000	1.4
***************
*** 1,6 ****
--- 1,24 ----
  import sys
+ import time
+ import socket
  import rpc
  
  def main():
+     """Start the Python execution server in a subprocess
+ 
+     In Idle, RPCServer is instantiated with handlerclass MyHandler, which
+     inherits register/unregister methods from RPCHandler via the mix-in class
+     SocketIO.
+ 
+     When the RPCServer is instantiated, the TCPServer initialization creates an
+     instance of run.MyHandler and calls its handle() method.  handle()
+     instantiates a run.Executive, passing it a reference to the MyHandler
+     object.  That reference is saved as an attribute of the Executive instance.
+     The Executive methods have access to the reference and can pass it on to
+     entities that they command (e.g. RemoteDebugger.Debugger.start_debugger()).
+     The latter, in turn, can call MyHandler(SocketIO) register/unregister
+     methods via the reference to register and unregister themselves.
+ 
+     """
      port = 8833
      if sys.argv[1:]:
***************
*** 8,12 ****
      sys.argv[:] = [""]
      addr = ("localhost", port)
!     svr = rpc.RPCServer(addr, MyHandler)
      svr.handle_request() # A single request only
  
--- 26,43 ----
      sys.argv[:] = [""]
      addr = ("localhost", port)
!     for i in range(12):
!         time.sleep(i)
!         try:
!             svr = rpc.RPCServer(addr, MyHandler)
!             break
!         except socket.error, err:
!             if i < 5:
!                 print>>sys.__stderr__, ".. ",
!             else:
!                 print>>sys.__stderr__,"\nPython subprocess socket error: "\
!                                               + err[1] + ", retrying...."
!     else:
!         print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
!         sys.exit()
      svr.handle_request() # A single request only