# -*- coding: utf-8 -*-
#
-# Copyright (c) 2010, 2011, 2013 Jack Kaliko <kaliko@azylum.org>
+# Copyright (c) 2010, 2011, 2013, 2014, 2015, 2020, 2021 kaliko <kaliko@azylum.org>
#
# This file is part of 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
-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 musicpd import VERSION as mversion
+from sima.info import __version__ as sversion
+
def getws(dic):
"""
"""
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
- 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
return normpath(join(getcwd(), path))
return path
+
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.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)
-def purge_cache(obj, age=4):
- """purge old entries in http client cache
- """
- 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):
- """SIGHUP raises this Exception"""
- pass
# ArgParse Callbacks
class Obsolete(Action):
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
"""
- # 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 checks(self):
"""control method
"""
- pass
+
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):
- #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):
- 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):
- 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
"""
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):
- 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):
- 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
"""
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):
- 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):
- self.parser.error('no write access to "{0}"'.format(self._file))
+ self.parser.error(f'no write access to "{self._file}"')
+
class Throttle:
"""throttle decorator"""
+ # pylint: disable=R0903
def __init__(self, wait):
self.wait = wait
self.last_called = datetime.now()
return result
return wrapper
-class Cache:
- """Plain cache object"""
- 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
+
class WSNotFound(WSError):
pass
+
class WSTimeout(WSError):
pass
+
class WSHTTPError(WSError):
pass
+class PluginException(MPDSimaException):
+ pass
+
# VIM MODLINE
# vim: ai ts=4 sw=4 sts=4 expandtab