From 9aa136ff3dc89f1f9a396f8404eb4c8064fa891c Mon Sep 17 00:00:00 2001 From: Kaliko Jack Date: Wed, 10 Apr 2024 17:23:47 +0200 Subject: [PATCH] Improved exceptions doc, fixed sphinx warnings --- .gitlab-ci.yml | 2 +- CHANGES.txt | 1 + doc/source/examples.rst | 13 +++++++ doc/source/examples/connect.py | 1 - doc/source/examples/exceptions.py | 59 +++++++++++++++++++++++++++++++ doc/source/use.rst | 18 ++++++---- musicpd.py | 8 ++--- 7 files changed, 89 insertions(+), 13 deletions(-) create mode 100644 doc/source/examples/exceptions.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 90f133a..d55875f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -120,7 +120,7 @@ build_doc: 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: diff --git a/CHANGES.txt b/CHANGES.txt index 52e46d1..fe285d3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,6 +12,7 @@ Changes in 0.9.0 * 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 ---------------- diff --git a/doc/source/examples.rst b/doc/source/examples.rst index 291344c..cf4c916 100644 --- a/doc/source/examples.rst +++ b/doc/source/examples.rst @@ -42,3 +42,16 @@ A plain client monitoring changes on MPD. :language: python :linenos: +.. _exceptions_example: + +Dealing with Exceptions +----------------------- + +Musicpd module will raise it's own :py:obj:`MPDError` +exceptions **and** python :py:obj:`OSError`. Then you can wrap +:py:obj:`OSError` in :py:obj:`MPDError` exceptions to have to deal +with a single type of exceptions in your code: + +.. literalinclude:: examples/exceptions.py + :language: python + :linenos: diff --git a/doc/source/examples/connect.py b/doc/source/examples/connect.py index 0c74448..3138464 100644 --- a/doc/source/examples/connect.py +++ b/doc/source/examples/connect.py @@ -1,4 +1,3 @@ -import musicpd import logging import musicpd diff --git a/doc/source/examples/exceptions.py b/doc/source/examples/exceptions.py new file mode 100644 index 0000000..1d10c32 --- /dev/null +++ b/doc/source/examples/exceptions.py @@ -0,0 +1,59 @@ +"""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 diff --git a/doc/source/use.rst b/doc/source/use.rst index e8d6d53..88728ef 100644 --- a/doc/source/use.rst +++ b/doc/source/use.rst @@ -84,15 +84,15 @@ Default settings 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 @@ -313,15 +313,19 @@ triggering a socket timeout unless the connection is actually lost (actually it 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` method raises +:py:obj:`ConnectionError` only (an :py:obj:`MPDError` exception) but then, calling other MPD commands, the module can raise +:py:obj:`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` 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 diff --git a/musicpd.py b/musicpd.py index 2fa1233..10bcdc4 100644 --- a/musicpd.py +++ b/musicpd.py @@ -164,12 +164,12 @@ class MPDClient: :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): @@ -738,8 +738,8 @@ class MPDClient: """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 -- 2.39.2