X-Git-Url: https://git.kaliko.me/?a=blobdiff_plain;f=musicpd.py;h=f72353e762d066d2f263cb95a6b934424c12d8d1;hb=1273e7e588476940dd5e9d862be7f18de0db708b;hp=1a7fd9759b290ab5db8e4549689e8343dc7cbc5f;hpb=966e3a848b9d545530cc7c7a78c5185c69183b22;p=python-musicpd.git diff --git a/musicpd.py b/musicpd.py index 1a7fd97..f72353e 100644 --- a/musicpd.py +++ b/musicpd.py @@ -1,6 +1,7 @@ # python-musicpd: Python MPD client library # Copyright (C) 2008-2010 J. Alexander Treuman -# Copyright (C) 2012-2018 Kaliko Jack +# Copyright (C) 2012-2019 Kaliko Jack +# Copyright (C) 2019 Naglis Jonaitis # # python-musicpd is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by @@ -27,7 +28,7 @@ HELLO_PREFIX = "OK MPD " ERROR_PREFIX = "ACK " SUCCESS = "OK" NEXT = "list_OK" -VERSION = '0.4.3' +VERSION = '0.4.4' def iterator_wrapper(func): @@ -38,6 +39,7 @@ def iterator_wrapper(func): if not instance.iterate: return list(generator) instance._iterating = True + def iterator(gen): try: for item in gen: @@ -51,24 +53,31 @@ def iterator_wrapper(func): class MPDError(Exception): pass + class ConnectionError(MPDError): pass + class ProtocolError(MPDError): pass + class CommandError(MPDError): pass + class CommandListError(MPDError): pass + class PendingCommandError(MPDError): pass + class IteratingError(MPDError): pass + class Range: def __init__(self, tpl): @@ -96,17 +105,34 @@ class Range: except (TypeError, ValueError): raise CommandError('Not a tuple of int') + class _NotConnected: + def __getattr__(self, attr): return self._dummy def _dummy(*args): raise ConnectionError("Not connected") + class MPDClient: - """MPDClient instance will look for ``MPD_HOST``/``MPD_PORT`` environment - variables and set instance attribute ``host``, ``port`` and ``password`` + + """MPDClient instance will look for ``MPD_HOST``/``MPD_PORT``/``XDG_RUNTIME_DIR`` environment + variables and set instance attribute ``host``, ``port`` and ``pwd`` accordingly. + + Then :py:obj:`musicpd.MPDClient.connect` will use ``host`` and ``port`` as defaults if not provided as args. + + Cf. :py:obj:`musicpd.MPDClient.connect` for details. + + >>> from os import environ + >>> environ['MPD_HOST'] = 'pass@mpdhost' + >>> cli = musicpd.MPDClient() + >>> cli.pwd == environ['MPD_HOST'].split('@')[0] + True + >>> cli.host == environ['MPD_HOST'].split('@')[1] + True + >>> # cli.connect() will use host/port as set in MPD_HOST/MPD_PORT """ def __init__(self): @@ -207,6 +233,10 @@ class MPDClient: "kill": None, "password": self._fetch_nothing, "ping": self._fetch_nothing, + # Partition Commands + "partition": self._fetch_nothing, + "listpartitions": self._fetch_list, + "newpartition": self._fetch_nothing, # Audio Output Commands "disableoutput": self._fetch_nothing, "enableoutput": self._fetch_nothing, @@ -235,7 +265,7 @@ class MPDClient: else use MPD_HOST=${XDG_RUNTIME_DIR:-/run/}/mpd/socket if file exists """ self.host = 'localhost' - self.password = None + self.pwd = None self.port = os.environ.get('MPD_PORT', '6600') mpd_host_env = os.environ.get('MPD_HOST') if mpd_host_env: @@ -245,7 +275,7 @@ class MPDClient: mpd_host_env.reverse() self.host = mpd_host_env[0] if len(mpd_host_env) > 1 and mpd_host_env[1]: - self.password = mpd_host_env[1] + self.pwd = mpd_host_env[1] else: # Is socket there xdg_runtime_dir = os.environ.get('XDG_RUNTIME_DIR', '/run') @@ -304,13 +334,13 @@ class MPDClient: raise IteratingError("Cannot execute '%s' while iterating" % command) if self._pending: - raise PendingCommandError("Cannot execute '%s' with " - "pending commands" % command) + 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) + raise CommandListError( + "'%s' not allowed in command list" % command) self._write_command(command, args) self._command_list.append(retval) else: @@ -489,8 +519,8 @@ class MPDClient: def _connect_unix(self, path): if not hasattr(socket, "AF_UNIX"): - raise ConnectionError("Unix domain sockets not supported " - "on this platform") + raise ConnectionError( + "Unix domain sockets not supported on this platform") sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.connect(path) return sock @@ -522,7 +552,8 @@ class MPDClient: def noidle(self): # noidle's special case if not self._pending or self._pending[0] != 'idle': - raise CommandError('cannot send noidle if send_idle was not called') + raise CommandError( + 'cannot send noidle if send_idle was not called') del self._pending[0] self._write_command("noidle") return self._fetch_list() @@ -538,9 +569,9 @@ class MPDClient: .. note:: Default host/port If host evaluate to :py:obj:`False` - * if ``MPD_HOST`` env. var. is set, use it for host + * use ``MPD_HOST`` env. var. if set, extract password if present, * else looks for a existing file in ``${XDG_RUNTIME_DIR:-/run/}/mpd/socket`` - * finally set host to ``localhost`` + * else set host to ``localhost`` If port evaluate to :py:obj:`False` * if ``MPD_PORT`` env. var. is set, use it for port @@ -566,8 +597,8 @@ class MPDClient: def disconnect(self): """Closes the MPD connection. - The client closes the actual socket and not using the - 'close' request from MPD protocol as suggested in documentation. + The client closes the actual socket, it does not use the + 'close' request from MPD protocol (as suggested in documentation). """ if hasattr(self._rfile, 'close'): self._rfile.close()