# Python MPD client library
-# Copyright (C) 2008 J. Alexander Treuman <jat@spatialrift.net>
+# Copyright (C) 2008-2010 J. Alexander Treuman <jat@spatialrift.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
class CommandListError(MPDError):
pass
+class PendingCommandError(MPDError):
+ pass
+
+class IteratingError(MPDError):
+ pass
+
class _NotConnected(object):
def __getattr__(self, attr):
}
def __getattr__(self, attr):
- try:
- retval = self._commands[attr]
- except KeyError:
+ if attr.startswith("send_"):
+ command = attr.replace("send_", "", 1)
+ wrapper = self._send
+ elif attr.startswith("fetch_"):
+ command = attr.replace("fetch_", "", 1)
+ wrapper = self._fetch
+ else:
+ command = attr
+ wrapper = self._execute
+ command = command.replace("_", " ")
+ if command not in self._commands:
raise AttributeError("'%s' object has no attribute '%s'" %
(self.__class__.__name__, attr))
- return lambda *args: self._execute(attr, args, retval)
+ return lambda *args: wrapper(command, args)
- def _execute(self, command, args, retval):
- if self._command_list is not None and not callable(retval):
- raise CommandListError("%s not allowed in command list" % command)
+ def _send(self, command, args):
+ if self._command_list is not None:
+ raise CommandListError("Cannot use send_%s in a command list" %
+ command.replace(" ", "_"))
self._write_command(command, args)
- if self._command_list is None:
+ self._pending.append(command)
+
+ def _fetch(self, command, args=None):
+ if self._command_list is not None:
+ raise CommandListError("Cannot use fetch_%s in a command list" %
+ command.replace(" ", "_"))
+ if self._iterating:
+ raise IteratingError("Cannot use fetch_%s while iterating" %
+ command.replace(" ", "_"))
+ if not self._pending:
+ raise PendingCommandError("No pending commands to fetch")
+ if self._pending[0] != command:
+ raise PendingCommandError("'%s' is not the currently "
+ "pending command" % command)
+ del self._pending[0]
+ retval = self._commands[command]
+ if callable(retval):
+ 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)
+ retval = self._commands[command]
+ if self._command_list is not None:
+ if not callable(retval):
+ raise CommandListError("'%s' not allowed in command list" %
+ command)
+ self._write_command(command, args)
+ self._command_list.append(retval)
+ else:
+ self._write_command(command, args)
if callable(retval):
return retval()
return retval
- self._command_list.append(retval)
def _write_line(self, line):
self._wfile.write("%s\n" % line)
while pair:
yield pair
pair = self._read_pair(separator)
- raise StopIteration
def _read_list(self):
seen = None
(seen, key))
seen = key
yield value
- raise StopIteration
def _read_playlist(self):
for key, value in self._read_pairs(":"):
yield value
- raise StopIteration
def _read_objects(self, delimiters=[]):
obj = {}
if key in delimiters:
yield obj
obj = {}
- elif obj.has_key(key):
+ elif key in obj:
if not isinstance(obj[key], list):
obj[key] = [obj[key], value]
else:
obj[key] = value
if obj:
yield obj
- raise StopIteration
def _read_command_list(self):
- for retval in self._command_list:
- yield retval()
- self._command_list = None
+ try:
+ for retval in self._command_list:
+ yield retval()
+ finally:
+ self._command_list = None
self._fetch_nothing()
- raise StopIteration
+
+ def _iterator_wrapper(self, iterator):
+ try:
+ for item in iterator:
+ yield item
+ finally:
+ self._iterating = False
def _wrap_iterator(self, iterator):
if not self.iterate:
return list(iterator)
- return iterator
+ self._iterating = True
+ return self._iterator_wrapper(iterator)
def _fetch_nothing(self):
line = self._read_line()
def _reset(self):
self.mpd_version = None
+ self._iterating = False
+ self._pending = []
self._command_list = None
self._sock = None
self._rfile = _NotConnected()
flags = socket.AI_ADDRCONFIG
except AttributeError:
flags = 0
- msg = "getaddrinfo returns an empty list"
+ err = None
for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
socket.SOCK_STREAM, socket.IPPROTO_TCP,
flags):
af, socktype, proto, canonname, sa = res
+ sock = None
try:
sock = socket.socket(af, socktype, proto)
sock.connect(sa)
- except socket.error, msg:
- if sock:
+ return sock
+ except socket.error, err:
+ if sock is not None:
sock.close()
- sock = None
- continue
- break
- if not sock:
- raise socket.error(msg)
- return sock
+ if err is not None:
+ raise err
+ else:
+ raise ConnectionError("getaddrinfo returns an empty list")
def connect(self, host, port):
- if self._sock:
+ if self._sock is not None:
raise ConnectionError("Already connected")
if host.startswith("/"):
self._sock = self._connect_unix(host)
self._sock.close()
self._reset()
+ def fileno(self):
+ if self._sock is None:
+ raise ConnectionError("Not connected")
+ return self._sock.fileno()
+
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")
self._write_command("command_list_ok_begin")
self._command_list = []
def command_list_end(self):
if self._command_list is None:
raise CommandListError("Not in command list")
+ if self._iterating:
+ raise IteratingError("Already iterating over a command list")
self._write_command("command_list_end")
return self._fetch_command_list()