mpd.py: check if iterating before fetching response
authorJ. Alexander Treuman <jat@spatialrift.net>
Thu, 15 Jul 2010 21:12:53 +0000 (17:12 -0400)
committerJ. Alexander Treuman <jat@spatialrift.net>
Thu, 15 Jul 2010 21:12:53 +0000 (17:12 -0400)
If an iterator is currently in progress, all functions which fetch a
response (such as <cmd>() and fetch_<cmd>()) will be disabled, as well as
command lists.  This makes iterators much more reliable by preventing code
from reading a response while an iterator is trying to read another
response.  Previously this was allowed, and would corrupt the library state
by providing false responses to both the iterator and the code trying to
fetch a different response.

mpd.py

diff --git a/mpd.py b/mpd.py
index 32b51a1..8810fac 100644 (file)
--- a/mpd.py
+++ b/mpd.py
@@ -41,6 +41,9 @@ class CommandListError(MPDError):
 class PendingCommandError(MPDError):
     pass
 
+class IteratingError(MPDError):
+    pass
+
 
 class _NotConnected(object):
     def __getattr__(self, attr):
@@ -159,6 +162,9 @@ class MPDClient(object):
         if self._command_list is not None:
             raise CommandListError("Cannot use fetch_%s in a command list" %
                                    command)
+        if self._iterating:
+            raise IteratingError("Cannot use fetch_%s while iterating" %
+                                 command)
         if not self._pending:
             raise PendingCommandError("No pending commands to fetch")
         if self._pending[0] != command:
@@ -170,6 +176,8 @@ class MPDClient(object):
             return retval()
 
     def _execute(self, command, args):
+        if self._iterating:
+            raise IteratingError("Cannot execute %s while iterating" % command)
         if self._pending:
             raise PendingCommandError("Cannot execute %s with "
                                       "pending commands" % command)
@@ -266,10 +274,16 @@ class MPDClient(object):
         self._command_list = None
         self._fetch_nothing()
 
+    def _iterator_wrapper(self, iterator):
+        self._iterating = True
+        for item in iterator:
+            yield item
+        self._iterating = False
+
     def _wrap_iterator(self, iterator):
         if not self.iterate:
             return list(iterator)
-        return iterator
+        return self._iterator_wrapper(iterator)
 
     def _fetch_nothing(self):
         line = self._read_line()
@@ -326,6 +340,7 @@ class MPDClient(object):
 
     def _reset(self):
         self.mpd_version = None
+        self._iterating = False
         self._pending = []
         self._command_list = None
         self._sock = None
@@ -392,6 +407,8 @@ class MPDClient(object):
     def command_list_ok_begin(self):
         if self._command_list is not None:
             raise CommandListError("Already in command list")
+        if self._iterating:
+            raise IteratingError("Cannot begin command list while iterating")
         if self._pending:
             raise PendingCommandError("Cannot begin command list "
                                       "with pending commands")