From 4049b23c3f8baeb191c6657f42229dae3cc4c500 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Mon, 25 Jul 2011 00:50:41 +0200 Subject: [PATCH 1/4] Add concept of delimeter to complemend terminator in LineProtocol --- mopidy/utils/network.py | 13 +++++++++-- tests/utils/network/lineprotocol_test.py | 28 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/mopidy/utils/network.py b/mopidy/utils/network.py index b7cc144d..9a02035b 100644 --- a/mopidy/utils/network.py +++ b/mopidy/utils/network.py @@ -278,9 +278,13 @@ class LineProtocol(ThreadingActor): then splitting data along line boundaries. """ - #: What terminator to use to split lines. + #: Line terinator to use for outputed lines. terminator = '\n' + #: Regex to use for splitings lines, will be set compiled version of its + #: own value, or to `terminator`s value if it is not set itself. + delimeter = None + #: What encoding to expect incomming data to be in, can be :class:`None`. encoding = 'utf-8' @@ -288,6 +292,11 @@ class LineProtocol(ThreadingActor): self.connection = connection self.recv_buffer = '' + if self.delimeter: + self.delimeter = re.compile(self.delimeter) + else: + self.delimeter = re.compile(self.terminator) + @property def host(self): return self.connection.host @@ -325,7 +334,7 @@ class LineProtocol(ThreadingActor): def parse_lines(self): """Consume new data and yield any lines found.""" while re.search(self.terminator, self.recv_buffer): - line, self.recv_buffer = re.split(self.terminator, + line, self.recv_buffer = self.delimeter.split( self.recv_buffer, 1) yield line diff --git a/tests/utils/network/lineprotocol_test.py b/tests/utils/network/lineprotocol_test.py index a87f461c..57b78417 100644 --- a/tests/utils/network/lineprotocol_test.py +++ b/tests/utils/network/lineprotocol_test.py @@ -1,5 +1,6 @@ #encoding: utf-8 +import re import unittest from mopidy.utils import network @@ -11,11 +12,21 @@ class LineProtocolTest(unittest.TestCase): self.mock = Mock(spec=network.LineProtocol) self.mock.terminator = network.LineProtocol.terminator self.mock.encoding = network.LineProtocol.encoding + self.mock.delimeter = network.LineProtocol.delimeter def test_init_stores_values_in_attributes(self): + delimeter = re.compile(network.LineProtocol.terminator) network.LineProtocol.__init__(self.mock, sentinel.connection) self.assertEqual(sentinel.connection, self.mock.connection) self.assertEqual('', self.mock.recv_buffer) + self.assertEqual(delimeter, self.mock.delimeter) + + def test_init_compiles_delimeter(self): + self.mock.delimeter = '\r?\n' + delimeter = re.compile('\r?\n') + + network.LineProtocol.__init__(self.mock, sentinel.connection) + self.assertEqual(delimeter, self.mock.delimeter) def test_on_receive_no_new_lines_adds_to_recv_buffer(self): self.mock.connection = Mock(spec=network.Connection) @@ -74,18 +85,21 @@ class LineProtocolTest(unittest.TestCase): self.assertEqual(2, self.mock.on_line_received.call_count) def test_parse_lines_emtpy_buffer(self): + self.mock.delimeter = re.compile(r'\n') self.mock.recv_buffer = '' lines = network.LineProtocol.parse_lines(self.mock) self.assertRaises(StopIteration, lines.next) def test_parse_lines_no_terminator(self): + self.mock.delimeter = re.compile(r'\n') self.mock.recv_buffer = 'data' lines = network.LineProtocol.parse_lines(self.mock) self.assertRaises(StopIteration, lines.next) def test_parse_lines_termintor(self): + self.mock.delimeter = re.compile(r'\n') self.mock.recv_buffer = 'data\n' lines = network.LineProtocol.parse_lines(self.mock) @@ -93,7 +107,17 @@ class LineProtocolTest(unittest.TestCase): self.assertRaises(StopIteration, lines.next) self.assertEqual('', self.mock.recv_buffer) + def test_parse_lines_termintor_with_carriage_return(self): + self.mock.delimeter = re.compile(r'\r?\n') + self.mock.recv_buffer = 'data\r\n' + + lines = network.LineProtocol.parse_lines(self.mock) + self.assertEqual('data', lines.next()) + self.assertRaises(StopIteration, lines.next) + self.assertEqual('', self.mock.recv_buffer) + def test_parse_lines_no_data_before_terminator(self): + self.mock.delimeter = re.compile(r'\n') self.mock.recv_buffer = '\n' lines = network.LineProtocol.parse_lines(self.mock) @@ -102,6 +126,7 @@ class LineProtocolTest(unittest.TestCase): self.assertEqual('', self.mock.recv_buffer) def test_parse_lines_extra_data_after_terminator(self): + self.mock.delimeter = re.compile(r'\n') self.mock.recv_buffer = 'data1\ndata2' lines = network.LineProtocol.parse_lines(self.mock) @@ -110,6 +135,7 @@ class LineProtocolTest(unittest.TestCase): self.assertEqual('data2', self.mock.recv_buffer) def test_parse_lines_unicode(self): + self.mock.delimeter = re.compile(r'\n') self.mock.recv_buffer = u'æøå\n'.encode('utf-8') lines = network.LineProtocol.parse_lines(self.mock) @@ -118,6 +144,7 @@ class LineProtocolTest(unittest.TestCase): self.assertEqual('', self.mock.recv_buffer) def test_parse_lines_multiple_lines(self): + self.mock.delimeter = re.compile(r'\n') self.mock.recv_buffer = 'abc\ndef\nghi\njkl' lines = network.LineProtocol.parse_lines(self.mock) @@ -128,6 +155,7 @@ class LineProtocolTest(unittest.TestCase): self.assertEqual('jkl', self.mock.recv_buffer) def test_parse_lines_multiple_calls(self): + self.mock.delimeter = re.compile(r'\n') self.mock.recv_buffer = 'data1' lines = network.LineProtocol.parse_lines(self.mock) From 68c947ddf261c3db9632772c743bf9586d46f041 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Mon, 25 Jul 2011 00:52:32 +0200 Subject: [PATCH 2/4] Allow clients to use carriage return in mpd sessions --- mopidy/frontends/mpd/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mopidy/frontends/mpd/__init__.py b/mopidy/frontends/mpd/__init__.py index 8b6d3770..78742ed5 100644 --- a/mopidy/frontends/mpd/__init__.py +++ b/mopidy/frontends/mpd/__init__.py @@ -48,6 +48,7 @@ class MpdSession(network.LineProtocol): terminator = protocol.LINE_TERMINATOR encoding = protocol.ENCODING + delimeter = r'\r?\n' def __init__(self, connection): super(MpdSession, self).__init__(connection) From 9fe4674b367b7714f235660d6d31019354db9b43 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Mon, 25 Jul 2011 01:24:19 +0200 Subject: [PATCH 3/4] Update on_received to handle that decode can fail --- mopidy/utils/network.py | 3 ++- tests/utils/network/lineprotocol_test.py | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/mopidy/utils/network.py b/mopidy/utils/network.py index 9a02035b..6b2f69e5 100644 --- a/mopidy/utils/network.py +++ b/mopidy/utils/network.py @@ -323,7 +323,8 @@ class LineProtocol(ThreadingActor): for line in self.parse_lines(): line = self.decode(line) - self.on_line_received(line) + if line is not None: + self.on_line_received(line) self.connection.enable_timeout() diff --git a/tests/utils/network/lineprotocol_test.py b/tests/utils/network/lineprotocol_test.py index 57b78417..f3877126 100644 --- a/tests/utils/network/lineprotocol_test.py +++ b/tests/utils/network/lineprotocol_test.py @@ -74,6 +74,15 @@ class LineProtocolTest(unittest.TestCase): network.LineProtocol.on_receive(self.mock, {'received': 'data\n'}) self.mock.on_line_received.assert_called_once_with(sentinel.decoded) + def test_on_receive_with_new_line_with_failed_decode(self): + self.mock.connection = Mock(spec=network.Connection) + self.mock.recv_buffer = '' + self.mock.parse_lines.return_value = [sentinel.line] + self.mock.decode.return_value = None + + network.LineProtocol.on_receive(self.mock, {'received': 'data\n'}) + self.assertEqual(0, self.mock.on_line_received.call_count) + def test_on_receive_with_new_lines_calls_on_recieve(self): self.mock.connection = Mock(spec=network.Connection) self.mock.recv_buffer = '' From b2188f13cbc36bf6286400621dec4a12a0ef23c9 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Mon, 25 Jul 2011 17:41:02 +0200 Subject: [PATCH 4/4] Typo fixes --- mopidy/utils/network.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mopidy/utils/network.py b/mopidy/utils/network.py index a1ddeb82..9306ccd7 100644 --- a/mopidy/utils/network.py +++ b/mopidy/utils/network.py @@ -278,11 +278,11 @@ class LineProtocol(ThreadingActor): then splitting data along line boundaries. """ - #: Line terinator to use for outputed lines. + #: Line terminator to use for outputed lines. terminator = '\n' - #: Regex to use for splitings lines, will be set compiled version of its - #: own value, or to `terminator`s value if it is not set itself. + #: Regex to use for spliting lines, will be set compiled version of its + #: own value, or to ``terminator``s value if it is not set itself. delimeter = None #: What encoding to expect incomming data to be in, can be :class:`None`.