home *** CD-ROM | disk | FTP | other *** search
/ Freelog Special Freeware 31 / FreelogHS31.iso / Texte / scribus / scribus-1.3.3.9-win32-install.exe / lib / asynchat.py < prev    next >
Text File  |  2004-10-15  |  11KB  |  296 lines

  1. # -*- Mode: Python; tab-width: 4 -*-
  2. #       Id: asynchat.py,v 2.26 2000/09/07 22:29:26 rushing Exp
  3. #       Author: Sam Rushing <rushing@nightmare.com>
  4.  
  5. # ======================================================================
  6. # Copyright 1996 by Sam Rushing
  7. #
  8. #                         All Rights Reserved
  9. #
  10. # Permission to use, copy, modify, and distribute this software and
  11. # its documentation for any purpose and without fee is hereby
  12. # granted, provided that the above copyright notice appear in all
  13. # copies and that both that copyright notice and this permission
  14. # notice appear in supporting documentation, and that the name of Sam
  15. # Rushing not be used in advertising or publicity pertaining to
  16. # distribution of the software without specific, written prior
  17. # permission.
  18. #
  19. # SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  20. # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
  21. # NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  22. # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
  23. # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  24. # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  25. # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  26. # ======================================================================
  27.  
  28. r"""A class supporting chat-style (command/response) protocols.
  29.  
  30. This class adds support for 'chat' style protocols - where one side
  31. sends a 'command', and the other sends a response (examples would be
  32. the common internet protocols - smtp, nntp, ftp, etc..).
  33.  
  34. The handle_read() method looks at the input stream for the current
  35. 'terminator' (usually '\r\n' for single-line responses, '\r\n.\r\n'
  36. for multi-line output), calling self.found_terminator() on its
  37. receipt.
  38.  
  39. for example:
  40. Say you build an async nntp client using this class.  At the start
  41. of the connection, you'll have self.terminator set to '\r\n', in
  42. order to process the single-line greeting.  Just before issuing a
  43. 'LIST' command you'll set it to '\r\n.\r\n'.  The output of the LIST
  44. command will be accumulated (using your own 'collect_incoming_data'
  45. method) up to the terminator, and then control will be returned to
  46. you - by calling your self.found_terminator() method.
  47. """
  48.  
  49. import socket
  50. import asyncore
  51. from collections import deque
  52.  
  53. class async_chat (asyncore.dispatcher):
  54.     """This is an abstract class.  You must derive from this class, and add
  55.     the two methods collect_incoming_data() and found_terminator()"""
  56.  
  57.     # these are overridable defaults
  58.  
  59.     ac_in_buffer_size       = 4096
  60.     ac_out_buffer_size      = 4096
  61.  
  62.     def __init__ (self, conn=None):
  63.         self.ac_in_buffer = ''
  64.         self.ac_out_buffer = ''
  65.         self.producer_fifo = fifo()
  66.         asyncore.dispatcher.__init__ (self, conn)
  67.  
  68.     def collect_incoming_data(self, data):
  69.         raise NotImplementedError, "must be implemented in subclass"
  70.  
  71.     def found_terminator(self):
  72.         raise NotImplementedError, "must be implemented in subclass"
  73.  
  74.     def set_terminator (self, term):
  75.         "Set the input delimiter.  Can be a fixed string of any length, an integer, or None"
  76.         self.terminator = term
  77.  
  78.     def get_terminator (self):
  79.         return self.terminator
  80.  
  81.     # grab some more data from the socket,
  82.     # throw it to the collector method,
  83.     # check for the terminator,
  84.     # if found, transition to the next state.
  85.  
  86.     def handle_read (self):
  87.  
  88.         try:
  89.             data = self.recv (self.ac_in_buffer_size)
  90.         except socket.error, why:
  91.             self.handle_error()
  92.             return
  93.  
  94.         self.ac_in_buffer = self.ac_in_buffer + data
  95.  
  96.         # Continue to search for self.terminator in self.ac_in_buffer,
  97.         # while calling self.collect_incoming_data.  The while loop
  98.         # is necessary because we might read several data+terminator
  99.         # combos with a single recv(1024).
  100.  
  101.         while self.ac_in_buffer:
  102.             lb = len(self.ac_in_buffer)
  103.             terminator = self.get_terminator()
  104.             if terminator is None or terminator == '':
  105.                 # no terminator, collect it all
  106.                 self.collect_incoming_data (self.ac_in_buffer)
  107.                 self.ac_in_buffer = ''
  108.             elif isinstance(terminator, int):
  109.                 # numeric terminator
  110.                 n = terminator
  111.                 if lb < n:
  112.                     self.collect_incoming_data (self.ac_in_buffer)
  113.                     self.ac_in_buffer = ''
  114.                     self.terminator = self.terminator - lb
  115.                 else:
  116.                     self.collect_incoming_data (self.ac_in_buffer[:n])
  117.                     self.ac_in_buffer = self.ac_in_buffer[n:]
  118.                     self.terminator = 0
  119.                     self.found_terminator()
  120.             else:
  121.                 # 3 cases:
  122.                 # 1) end of buffer matches terminator exactly:
  123.                 #    collect data, transition
  124.                 # 2) end of buffer matches some prefix:
  125.                 #    collect data to the prefix
  126.                 # 3) end of buffer does not match any prefix:
  127.                 #    collect data
  128.                 terminator_len = len(terminator)
  129.                 index = self.ac_in_buffer.find(terminator)
  130.                 if index != -1:
  131.                     # we found the terminator
  132.                     if index > 0:
  133.                         # don't bother reporting the empty string (source of subtle bugs)
  134.                         self.collect_incoming_data (self.ac_in_buffer[:index])
  135.                     self.ac_in_buffer = self.ac_in_buffer[index+terminator_len:]
  136.                     # This does the Right Thing if the terminator is changed here.
  137.                     self.found_terminator()
  138.                 else:
  139.                     # check for a prefix of the terminator
  140.                     index = find_prefix_at_end (self.ac_in_buffer, terminator)
  141.                     if index:
  142.                         if index != lb:
  143.                             # we found a prefix, collect up to the prefix
  144.                             self.collect_incoming_data (self.ac_in_buffer[:-index])
  145.                             self.ac_in_buffer = self.ac_in_buffer[-index:]
  146.                         break
  147.                     else:
  148.                         # no prefix, collect it all
  149.                         self.collect_incoming_data (self.ac_in_buffer)
  150.                         self.ac_in_buffer = ''
  151.  
  152.     def handle_write (self):
  153.         self.initiate_send ()
  154.  
  155.     def handle_close (self):
  156.         self.close()
  157.  
  158.     def push (self, data):
  159.         self.producer_fifo.push (simple_producer (data))
  160.         self.initiate_send()
  161.  
  162.     def push_with_producer (self, producer):
  163.         self.producer_fifo.push (producer)
  164.         self.initiate_send()
  165.  
  166.     def readable (self):
  167.         "predicate for inclusion in the readable for select()"
  168.         return (len(self.ac_in_buffer) <= self.ac_in_buffer_size)
  169.  
  170.     def writable (self):
  171.         "predicate for inclusion in the writable for select()"
  172.         # return len(self.ac_out_buffer) or len(self.producer_fifo) or (not self.connected)
  173.         # this is about twice as fast, though not as clear.
  174.         return not (
  175.                 (self.ac_out_buffer == '') and
  176.                 self.producer_fifo.is_empty() and
  177.                 self.connected
  178.                 )
  179.  
  180.     def close_when_done (self):
  181.         "automatically close this channel once the outgoing queue is empty"
  182.         self.producer_fifo.push (None)
  183.  
  184.     # refill the outgoing buffer by calling the more() method
  185.     # of the first producer in the queue
  186.     def refill_buffer (self):
  187.         while 1:
  188.             if len(self.producer_fifo):
  189.                 p = self.producer_fifo.first()
  190.                 # a 'None' in the producer fifo is a sentinel,
  191.                 # telling us to close the channel.
  192.                 if p is None:
  193.                     if not self.ac_out_buffer:
  194.                         self.producer_fifo.pop()
  195.                         self.close()
  196.                     return
  197.                 elif isinstance(p, str):
  198.                     self.producer_fifo.pop()
  199.                     self.ac_out_buffer = self.ac_out_buffer + p
  200.                     return
  201.                 data = p.more()
  202.                 if data:
  203.                     self.ac_out_buffer = self.ac_out_buffer + data
  204.                     return
  205.                 else:
  206.                     self.producer_fifo.pop()
  207.             else:
  208.                 return
  209.  
  210.     def initiate_send (self):
  211.         obs = self.ac_out_buffer_size
  212.         # try to refill the buffer
  213.         if (len (self.ac_out_buffer) < obs):
  214.             self.refill_buffer()
  215.  
  216.         if self.ac_out_buffer and self.connected:
  217.             # try to send the buffer
  218.             try:
  219.                 num_sent = self.send (self.ac_out_buffer[:obs])
  220.                 if num_sent:
  221.                     self.ac_out_buffer = self.ac_out_buffer[num_sent:]
  222.  
  223.             except socket.error, why:
  224.                 self.handle_error()
  225.                 return
  226.  
  227.     def discard_buffers (self):
  228.         # Emergencies only!
  229.         self.ac_in_buffer = ''
  230.         self.ac_out_buffer = ''
  231.         while self.producer_fifo:
  232.             self.producer_fifo.pop()
  233.  
  234.  
  235. class simple_producer:
  236.  
  237.     def __init__ (self, data, buffer_size=512):
  238.         self.data = data
  239.         self.buffer_size = buffer_size
  240.  
  241.     def more (self):
  242.         if len (self.data) > self.buffer_size:
  243.             result = self.data[:self.buffer_size]
  244.             self.data = self.data[self.buffer_size:]
  245.             return result
  246.         else:
  247.             result = self.data
  248.             self.data = ''
  249.             return result
  250.  
  251. class fifo:
  252.     def __init__ (self, list=None):
  253.         if not list:
  254.             self.list = deque()
  255.         else:
  256.             self.list = deque(list)
  257.  
  258.     def __len__ (self):
  259.         return len(self.list)
  260.  
  261.     def is_empty (self):
  262.         return not self.list
  263.  
  264.     def first (self):
  265.         return self.list[0]
  266.  
  267.     def push (self, data):
  268.         self.list.append(data)
  269.  
  270.     def pop (self):
  271.         if self.list:
  272.             return (1, self.list.popleft())
  273.         else:
  274.             return (0, None)
  275.  
  276. # Given 'haystack', see if any prefix of 'needle' is at its end.  This
  277. # assumes an exact match has already been checked.  Return the number of
  278. # characters matched.
  279. # for example:
  280. # f_p_a_e ("qwerty\r", "\r\n") => 1
  281. # f_p_a_e ("qwertydkjf", "\r\n") => 0
  282. # f_p_a_e ("qwerty\r\n", "\r\n") => <undefined>
  283.  
  284. # this could maybe be made faster with a computed regex?
  285. # [answer: no; circa Python-2.0, Jan 2001]
  286. # new python:   28961/s
  287. # old python:   18307/s
  288. # re:        12820/s
  289. # regex:     14035/s
  290.  
  291. def find_prefix_at_end (haystack, needle):
  292.     l = len(needle) - 1
  293.     while l and not haystack.endswith(needle[:l]):
  294.         l -= 1
  295.     return l
  296.