/circuits/net/protocols/line.py

https://bitbucket.org/prologic/circuits/ · Python · 113 lines · 67 code · 12 blank · 34 comment · 3 complexity · 1cdb47a1f45dbf70eaccc44e095dd4c6 MD5 · raw file

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