]> kaliko git repositories - mpd-sima.git/blobdiff - sima/utils/utils.py
Cleanup PlayerError exception wrapper
[mpd-sima.git] / sima / utils / utils.py
index d0edcd4ab6f8ca671143df6fbfe8e0e97ac44cc0..cb672639e2e780d06187f641b9b0ac0c5d84ebe7 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
 # -*- coding: utf-8 -*-
 #
-# Copyright (c) 2010, 2011, 2013 Jack Kaliko <kaliko@azylum.org>
+# Copyright (c) 2010, 2011, 2013, 2014, 2015, 2020, 2021, 2024 kaliko <kaliko@azylum.org>
 #
 #  This file is part of sima
 #
 #
 #  This file is part of sima
 #
 #
 """generic tools and utilities for sima
 """
 #
 """generic tools and utilities for sima
 """
+# pylint: disable=C0111
 
 
+import logging
 import traceback
 import sys
 
 from argparse import ArgumentError, Action
 from base64 import b64decode as push
 from codecs import getencoder
 import traceback
 import sys
 
 from argparse import ArgumentError, Action
 from base64 import b64decode as push
 from codecs import getencoder
-from datetime import datetime, timedelta
-from os import environ, access, getcwd, W_OK, R_OK
+from datetime import datetime
+from os import getenv, access, getcwd, W_OK, R_OK
 from os.path import dirname, isabs, join, normpath, exists, isdir, isfile
 from time import sleep
 
 from os.path import dirname, isabs, join, normpath, exists, isdir, isfile
 from time import sleep
 
+from musicpd import VERSION as mversion
+from sima.info import __version__ as sversion
+
 
 def getws(dic):
     """
 
 def getws(dic):
     """
@@ -41,23 +46,37 @@ def getws(dic):
     """
     aka = push(bytes(dic.get('apikey') + '=', 'utf-8'))
     aka = getencoder('rot-13')(str((aka), 'utf-8'))[0]
     """
     aka = push(bytes(dic.get('apikey') + '=', 'utf-8'))
     aka = getencoder('rot-13')(str((aka), 'utf-8'))[0]
-    dic.update({'apikey':aka})
+    dic.update({'apikey': aka})
+
+
+def parse_mpd_host(value):
+    passwd = host = None
+    # If password is set: MPD_HOST=pass@host
+    if '@' in value:
+        mpd_host_env = value.split('@', 1)
+        if mpd_host_env[0]:
+            # A password is actually set
+            passwd = mpd_host_env[0]
+            if mpd_host_env[1]:
+                host = mpd_host_env[1]
+        elif mpd_host_env[1]:
+            # No password set but leading @ is an abstract socket
+            host = '@'+mpd_host_env[1]
+    else:
+        # MPD_HOST is a plain host
+        host = value
+    return host, passwd
+
 
 def get_mpd_environ():
     """
     Retrieve MPD env. var.
     """
     passwd = host = None
 
 def get_mpd_environ():
     """
     Retrieve MPD env. var.
     """
     passwd = host = None
-    mpd_host_env = environ.get('MPD_HOST')
-    if mpd_host_env:
-        # If password is set:
-        # mpd_host_env = ['pass', 'host'] because MPD_HOST=pass@host
-        mpd_host_env = mpd_host_env.split('@')
-        mpd_host_env.reverse()
-        host = mpd_host_env[0]
-        if len(mpd_host_env) > 1 and mpd_host_env[1]:
-            passwd = mpd_host_env[1]
-    return (host, environ.get('MPD_PORT', None), passwd)
+    if getenv('MPD_HOST'):
+        host, passwd = parse_mpd_host(getenv('MPD_HOST'))
+    return (host, getenv('MPD_PORT', None), passwd)
+
 
 def normalize_path(path):
     """Get absolute path
 
 def normalize_path(path):
     """Get absolute path
@@ -66,33 +85,20 @@ def normalize_path(path):
         return normpath(join(getcwd(), path))
     return path
 
         return normpath(join(getcwd(), path))
     return path
 
+
 def exception_log():
     """Log unknown exceptions"""
 def exception_log():
     """Log unknown exceptions"""
-    import logging
-    log = logging.getLogger('sima')
+    log = logging.getLogger(__name__)
     log.error('Unhandled Exception!!!')
     log.error(''.join(traceback.format_exc()))
     log.error('Unhandled Exception!!!')
     log.error(''.join(traceback.format_exc()))
+    log.info('musicpd python module version: %s', mversion)
+    log.info('MPD_sima version: %s', sversion)
     log.info('Please report the previous message'
              ' along with some log entries right before the crash.')
     log.info('thanks for your help :)')
     log.info('Quiting now!')
     sys.exit(1)
 
     log.info('Please report the previous message'
              ' along with some log entries right before the crash.')
     log.info('thanks for your help :)')
     log.info('Quiting now!')
     sys.exit(1)
 
-def purge_cache(obj, age=4):
-    now = datetime.utcnow()
-    if now.hour == obj.timestamp.hour:
-        return
-    obj.timestamp = datetime.utcnow()
-    cache = obj.cache
-    delta = timedelta(hours=age)
-    for url in list(cache.keys()):
-        timestamp = cache.get(url).created()
-        if now - timestamp > delta:
-            cache.pop(url)
-
-
-class SigHup(Exception):
-    pass
 
 # ArgParse Callbacks
 class Obsolete(Action):
 
 # ArgParse Callbacks
 class Obsolete(Action):
@@ -102,10 +108,11 @@ class Obsolete(Action):
     def __call__(self, parser, namespace, values, option_string=None):
         raise ArgumentError(self, 'obsolete argument')
 
     def __call__(self, parser, namespace, values, option_string=None):
         raise ArgumentError(self, 'obsolete argument')
 
+
 class FileAction(Action):
     """Generic class to inherit from for ArgParse action on file/dir
     """
 class FileAction(Action):
     """Generic class to inherit from for ArgParse action on file/dir
     """
-    # pylint: disable=R0903
+    # pylint: disable=R0903,W0201
     def __call__(self, parser, namespace, values, option_string=None):
         self._file = normalize_path(values)
         self._dir = dirname(self._file)
     def __call__(self, parser, namespace, values, option_string=None):
         self._file = normalize_path(values)
         self._dir = dirname(self._file)
@@ -116,23 +123,25 @@ class FileAction(Action):
     def checks(self):
         """control method
         """
     def checks(self):
         """control method
         """
-        pass
+
 
 class Wfile(FileAction):
     # pylint: disable=R0903
     """Is file writable
     """
     def checks(self):
 
 class Wfile(FileAction):
     # pylint: disable=R0903
     """Is file writable
     """
     def checks(self):
+        if isdir(self._file):
+            self.parser.error(f'need a file not a directory: {self._file}')
         if not exists(self._dir):
         if not exists(self._dir):
-            #raise ArgumentError(self, '"{0}" does not exist'.format(self._dir))
-            self.parser.error('file does not exist: {0}'.format(self._dir))
+            self.parser.error(f'directory does not exist: {self._dir}')
         if not exists(self._file):
             # Is parent directory writable then
             if not access(self._dir, W_OK):
         if not exists(self._file):
             # Is parent directory writable then
             if not access(self._dir, W_OK):
-                self.parser.error('no write access to "{0}"'.format(self._dir))
+                self.parser.error(f'no write access to "{self._dir}"')
         else:
             if not access(self._file, W_OK):
         else:
             if not access(self._file, W_OK):
-                self.parser.error('no write access to "{0}"'.format(self._file))
+                self.parser.error(f'no write access to "{self._file}"')
+
 
 class Rfile(FileAction):
     # pylint: disable=R0903
 
 class Rfile(FileAction):
     # pylint: disable=R0903
@@ -140,11 +149,12 @@ class Rfile(FileAction):
     """
     def checks(self):
         if not exists(self._file):
     """
     def checks(self):
         if not exists(self._file):
-            self.parser.error('file does not exist: {0}'.format(self._file))
+            self.parser.error(f'file does not exist: {self._file}')
         if not isfile(self._file):
         if not isfile(self._file):
-            self.parser.error('not a file: {0}'.format(self._file))
+            self.parser.error(f'not a file: {self._file}')
         if not access(self._file, R_OK):
         if not access(self._file, R_OK):
-            self.parser.error('no read access to "{0}"'.format(self._file))
+            self.parser.error(f'no read access to "{self._file}"')
+
 
 class Wdir(FileAction):
     # pylint: disable=R0903
 
 class Wdir(FileAction):
     # pylint: disable=R0903
@@ -152,13 +162,16 @@ class Wdir(FileAction):
     """
     def checks(self):
         if not exists(self._file):
     """
     def checks(self):
         if not exists(self._file):
-            self.parser.error('directory does not exist: {0}'.format(self._file))
+            self.parser.error(f'directory does not exist: {self._file}')
         if not isdir(self._file):
         if not isdir(self._file):
-            self.parser.error('not a directory: {0}'.format(self._file))
+            self.parser.error(f'not a directory: {self._file}')
         if not access(self._file, W_OK):
         if not access(self._file, W_OK):
-            self.parser.error('no write access to "{0}"'.format(self._file))
+            self.parser.error(f'no write access to "{self._file}"')
 
 
-class Throttle():
+
+class Throttle:
+    """throttle decorator"""
+    # pylint: disable=R0903
     def __init__(self, wait):
         self.wait = wait
         self.last_called = datetime.now()
     def __init__(self, wait):
         self.wait = wait
         self.last_called = datetime.now()
@@ -172,34 +185,50 @@ class Throttle():
             return result
         return wrapper
 
             return result
         return wrapper
 
-class Cache():
-    def __init__(self, elem, last=None):
-        self.elem = elem
-        self.requestdate = last
-        if not last:
-            self.requestdate = datetime.utcnow()
 
 
-    def created(self):
-        return self.requestdate
+class MPDSimaException(Exception):
+    """Generic MPD_sima Exception"""
 
 
-    def get(self):
-        return self.elem
 
 
+class SigHup(MPDSimaException):
+    """SIGHUP raises this Exception"""
 
 
-# http client exceptions (for webservices)
 
 
-class WSError(Exception):
+# http client exceptions (for webservices)
+class WSError(MPDSimaException):
     pass
 
     pass
 
+
 class WSNotFound(WSError):
     pass
 
 class WSNotFound(WSError):
     pass
 
+
 class WSTimeout(WSError):
     pass
 
 class WSTimeout(WSError):
     pass
 
+
 class WSHTTPError(WSError):
     pass
 
 
 class WSHTTPError(WSError):
     pass
 
 
+class PluginException(MPDSimaException):
+    pass
+
+
+# Wrap Exception decorator
+def get_decorator(errors=(OSError, TimeoutError), wrap_into=Exception):
+    def decorator(func):
+        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 wrap_into(strerr) from err
+        return w_func
+    return decorator
+
 # VIM MODLINE
 # vim: ai ts=4 sw=4 sts=4 expandtab
 # VIM MODLINE
 # vim: ai ts=4 sw=4 sts=4 expandtab