stage: build
script:
- pip install sphinx sphinx_rtd_theme
- - sphinx-build doc/source -b html ./html -D html_theme=sphinx_rtd_theme
+ - sphinx-build doc/source -b html ./html -D html_theme=sphinx_rtd_theme -E -W -n --keep-going
rules:
- if: $CI_PIPELINE_SOURCE == "push"
changes:
* Switch to pyproject.toml (setuptools build system)
* The connect sequence raises ConnectionError only on error,
previously getaddrinfo or unix socket connection error raised an OSError
+ * Improved documentation, add examples
Changes in 0.8.0
----------------
:language: python
:linenos:
+.. _exceptions_example:
+
+Dealing with Exceptions
+-----------------------
+
+Musicpd module will raise it's own :py:obj:`MPDError<musicpd.MPDError>`
+exceptions **and** python :py:obj:`OSError`. Then you can wrap
+:py:obj:`OSError` in :py:obj:`MPDError<musicpd.MPDError>` exceptions to have to deal
+with a single type of exceptions in your code:
+
+.. literalinclude:: examples/exceptions.py
+ :language: python
+ :linenos:
-import musicpd
import logging
import musicpd
--- /dev/null
+"""client class dealing with all Exceptions
+"""
+import logging
+
+import musicpd
+
+
+# Wrap Exception decorator
+def wrapext(func):
+ """Decorator to wrap errors in musicpd.MPDError"""
+ errors=(OSError, TimeoutError)
+ into = musicpd.MPDError
+ def w_func(*args, **kwargs):
+ try:
+ return func(*args, **kwargs)
+ except errors as err:
+ strerr = str(err)
+ if hasattr(err, 'strerror'):
+ if err.strerror:
+ strerr = err.strerror
+ raise into(strerr) from err
+ return w_func
+
+
+class MyClient(musicpd.MPDClient):
+ """Plain client inheriting from MPDClient"""
+
+ def __init__(self):
+ # Set logging to debug level
+ logging.basicConfig(level=logging.DEBUG,
+ format='%(levelname)-8s %(module)-10s %(message)s')
+ self.log = logging.getLogger(__name__)
+ super().__init__()
+
+ @wrapext
+ def __getattr__(self, cmd):
+ """Wrapper around MPDClient calls for abstract overriding"""
+ self.log.debug('cmd: %s', cmd)
+ return super().__getattr__(cmd)
+
+
+if __name__ == '__main__':
+ cli = MyClient()
+ # You can overrides host here or in init
+ #cli.host = 'example.org'
+ # Connect MPD server
+ try:
+ cli.connect()
+ cli.currentsong()
+ cli.stats()
+ except musicpd.MPDError as error:
+ cli.log.fatal(error)
+ finally:
+ cli.log.info('Disconnecting')
+ try:
+ # Tries to close the socket anyway
+ cli.disconnect()
+ except OSError:
+ pass
Default host:
* use :envvar:`MPD_HOST` environment variable if set, extract password if present,
- * else looks for an existing file in :envvar:`${XDG_RUNTIME_DIR:-/run/}/mpd/socket`
+ * else use :envvar:`XDG_RUNTIME_DIR` to looks for an existing file in ``${XDG_RUNTIME_DIR}/mpd/socket``, :envvar:`XDG_RUNTIME_DIR` defaults to ``/run`` if not set.
* else set host to ``localhost``
Default port:
- * use :envvar:`MPD_PORT` environment variable is set
+ * use :envvar:`MPD_PORT` environment variable if set
* else use ``6600``
Default timeout:
- * use :envvar:`MPD_TIMEOUT` is set
+ * use :envvar:`MPD_TIMEOUT` if set
* else use :py:obj:`musicpd.CONNECTION_TIMEOUT`
Context manager
could also be that MPD took too much time to answer, but MPD taking more than a
couple of seconds for these commands should never occur).
+.. _exceptions:
Exceptions
----------
-The :py:obj:`musicpd.MPDClient.connect` method raises
-:py:obj:`musicpd.ConnectionError` only but then, calling other MPD commands,
-the module can raise :py:obj:`musicpd.MPDError` or an :py:obj:`OSError` depending on the error and where it occurs.
+The :py:obj:`connect<musicpd.MPDClient.connect>` method raises
+:py:obj:`ConnectionError<musicpd.ConnectionError>` only (an :py:obj:`MPDError<musicpd.MPDError>` exception) but then, calling other MPD commands, the module can raise
+:py:obj:`MPDError<musicpd.MPDError>` or an :py:obj:`OSError` depending on the error and
+where it occurs.
-Using musicpd module both :py:obj:`musicpd.MPDError` and :py:obj:`OSError` exceptions families are expected.
+Then using musicpd module both :py:obj:`musicpd.MPDError` and :py:obj:`OSError`
+exceptions families are expected, see :ref:`examples<exceptions_example>` for a
+way to deal with this.
.. _MPD protocol documentation: http://www.musicpd.org/doc/protocol/
.. _snake case: https://en.wikipedia.org/wiki/Snake_case
:py:attr:`musicpd.MPDClient.port` keep track of current connection
host and port, :py:attr:`musicpd.MPDClient.pwd` is set once with
password extracted from environment variable.
- Calling :py:meth:`password` methode with a new password
+ Calling MPS's password method with a new password
won't update :py:attr:`musicpd.MPDClient.pwd` value.
Moreover, :py:attr:`musicpd.MPDClient.pwd` is only an helper attribute
exposing password extracted from :envvar:`MPD_HOST` environment variable, it
- will not be used as default value for the :py:meth:`password` method
+ will not be used as default value for the MPD's password command.
"""
def __init__(self):
"""Socket timeout in second (defaults to :py:obj:`SOCKET_TIMEOUT`).
Use :py:obj:`None` to disable socket timout.
- :setter: Set the socket timeout
- :type: int or None (integer > 0)
+ :setter: Set the socket timeout (integer > 0)
+ :type: int or None
"""
return self._socket_timeout