X-Git-Url: http://git.kaliko.me/?p=python-musicpd.git;a=blobdiff_plain;f=mpd.py;h=b87891b8b93e9be559d26ac39c85f44c4ac56888;hp=1704b7b7fc1e4b6b820f70fad09a343bb34ece30;hb=46753bf1c1e917dee5ba9ced19fbf1f6a9c103ca;hpb=06f843337164b653c7db5eddf0098c6d4ec2ff45 diff --git a/mpd.py b/mpd.py index 1704b7b..b87891b 100644 --- a/mpd.py +++ b/mpd.py @@ -1,5 +1,5 @@ # Python MPD client library -# Copyright (C) 2008 J. Alexander Treuman +# Copyright (C) 2008-2010 J. Alexander Treuman # # 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 @@ -38,6 +38,12 @@ class CommandError(MPDError): class CommandListError(MPDError): pass +class PendingCommandError(MPDError): + pass + +class IteratingError(MPDError): + pass + class _NotConnected(object): def __getattr__(self, attr): @@ -131,22 +137,62 @@ class MPDClient(object): } 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 + 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) 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) + 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: + 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) @@ -189,7 +235,6 @@ class MPDClient(object): while pair: yield pair pair = self._read_pair(separator) - raise StopIteration def _read_list(self): seen = None @@ -200,12 +245,10 @@ class MPDClient(object): (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 = {} @@ -215,7 +258,7 @@ class MPDClient(object): 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: @@ -224,19 +267,27 @@ class MPDClient(object): 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() @@ -293,6 +344,8 @@ class MPDClient(object): def _reset(self): self.mpd_version = None + self._iterating = False + self._pending = [] self._command_list = None self._sock = None self._rfile = _NotConnected() @@ -311,23 +364,23 @@ class MPDClient(object): 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: @@ -350,15 +403,27 @@ class MPDClient(object): self._sock.close() self._reset() + def fileno(self): + if not self._sock: + 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()