PageRenderTime 26ms CodeModel.GetById 14ms app.highlight 8ms RepoModel.GetById 2ms app.codeStats 0ms

/circuits/net/protocols/line.py

https://bitbucket.org/prologic/circuits/
Python | 113 lines | 67 code | 12 blank | 34 comment | 1 complexity | 1cdb47a1f45dbf70eaccc44e095dd4c6 MD5 | raw file
  1# Module:   line
  2# Date:     04th February 2010
  3# Author:   James Mills <prologic@shortcircuit.net.au>
  4
  5"""Line Protocol
  6
  7This module implements the basic Line protocol.
  8
  9This module can be used in both server and client implementations.
 10"""
 11
 12import re
 13
 14from circuits.core import handler, Event, BaseComponent
 15
 16LINESEP = re.compile(b"\r?\n")
 17
 18
 19def splitLines(s, buffer):
 20    """splitLines(s, buffer) -> lines, buffer
 21
 22    Append s to buffer and find any new lines of text in the
 23    string splitting at the standard IRC delimiter CRLF. Any
 24    new lines found, return them as a list and the remaining
 25    buffer for further processing.
 26    """
 27
 28    lines = LINESEP.split(buffer + s)
 29    return lines[:-1], lines[-1]
 30
 31
 32class Line(Event):
 33    """Line Event"""
 34
 35
 36class LP(BaseComponent):
 37    """Line Protocol
 38
 39    Implements the Line Protocol.
 40
 41    Incoming data is split into lines with a splitter function. For each
 42    line of data processed a Line Event is created. Any unfinished lines
 43    are appended into an internal buffer.
 44
 45    A custom line splitter function can be passed to customize how data
 46    is split into lines. This function must accept two arguments, the data
 47    to process and any left over data from a previous invocation of the
 48    splitter function. The function must also return a tiple of two items,
 49    a list of lines and any left over data.
 50
 51    :param splitter: a line splitter function
 52    :type  splitter: function
 53
 54    This Component operates in two modes. In normal operation it's expected
 55    to be used in conjunction with components that expose a Read Event on
 56    a "read" channel with only one argument (data). Some builtin components
 57    that expose such events are:
 58    - circuits.net.sockets.TCPClient
 59    - circuits.io.File
 60
 61    The second mode of operation works with circuits.net.sockets.Server
 62    components such as TCPServer, UNIXServer, etc. It's expected that
 63    two arguments exist in the Read Event, sock and data. The following
 64    two arguments can be passed to affect how unfinished data is stored
 65    and retrieved for such components:
 66
 67    :param getBuffer: function to retrieve the buffer for a client sock
 68    :type getBuffer: function
 69
 70    This function must accept one argument (sock,) the client socket
 71    whoose buffer is to be retrieved.
 72
 73    :param updateBuffer: function to update the buffer for a client sock
 74    :type updateBuffer: function
 75
 76    This function must accept two arguments (sock, buffer,) the client
 77    socket and the left over buffer to be updated.
 78
 79    @note: This Component must be used in conjunction with a Component that
 80           exposes Read events on a "read" Channel.
 81    """
 82
 83    def __init__(self, *args, **kwargs):
 84        "initializes x; see x.__class__.__doc__ for signature"
 85
 86        super(LP, self).__init__(*args, **kwargs)
 87
 88        self.encoding = kwargs.get('encoding', 'utf-8')
 89
 90        # Used for Servers
 91        self.getBuffer = kwargs.get("getBuffer")
 92        self.updateBuffer = kwargs.get("updateBuffer")
 93
 94        self.splitter = kwargs.get("splitter", splitLines)
 95
 96        self.buffer = b""
 97
 98    @handler("read")
 99    def _on_read(self, *args):
100        if len(args) == 1:
101            # Client read
102            data, = args
103            lines, self.buffer = self.splitter(data, self.buffer)
104            for line in lines:
105                self.fire(Line(line.decode(self.encoding, "replace")))
106        else:
107            # Server read
108            sock, data = args
109            lines, buffer = self.splitter(data, self.getBuffer(sock))
110            self.updateBuffer(sock, buffer)
111            for line in lines:
112                self.fire(Line(sock,
113                    line.decode(self.encoding, "replace")))