| 
									
										
										
										
											1999-01-12 20:19:27 +00:00
										 |  |  | # -*- Mode: Python; tab-width: 4 -*- | 
					
						
							| 
									
										
										
										
											2001-02-09 20:06:00 +00:00
										 |  |  | #       Id: asynchat.py,v 2.26 2000/09/07 22:29:26 rushing Exp | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  | #       Author: Sam Rushing <rushing@nightmare.com> | 
					
						
							| 
									
										
										
										
											1999-01-12 20:19:27 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # ====================================================================== | 
					
						
							|  |  |  | # Copyright 1996 by Sam Rushing | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											1999-01-12 20:19:27 +00:00
										 |  |  | #                         All Rights Reserved | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											1999-01-12 20:19:27 +00:00
										 |  |  | # Permission to use, copy, modify, and distribute this software and | 
					
						
							|  |  |  | # its documentation for any purpose and without fee is hereby | 
					
						
							|  |  |  | # granted, provided that the above copyright notice appear in all | 
					
						
							|  |  |  | # copies and that both that copyright notice and this permission | 
					
						
							|  |  |  | # notice appear in supporting documentation, and that the name of Sam | 
					
						
							|  |  |  | # Rushing not be used in advertising or publicity pertaining to | 
					
						
							|  |  |  | # distribution of the software without specific, written prior | 
					
						
							|  |  |  | # permission. | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											1999-01-12 20:19:27 +00:00
										 |  |  | # SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | 
					
						
							|  |  |  | # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN | 
					
						
							|  |  |  | # NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR | 
					
						
							|  |  |  | # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS | 
					
						
							|  |  |  | # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, | 
					
						
							|  |  |  | # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | 
					
						
							|  |  |  | # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
					
						
							|  |  |  | # ====================================================================== | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-04-06 15:30:33 +00:00
										 |  |  | r"""A class supporting chat-style (command/response) protocols.
 | 
					
						
							| 
									
										
										
										
											2000-02-04 15:39:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | This class adds support for 'chat' style protocols - where one side | 
					
						
							|  |  |  | sends a 'command', and the other sends a response (examples would be | 
					
						
							|  |  |  | the common internet protocols - smtp, nntp, ftp, etc..). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The handle_read() method looks at the input stream for the current | 
					
						
							|  |  |  | 'terminator' (usually '\r\n' for single-line responses, '\r\n.\r\n' | 
					
						
							|  |  |  | for multi-line output), calling self.found_terminator() on its | 
					
						
							|  |  |  | receipt. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | for example: | 
					
						
							|  |  |  | Say you build an async nntp client using this class.  At the start | 
					
						
							|  |  |  | of the connection, you'll have self.terminator set to '\r\n', in | 
					
						
							|  |  |  | order to process the single-line greeting.  Just before issuing a | 
					
						
							|  |  |  | 'LIST' command you'll set it to '\r\n.\r\n'.  The output of the LIST | 
					
						
							|  |  |  | command will be accumulated (using your own 'collect_incoming_data' | 
					
						
							|  |  |  | method) up to the terminator, and then control will be returned to | 
					
						
							|  |  |  | you - by calling your self.found_terminator() method. | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1999-01-12 20:19:27 +00:00
										 |  |  | import socket | 
					
						
							|  |  |  | import asyncore | 
					
						
							| 
									
										
										
										
											2004-02-07 03:19:10 +00:00
										 |  |  | from collections import deque | 
					
						
							| 
									
										
										
										
											2008-09-02 01:25:16 +00:00
										 |  |  | from sys import py3kwarning | 
					
						
							|  |  |  | from warnings import filterwarnings, catch_warnings | 
					
						
							| 
									
										
										
										
											1999-01-12 20:19:27 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class async_chat (asyncore.dispatcher): | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |     """This is an abstract class.  You must derive from this class, and add
 | 
					
						
							|  |  |  |     the two methods collect_incoming_data() and found_terminator()"""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # these are overridable defaults | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ac_in_buffer_size       = 4096 | 
					
						
							|  |  |  |     ac_out_buffer_size      = 4096 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-07 04:51:46 +00:00
										 |  |  |     def __init__ (self, sock=None, map=None): | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  |         # for string terminator matching | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |         self.ac_in_buffer = '' | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # we use a list here rather than cStringIO for a few reasons... | 
					
						
							|  |  |  |         # del lst[:] is faster than sio.truncate(0) | 
					
						
							|  |  |  |         # lst = [] is faster than sio.truncate(0) | 
					
						
							|  |  |  |         # cStringIO will be gaining unicode support in py3k, which | 
					
						
							|  |  |  |         # will negatively affect the performance of bytes compared to | 
					
						
							|  |  |  |         # a ''.join() equivalent | 
					
						
							|  |  |  |         self.incoming = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # we toss the use of the "simple producer" and replace it with | 
					
						
							|  |  |  |         # a pure deque, which the original fifo was a wrapping of | 
					
						
							|  |  |  |         self.producer_fifo = deque() | 
					
						
							| 
									
										
										
										
											2008-07-07 04:51:46 +00:00
										 |  |  |         asyncore.dispatcher.__init__ (self, sock, map) | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-03-08 18:27:11 +00:00
										 |  |  |     def collect_incoming_data(self, data): | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  |         raise NotImplementedError("must be implemented in subclass") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _collect_incoming_data(self, data): | 
					
						
							|  |  |  |         self.incoming.append(data) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _get_data(self): | 
					
						
							|  |  |  |         d = ''.join(self.incoming) | 
					
						
							|  |  |  |         del self.incoming[:] | 
					
						
							|  |  |  |         return d | 
					
						
							| 
									
										
										
										
											2002-04-16 01:38:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-03-08 18:27:11 +00:00
										 |  |  |     def found_terminator(self): | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  |         raise NotImplementedError("must be implemented in subclass") | 
					
						
							| 
									
										
										
										
											2002-04-16 01:38:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |     def set_terminator (self, term): | 
					
						
							|  |  |  |         "Set the input delimiter.  Can be a fixed string of any length, an integer, or None" | 
					
						
							|  |  |  |         self.terminator = term | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_terminator (self): | 
					
						
							|  |  |  |         return self.terminator | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # grab some more data from the socket, | 
					
						
							|  |  |  |     # throw it to the collector method, | 
					
						
							|  |  |  |     # check for the terminator, | 
					
						
							|  |  |  |     # if found, transition to the next state. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def handle_read (self): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             data = self.recv (self.ac_in_buffer_size) | 
					
						
							|  |  |  |         except socket.error, why: | 
					
						
							|  |  |  |             self.handle_error() | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.ac_in_buffer = self.ac_in_buffer + data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Continue to search for self.terminator in self.ac_in_buffer, | 
					
						
							|  |  |  |         # while calling self.collect_incoming_data.  The while loop | 
					
						
							|  |  |  |         # is necessary because we might read several data+terminator | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  |         # combos with a single recv(4096). | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         while self.ac_in_buffer: | 
					
						
							|  |  |  |             lb = len(self.ac_in_buffer) | 
					
						
							|  |  |  |             terminator = self.get_terminator() | 
					
						
							| 
									
										
										
										
											2005-06-09 14:59:45 +00:00
										 |  |  |             if not terminator: | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |                 # no terminator, collect it all | 
					
						
							|  |  |  |                 self.collect_incoming_data (self.ac_in_buffer) | 
					
						
							|  |  |  |                 self.ac_in_buffer = '' | 
					
						
							| 
									
										
										
										
											2005-06-09 14:59:45 +00:00
										 |  |  |             elif isinstance(terminator, int) or isinstance(terminator, long): | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |                 # numeric terminator | 
					
						
							|  |  |  |                 n = terminator | 
					
						
							|  |  |  |                 if lb < n: | 
					
						
							|  |  |  |                     self.collect_incoming_data (self.ac_in_buffer) | 
					
						
							|  |  |  |                     self.ac_in_buffer = '' | 
					
						
							|  |  |  |                     self.terminator = self.terminator - lb | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     self.collect_incoming_data (self.ac_in_buffer[:n]) | 
					
						
							|  |  |  |                     self.ac_in_buffer = self.ac_in_buffer[n:] | 
					
						
							|  |  |  |                     self.terminator = 0 | 
					
						
							|  |  |  |                     self.found_terminator() | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 # 3 cases: | 
					
						
							|  |  |  |                 # 1) end of buffer matches terminator exactly: | 
					
						
							|  |  |  |                 #    collect data, transition | 
					
						
							|  |  |  |                 # 2) end of buffer matches some prefix: | 
					
						
							|  |  |  |                 #    collect data to the prefix | 
					
						
							|  |  |  |                 # 3) end of buffer does not match any prefix: | 
					
						
							|  |  |  |                 #    collect data | 
					
						
							|  |  |  |                 terminator_len = len(terminator) | 
					
						
							| 
									
										
										
										
											2001-04-05 22:38:32 +00:00
										 |  |  |                 index = self.ac_in_buffer.find(terminator) | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |                 if index != -1: | 
					
						
							|  |  |  |                     # we found the terminator | 
					
						
							|  |  |  |                     if index > 0: | 
					
						
							|  |  |  |                         # don't bother reporting the empty string (source of subtle bugs) | 
					
						
							|  |  |  |                         self.collect_incoming_data (self.ac_in_buffer[:index]) | 
					
						
							|  |  |  |                     self.ac_in_buffer = self.ac_in_buffer[index+terminator_len:] | 
					
						
							|  |  |  |                     # This does the Right Thing if the terminator is changed here. | 
					
						
							|  |  |  |                     self.found_terminator() | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     # check for a prefix of the terminator | 
					
						
							|  |  |  |                     index = find_prefix_at_end (self.ac_in_buffer, terminator) | 
					
						
							|  |  |  |                     if index: | 
					
						
							|  |  |  |                         if index != lb: | 
					
						
							|  |  |  |                             # we found a prefix, collect up to the prefix | 
					
						
							|  |  |  |                             self.collect_incoming_data (self.ac_in_buffer[:-index]) | 
					
						
							|  |  |  |                             self.ac_in_buffer = self.ac_in_buffer[-index:] | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         # no prefix, collect it all | 
					
						
							|  |  |  |                         self.collect_incoming_data (self.ac_in_buffer) | 
					
						
							|  |  |  |                         self.ac_in_buffer = '' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def handle_write (self): | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  |         self.initiate_send() | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def handle_close (self): | 
					
						
							|  |  |  |         self.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def push (self, data): | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  |         sabs = self.ac_out_buffer_size | 
					
						
							|  |  |  |         if len(data) > sabs: | 
					
						
							|  |  |  |             for i in xrange(0, len(data), sabs): | 
					
						
							|  |  |  |                 self.producer_fifo.append(data[i:i+sabs]) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.producer_fifo.append(data) | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |         self.initiate_send() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def push_with_producer (self, producer): | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  |         self.producer_fifo.append(producer) | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |         self.initiate_send() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def readable (self): | 
					
						
							|  |  |  |         "predicate for inclusion in the readable for select()" | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  |         # cannot use the old predicate, it violates the claim of the | 
					
						
							|  |  |  |         # set_terminator method. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # return (len(self.ac_in_buffer) <= self.ac_in_buffer_size) | 
					
						
							|  |  |  |         return 1 | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def writable (self): | 
					
						
							|  |  |  |         "predicate for inclusion in the writable for select()" | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  |         return self.producer_fifo or (not self.connected) | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def close_when_done (self): | 
					
						
							|  |  |  |         "automatically close this channel once the outgoing queue is empty" | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  |         self.producer_fifo.append(None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def initiate_send(self): | 
					
						
							|  |  |  |         while self.producer_fifo and self.connected: | 
					
						
							|  |  |  |             first = self.producer_fifo[0] | 
					
						
							|  |  |  |             # handle empty string/buffer or None entry | 
					
						
							|  |  |  |             if not first: | 
					
						
							|  |  |  |                 del self.producer_fifo[0] | 
					
						
							|  |  |  |                 if first is None: | 
					
						
							|  |  |  |                     self.handle_close() | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |                     return | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             # handle classic producer behavior | 
					
						
							|  |  |  |             obs = self.ac_out_buffer_size | 
					
						
							|  |  |  |             try: | 
					
						
							| 
									
										
										
										
											2008-09-02 01:25:16 +00:00
										 |  |  |                 with catch_warnings(): | 
					
						
							|  |  |  |                     if py3kwarning: | 
					
						
							|  |  |  |                         filterwarnings("ignore", ".*buffer", DeprecationWarning) | 
					
						
							| 
									
										
										
										
											2008-08-09 23:06:16 +00:00
										 |  |  |                     data = buffer(first, 0, obs) | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  |             except TypeError: | 
					
						
							|  |  |  |                 data = first.more() | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |                 if data: | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  |                     self.producer_fifo.appendleft(data) | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  |                     del self.producer_fifo[0] | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  |             # send the data | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |             try: | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  |                 num_sent = self.send(data) | 
					
						
							|  |  |  |             except socket.error: | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |                 self.handle_error() | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  |             if num_sent: | 
					
						
							|  |  |  |                 if num_sent < len(data) or obs < len(first): | 
					
						
							|  |  |  |                     self.producer_fifo[0] = first[num_sent:] | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     del self.producer_fifo[0] | 
					
						
							|  |  |  |             # we tried to send some actual data | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |     def discard_buffers (self): | 
					
						
							|  |  |  |         # Emergencies only! | 
					
						
							|  |  |  |         self.ac_in_buffer = '' | 
					
						
							| 
									
										
										
										
											2008-06-10 05:00:08 +00:00
										 |  |  |         del self.incoming[:] | 
					
						
							|  |  |  |         self.producer_fifo.clear() | 
					
						
							| 
									
										
										
										
											2000-09-08 20:30:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1999-01-12 20:19:27 +00:00
										 |  |  | class simple_producer: | 
					
						
							| 
									
										
										
										
											1999-06-08 13:20:05 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |     def __init__ (self, data, buffer_size=512): | 
					
						
							|  |  |  |         self.data = data | 
					
						
							|  |  |  |         self.buffer_size = buffer_size | 
					
						
							| 
									
										
										
										
											1999-01-12 20:19:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |     def more (self): | 
					
						
							|  |  |  |         if len (self.data) > self.buffer_size: | 
					
						
							|  |  |  |             result = self.data[:self.buffer_size] | 
					
						
							|  |  |  |             self.data = self.data[self.buffer_size:] | 
					
						
							|  |  |  |             return result | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             result = self.data | 
					
						
							|  |  |  |             self.data = '' | 
					
						
							|  |  |  |             return result | 
					
						
							| 
									
										
										
										
											1999-01-12 20:19:27 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class fifo: | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |     def __init__ (self, list=None): | 
					
						
							|  |  |  |         if not list: | 
					
						
							| 
									
										
										
										
											2004-02-07 03:19:10 +00:00
										 |  |  |             self.list = deque() | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2004-02-07 03:19:10 +00:00
										 |  |  |             self.list = deque(list) | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __len__ (self): | 
					
						
							|  |  |  |         return len(self.list) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def is_empty (self): | 
					
						
							| 
									
										
										
										
											2004-09-27 17:49:00 +00:00
										 |  |  |         return not self.list | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def first (self): | 
					
						
							| 
									
										
										
										
											2004-03-01 23:16:22 +00:00
										 |  |  |         return self.list[0] | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def push (self, data): | 
					
						
							| 
									
										
										
										
											2004-02-07 03:19:10 +00:00
										 |  |  |         self.list.append(data) | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def pop (self): | 
					
						
							|  |  |  |         if self.list: | 
					
						
							| 
									
										
										
										
											2004-02-07 03:19:10 +00:00
										 |  |  |             return (1, self.list.popleft()) | 
					
						
							| 
									
										
										
										
											2001-01-14 18:09:23 +00:00
										 |  |  |         else: | 
					
						
							|  |  |  |             return (0, None) | 
					
						
							| 
									
										
										
										
											1999-01-12 20:19:27 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # Given 'haystack', see if any prefix of 'needle' is at its end.  This | 
					
						
							|  |  |  | # assumes an exact match has already been checked.  Return the number of | 
					
						
							|  |  |  | # characters matched. | 
					
						
							|  |  |  | # for example: | 
					
						
							|  |  |  | # f_p_a_e ("qwerty\r", "\r\n") => 1 | 
					
						
							|  |  |  | # f_p_a_e ("qwertydkjf", "\r\n") => 0 | 
					
						
							| 
									
										
										
										
											2002-03-20 02:22:58 +00:00
										 |  |  | # f_p_a_e ("qwerty\r\n", "\r\n") => <undefined> | 
					
						
							| 
									
										
										
										
											1999-01-12 20:19:27 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # this could maybe be made faster with a computed regex? | 
					
						
							| 
									
										
										
										
											2001-01-24 21:10:55 +00:00
										 |  |  | # [answer: no; circa Python-2.0, Jan 2001] | 
					
						
							| 
									
										
										
										
											2002-03-20 02:22:58 +00:00
										 |  |  | # new python:   28961/s | 
					
						
							|  |  |  | # old python:   18307/s | 
					
						
							| 
									
										
										
										
											2001-01-24 21:10:55 +00:00
										 |  |  | # re:        12820/s | 
					
						
							|  |  |  | # regex:     14035/s | 
					
						
							| 
									
										
										
										
											1999-01-12 20:19:27 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def find_prefix_at_end (haystack, needle): | 
					
						
							| 
									
										
										
										
											2002-04-16 01:38:40 +00:00
										 |  |  |     l = len(needle) - 1 | 
					
						
							|  |  |  |     while l and not haystack.endswith(needle[:l]): | 
					
						
							|  |  |  |         l -= 1 | 
					
						
							|  |  |  |     return l |