Staging
v0.5.1
https://github.com/python/cpython
Revision 3feef61742facb3cbf53f9df1cb89062452597f2 authored by Christian Heimes on 11 February 2008, 06:19:17 UTC, committed by Christian Heimes on 11 February 2008, 06:19:17 UTC
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r60701 | georg.brandl | 2008-02-09 22:36:15 +0100 (Sat, 09 Feb 2008) | 2 lines

  Needs only 2.4 now.
........
  r60702 | georg.brandl | 2008-02-09 22:38:54 +0100 (Sat, 09 Feb 2008) | 2 lines

  Docs are rst now.
........
  r60703 | georg.brandl | 2008-02-09 23:00:00 +0100 (Sat, 09 Feb 2008) | 2 lines

  Fix link.
........
  r60704 | georg.brandl | 2008-02-10 00:09:25 +0100 (Sun, 10 Feb 2008) | 2 lines

  Fix for newest doctools.
........
  r60709 | raymond.hettinger | 2008-02-10 08:21:09 +0100 (Sun, 10 Feb 2008) | 1 line

  Clarify that decimal also supports fixed-point arithmetic.
........
  r60710 | nick.coghlan | 2008-02-10 08:32:52 +0100 (Sun, 10 Feb 2008) | 1 line

  Add missing NEWS entry for r60695
........
  r60712 | mark.dickinson | 2008-02-10 15:58:38 +0100 (Sun, 10 Feb 2008) | 3 lines

  Turn classmethods into staticmethods, and avoid calling the constructor
  of subclasses of Rational.  (See discussion in issue #1682.)
........
  r60715 | mark.dickinson | 2008-02-10 16:19:58 +0100 (Sun, 10 Feb 2008) | 2 lines

  Typos in decimal comment and documentation
........
  r60716 | skip.montanaro | 2008-02-10 16:31:54 +0100 (Sun, 10 Feb 2008) | 2 lines

  Get the saying right. ;-)
........
  r60717 | skip.montanaro | 2008-02-10 16:32:16 +0100 (Sun, 10 Feb 2008) | 2 lines

  whoops - revert
........
  r60718 | mark.dickinson | 2008-02-10 20:23:36 +0100 (Sun, 10 Feb 2008) | 2 lines

  Remove reference to Rational
........
  r60719 | raymond.hettinger | 2008-02-10 21:35:16 +0100 (Sun, 10 Feb 2008) | 1 line

  Complete an open todo on pickletools -- add a pickle optimizer.
........
  r60721 | mark.dickinson | 2008-02-10 22:29:51 +0100 (Sun, 10 Feb 2008) | 3 lines

  Rename rational.Rational to fractions.Fraction, to avoid name clash
  with numbers.Rational.  See issue #1682 for related discussion.
........
  r60722 | christian.heimes | 2008-02-11 03:26:22 +0100 (Mon, 11 Feb 2008) | 1 line

  The test requires the network resource
........
  r60723 | mark.dickinson | 2008-02-11 04:11:55 +0100 (Mon, 11 Feb 2008) | 3 lines

  Put an extra space into the repr of a Fraction:
  Fraction(1, 2) instead of Fraction(1,2).
........
1 parent ba99c58
Raw File
Tip revision: 3feef61742facb3cbf53f9df1cb89062452597f2 authored by Christian Heimes on 11 February 2008, 06:19:17 UTC
Merged revisions 60481,60485,60489-60492,60494-60496,60498-60499,60501-60503,60505-60506,60508-60509,60523-60524,60532,60543,60545,60547-60548,60552,60554,60556-60559,60561-60562,60569,60571-60572,60574,60576-60583,60585-60586,60589,60591,60594-60595,60597-60598,60600-60601,60606-60612,60615,60617,60619-60621,60623-60625,60627-60629,60631,60633,60635,60647,60650,60652,60654,60656,60658-60659,60664-60666,60668-60670,60672,60676,60678,60680-60683,60685-60686,60688,60690,60692-60694,60697-60706,60708-60712,60714-60724 via svnmerge from
Tip revision: 3feef61
asynchat.py
# -*- Mode: Python; tab-width: 4 -*-
#       Id: asynchat.py,v 2.26 2000/09/07 22:29:26 rushing Exp
#       Author: Sam Rushing <rushing@nightmare.com>

# ======================================================================
# Copyright 1996 by Sam Rushing
#
#                         All Rights Reserved
#
# 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.
#
# 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.
# ======================================================================

r"""A class supporting chat-style (command/response) protocols.

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.
"""

import sys
import socket
import asyncore
from collections import deque

class async_chat (asyncore.dispatcher):
    """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

    def __init__ (self, conn=None):
        self.ac_in_buffer = b''
        self.ac_out_buffer = b''
        self.producer_fifo = fifo()
        asyncore.dispatcher.__init__ (self, conn)

    def collect_incoming_data(self, data):
        raise NotImplementedError("must be implemented in subclass")

    def found_terminator(self):
        raise NotImplementedError("must be implemented in subclass")

    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 as why:
            self.handle_error()
            return

        if isinstance(data, str):
            data = data.encode('ascii')
        self.ac_in_buffer = self.ac_in_buffer + bytes(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
        # combos with a single recv(1024).

        while self.ac_in_buffer:
            lb = len(self.ac_in_buffer)
            terminator = self.get_terminator()
            if not terminator:
                # no terminator, collect it all
                self.collect_incoming_data (self.ac_in_buffer)
                self.ac_in_buffer = b''
            elif isinstance(terminator, int) or isinstance(terminator, int):
                # numeric terminator
                n = terminator
                if lb < n:
                    self.collect_incoming_data (self.ac_in_buffer)
                    self.ac_in_buffer = b''
                    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)
                if isinstance(terminator, str):
                    terminator = terminator.encode('ascii')
                index = self.ac_in_buffer.find(terminator)
                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 = b''

    def handle_write (self):
        self.initiate_send ()

    def handle_close (self):
        self.close()

    def push (self, data):
        self.producer_fifo.push (simple_producer (data))
        self.initiate_send()

    def push_with_producer (self, producer):
        self.producer_fifo.push (producer)
        self.initiate_send()

    def readable (self):
        "predicate for inclusion in the readable for select()"
        return (len(self.ac_in_buffer) <= self.ac_in_buffer_size)

    def writable (self):
        "predicate for inclusion in the writable for select()"
        # return len(self.ac_out_buffer) or len(self.producer_fifo) or (not self.connected)
        # this is about twice as fast, though not as clear.
        return not (
                (self.ac_out_buffer == b'') and
                self.producer_fifo.is_empty() and
                self.connected
                )

    def close_when_done (self):
        "automatically close this channel once the outgoing queue is empty"
        self.producer_fifo.push (None)

    # refill the outgoing buffer by calling the more() method
    # of the first producer in the queue
    def refill_buffer (self):
        while 1:
            if len(self.producer_fifo):
                p = self.producer_fifo.first()
                # a 'None' in the producer fifo is a sentinel,
                # telling us to close the channel.
                if p is None:
                    if not self.ac_out_buffer:
                        self.producer_fifo.pop()
                        self.close()
                    return
                elif isinstance(p, str) or isinstance(p, bytes):
                    if isinstance(p, str):
                        p = p.encode('ascii')
                    self.producer_fifo.pop()
                    self.ac_out_buffer = self.ac_out_buffer + p
                    return
                data = p.more()
                if data:
                    if isinstance(data, str):
                        data = data.encode('ascii')
                    self.ac_out_buffer = self.ac_out_buffer + bytes(data)
                    return
                else:
                    self.producer_fifo.pop()
            else:
                return

    def initiate_send (self):
        obs = self.ac_out_buffer_size
        # try to refill the buffer
        if (len (self.ac_out_buffer) < obs):
            self.refill_buffer()

        if self.ac_out_buffer and self.connected:
            # try to send the buffer
            try:
                num_sent = self.send (self.ac_out_buffer[:obs])
                if num_sent:
                    self.ac_out_buffer = self.ac_out_buffer[num_sent:]

            except socket.error as why:
                self.handle_error()
                return

    def discard_buffers (self):
        # Emergencies only!
        self.ac_in_buffer = b''
        self.ac_out_buffer = b''
        while self.producer_fifo:
            self.producer_fifo.pop()


class simple_producer:

    def __init__ (self, data, buffer_size=512):
        self.data = data
        self.buffer_size = buffer_size

    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 = b''
            return result

class fifo:
    def __init__ (self, list=None):
        if not list:
            self.list = deque()
        else:
            self.list = deque(list)

    def __len__ (self):
        return len(self.list)

    def is_empty (self):
        return not self.list

    def first (self):
        return self.list[0]

    def push (self, data):
        self.list.append(data)

    def pop (self):
        if self.list:
            return (1, self.list.popleft())
        else:
            return (0, None)

# 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
# f_p_a_e ("qwerty\r\n", "\r\n") => <undefined>

# this could maybe be made faster with a computed regex?
# [answer: no; circa Python-2.0, Jan 2001]
# new python:   28961/s
# old python:   18307/s
# re:        12820/s
# regex:     14035/s

def find_prefix_at_end (haystack, needle):
    l = len(needle) - 1
    while l and not haystack.endswith(needle[:l]):
        l -= 1
    return l
back to top