home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pytho152.zip / emx / lib / python1.5 / asyncore.py < prev    next >
Text File  |  2000-08-10  |  11KB  |  454 lines

  1. # -*- Mode: Python; tab-width: 4 -*-
  2. #     $Id: asyncore.py,v 1.1 1999/01/12 20:19:27 guido Exp $
  3. #    Author: Sam Rushing <rushing@nightmare.com>
  4.  
  5. # ======================================================================
  6. # Copyright 1996 by Sam Rushing
  7. #                         All Rights Reserved
  8. # Permission to use, copy, modify, and distribute this software and
  9. # its documentation for any purpose and without fee is hereby
  10. # granted, provided that the above copyright notice appear in all
  11. # copies and that both that copyright notice and this permission
  12. # notice appear in supporting documentation, and that the name of Sam
  13. # Rushing not be used in advertising or publicity pertaining to
  14. # distribution of the software without specific, written prior
  15. # permission.
  16. # SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  17. # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
  18. # NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  19. # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
  20. # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  21. # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  22. # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  23. # ======================================================================
  24.  
  25. import select
  26. import socket
  27. import string
  28. import sys
  29.  
  30. import os
  31. if os.name == 'nt':
  32.     EWOULDBLOCK    = 10035
  33.     EINPROGRESS    = 10036
  34.     EALREADY    = 10037
  35.     ECONNRESET  = 10054
  36.     ENOTCONN    = 10057
  37. else:
  38.     from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, ENOTCONN
  39.  
  40. socket_map = {}
  41.  
  42. def poll (timeout=0.0, ignore_expt=1):
  43.     if socket_map:
  44.         sockets = socket_map.keys()
  45.         r = filter (lambda x: x.readable(), sockets)
  46.         w = filter (lambda x: x.writable(), sockets)
  47.         if ignore_expt:
  48.             e = []
  49.         else:
  50.             e = sockets[:]
  51.  
  52.         (r,w,e) = select.select (r,w,e, timeout)
  53.  
  54.         for x in e:
  55.             try:
  56.                 x.handle_expt_event()
  57.             except:
  58.                 x.handle_error (sys.exc_type, sys.exc_value, sys.exc_traceback)
  59.         for x in r:
  60.             try:
  61.                 x.handle_read_event()
  62.             except:
  63.                 x.handle_error (sys.exc_type, sys.exc_value, sys.exc_traceback)
  64.         for x in w:
  65.             try:
  66.                 x.handle_write_event()
  67.             except:
  68.                 x.handle_error (sys.exc_type, sys.exc_value, sys.exc_traceback)
  69.  
  70. def poll2 (timeout=0.0):
  71.     import poll
  72.     # timeout is in milliseconds
  73.     timeout = int(timeout*1000)
  74.     if socket_map:
  75.         fd_map = {}
  76.         for s in socket_map.keys():
  77.             fd_map[s.fileno()] = s
  78.         l = []
  79.         for fd, s in fd_map.items():
  80.             flags = 0
  81.             if s.readable():
  82.                 flags = poll.POLLIN
  83.             if s.writable():
  84.                 flags = flags | poll.POLLOUT
  85.             if flags:
  86.                 l.append (fd, flags)
  87.         r = poll.poll (l, timeout)
  88.         print r
  89.         for fd, flags in r:
  90.             s = fd_map[fd]
  91.             try:
  92.                 if (flags & poll.POLLIN):
  93.                         s.handle_read_event()
  94.                 if (flags & poll.POLLOUT):
  95.                         s.handle_write_event()
  96.                 if (flags & poll.POLLERR):
  97.                         s.handle_expt_event()
  98.             except:
  99.                 apply (s.handle_error, sys.exc_info())
  100.  
  101.  
  102. def loop (timeout=30.0, use_poll=0):
  103.  
  104.     if use_poll:
  105.         poll_fun = poll2
  106.     else:
  107.         poll_fun = poll
  108.  
  109.     while socket_map:
  110.         poll_fun (timeout)
  111.  
  112. class dispatcher:
  113.     debug = 0
  114.     connected = 0
  115.     accepting = 0
  116.     closing = 0
  117.     addr = None
  118.  
  119.     def __init__ (self, sock=None):
  120.         if sock:
  121.             self.set_socket (sock)
  122.             # I think it should inherit this anyway
  123.             self.socket.setblocking (0)
  124.             self.connected = 1
  125.  
  126.     def __repr__ (self):
  127.         try:
  128.             status = []
  129.             if self.accepting and self.addr:
  130.                 status.append ('listening')
  131.             elif self.connected:
  132.                 status.append ('connected')
  133.             if self.addr:
  134.                 status.append ('%s:%d' % self.addr)
  135.             return '<%s %s at %x>' % (
  136.                 self.__class__.__name__,
  137.                 string.join (status, ' '),
  138.                 id(self)
  139.                 )
  140.         except:
  141.             try:
  142.                 ar = repr(self.addr)
  143.             except:
  144.                 ar = 'no self.addr!'
  145.                 
  146.             return '<__repr__ (self) failed for object at %x (addr=%s)>' % (id(self),ar)
  147.  
  148.     def add_channel (self):
  149.         self.log ('adding channel %s' % self)
  150.         socket_map [self] = 1
  151.  
  152.     def del_channel (self):
  153.         if socket_map.has_key (self):
  154.             self.log ('closing channel %d:%s' % (self.fileno(), self))
  155.             del socket_map [self]
  156.  
  157.     def create_socket (self, family, type):
  158.         self.family_and_type = family, type
  159.         self.socket = socket.socket (family, type)
  160.         self.socket.setblocking(0)
  161.         self.add_channel()
  162.  
  163.     def set_socket (self, socket):
  164.         self.socket = socket
  165.         self.add_channel()
  166.  
  167.     def set_reuse_addr (self):
  168.         # try to re-use a server port if possible
  169.         try:
  170.             self.socket.setsockopt (
  171.                 socket.SOL_SOCKET, socket.SO_REUSEADDR,
  172.                 self.socket.getsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR) | 1
  173.                 )
  174.         except:
  175.             pass
  176.  
  177.     # ==================================================
  178.     # predicates for select()
  179.     # these are used as filters for the lists of sockets
  180.     # to pass to select().
  181.     # ==================================================
  182.  
  183.     def readable (self):
  184.         return 1
  185.  
  186.     if os.name == 'mac':
  187.         # The macintosh will select a listening socket for
  188.         # write if you let it.  What might this mean?
  189.         def writable (self):
  190.             return not self.accepting
  191.     else:
  192.         def writable (self):
  193.             return 1
  194.  
  195.     # ==================================================
  196.     # socket object methods.
  197.     # ==================================================
  198.  
  199.     def listen (self, num):
  200.         self.accepting = 1
  201.         if os.name == 'nt' and num > 5:
  202.             num = 1
  203.         return self.socket.listen (num)
  204.  
  205.     def bind (self, addr):
  206.         self.addr = addr
  207.         return self.socket.bind (addr)
  208.  
  209.     def connect (self, address):
  210.         try:
  211.             self.socket.connect (address)
  212.         except socket.error, why:
  213.             if why[0] in (EINPROGRESS, EALREADY, EWOULDBLOCK):
  214.                 return
  215.             else:
  216.                 raise socket.error, why
  217.         self.connected = 1
  218.         self.handle_connect()
  219.  
  220.     def accept (self):
  221.         try:
  222.             conn, addr = self.socket.accept()
  223.             return conn, addr
  224.         except socket.error, why:
  225.             if why[0] == EWOULDBLOCK:
  226.                 pass
  227.             else:
  228.                 raise socket.error, why
  229.  
  230.     def send (self, data):
  231.         try:
  232.             result = self.socket.send (data)
  233.             return result
  234.         except socket.error, why:
  235.             if why[0] == EWOULDBLOCK:
  236.                 return 0
  237.             else:
  238.                 raise socket.error, why
  239.             return 0
  240.  
  241.     def recv (self, buffer_size):
  242.         try:
  243.             data = self.socket.recv (buffer_size)
  244.             if not data:
  245.                 # a closed connection is indicated by signaling
  246.                 # a read condition, and having recv() return 0.
  247.                 self.handle_close()
  248.                 return ''
  249.             else:
  250.                 return data
  251.         except socket.error, why:
  252.             # winsock sometimes throws ENOTCONN
  253.             if why[0] in [ECONNRESET, ENOTCONN]:
  254.                 self.handle_close()
  255.                 return ''
  256.             else:
  257.                 raise socket.error, why
  258.  
  259.     def close (self):
  260.         self.del_channel()
  261.         self.socket.close()
  262.         self.connected = 0
  263.  
  264.     # cheap inheritance, used to pass all other attribute
  265.     # references to the underlying socket object.
  266.     def __getattr__ (self, attr):
  267.         if attr != 'socket':
  268.             return getattr (self.socket, attr)
  269.         else:
  270.             raise AttributeError, attr
  271.  
  272.     def log (self, message):
  273.         print 'log:', message
  274.  
  275.     def handle_read_event (self):
  276.         if self.accepting:
  277.             # for an accepting socket, getting a read implies
  278.             # that we are connected
  279.             if not self.connected:
  280.                 self.connected = 1
  281.             self.handle_accept()
  282.         elif not self.connected:
  283.             self.handle_connect()
  284.             self.connected = 1
  285.             self.handle_read()
  286.         else:
  287.             self.handle_read()
  288.  
  289.     def handle_write_event (self):
  290.         # getting a write implies that we are connected
  291.         if not self.connected:
  292.             self.handle_connect()
  293.             self.connected = 1
  294.         self.handle_write()
  295.  
  296.     def handle_expt_event (self):
  297.         self.handle_expt()
  298.  
  299.     def handle_error (self, *info):
  300.         (t,v,tb) = info
  301.         (file,fun,line), tbinfo = compact_traceback (t,v,tb)
  302.  
  303.         # sometimes a user repr method will crash.
  304.         try:
  305.             self_repr = repr (self)
  306.         except:
  307.             self_repr = '<__repr__ (self) failed for object at %0x>' % id(self)
  308.  
  309.         print (
  310.             'uncaptured python exception, closing channel %s (%s:%s %s)' % (
  311.                 self_repr,
  312.                 str(t),
  313.                 str(v),
  314.                 tbinfo
  315.                 )
  316.             )
  317.         del t,v,tb
  318.         self.close()
  319.  
  320.     def handle_expt (self):
  321.         self.log ('unhandled exception')
  322.  
  323.     def handle_read (self):
  324.         self.log ('unhandled read event')
  325.  
  326.     def handle_write (self):
  327.         self.log ('unhandled write event')
  328.  
  329.     def handle_connect (self):
  330.         self.log ('unhandled connect event')
  331.  
  332.     def handle_oob (self):
  333.         self.log ('unhandled out-of-band event')
  334.  
  335.     def handle_accept (self):
  336.         self.log ('unhandled accept event')
  337.  
  338.     def handle_close (self):
  339.         self.log ('unhandled close event')
  340.         self.close()
  341.  
  342. # ---------------------------------------------------------------------------
  343. # adds simple buffered output capability, useful for simple clients.
  344. # [for more sophisticated usage use asynchat.async_chat]
  345. # ---------------------------------------------------------------------------
  346.  
  347. class dispatcher_with_send (dispatcher):
  348.     def __init__ (self, sock=None):
  349.         dispatcher.__init__ (self, sock)
  350.         self.out_buffer = ''
  351.  
  352.     def initiate_send (self):
  353.         num_sent = 0
  354.         num_sent = dispatcher.send (self, self.out_buffer[:512])
  355.         self.out_buffer = self.out_buffer[num_sent:]
  356.  
  357.     def handle_write (self):
  358.         self.initiate_send()
  359.  
  360.     def writable (self):
  361.         return (not self.connected) or len(self.out_buffer)
  362.  
  363.     def send (self, data):
  364.         if self.debug:
  365.             self.log ('sending %s' % repr(data))
  366.         self.out_buffer = self.out_buffer + data
  367.         self.initiate_send()
  368.  
  369. # ---------------------------------------------------------------------------
  370. # used for debugging.
  371. # ---------------------------------------------------------------------------
  372.  
  373. def compact_traceback (t,v,tb):
  374.     tbinfo = []
  375.     while 1:
  376.         tbinfo.append (
  377.             tb.tb_frame.f_code.co_filename,
  378.             tb.tb_frame.f_code.co_name,                
  379.             str(tb.tb_lineno)
  380.             )
  381.         tb = tb.tb_next
  382.         if not tb:
  383.             break
  384.  
  385.     file, function, line = tbinfo[-1]
  386.     info = '[' + string.join (
  387.         map (
  388.             lambda x: string.join (x, '|'),
  389.             tbinfo
  390.             ),
  391.         '] ['
  392.         ) + ']'
  393.     return (file, function, line), info
  394.  
  395. def close_all ():
  396.     global socket_map
  397.     for x in socket_map.keys():
  398.         x.socket.close()
  399.     socket_map.clear()
  400.  
  401. # Asynchronous File I/O:
  402. #
  403. # After a little research (reading man pages on various unixen, and
  404. # digging through the linux kernel), I've determined that select()
  405. # isn't meant for doing doing asynchronous file i/o.
  406. # Heartening, though - reading linux/mm/filemap.c shows that linux
  407. # supports asynchronous read-ahead.  So _MOST_ of the time, the data
  408. # will be sitting in memory for us already when we go to read it.
  409. #
  410. # What other OS's (besides NT) support async file i/o?  [VMS?]
  411. #
  412. # Regardless, this is useful for pipes, and stdin/stdout...
  413.  
  414. import os
  415. if os.name == 'posix':
  416.     import fcntl
  417.     import FCNTL
  418.  
  419.     class file_wrapper:
  420.         # here we override just enough to make a file
  421.         # look like a socket for the purposes of asyncore.
  422.         def __init__ (self, fd):
  423.             self.fd = fd
  424.  
  425.         def recv (self, *args):
  426.             return apply (os.read, (self.fd,)+args)
  427.  
  428.         def write (self, *args):
  429.             return apply (os.write, (self.fd,)+args)
  430.  
  431.         def close (self):
  432.             return os.close (self.fd)
  433.  
  434.         def fileno (self):
  435.             return self.fd
  436.  
  437.     class file_dispatcher (dispatcher):
  438.         def __init__ (self, fd):
  439.             dispatcher.__init__ (self)
  440.             self.connected = 1
  441.             # set it to non-blocking mode
  442.             flags = fcntl.fcntl (fd, FCNTL.F_GETFL, 0)
  443.             flags = flags | FCNTL.O_NONBLOCK
  444.             fcntl.fcntl (fd, FCNTL.F_SETFL, flags)
  445.             self.set_file (fd)
  446.  
  447.         def set_file (self, fd):
  448.             self.socket = file_wrapper (fd)
  449.             self.add_channel()
  450. #not really
  451.