1 # -*- coding: utf-8 -*-
3 # Copyright (c) 2010, 2011, 2013 Jack Kaliko <kaliko@azylum.org>
5 # This file is part of sima
7 # sima is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # sima is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with sima. If not, see <http://www.gnu.org/licenses/>.
21 """generic tools and utilities for sima
27 from argparse import ArgumentError, Action
28 from base64 import b64decode as push
29 from codecs import getencoder
30 from datetime import datetime
31 from os import environ, access, getcwd, W_OK, R_OK
32 from os.path import dirname, isabs, join, normpath, exists, isdir, isfile
37 Decode Obfuscated api key.
38 Only preventing API keys harvesting over the network
39 https://developer.echonest.com/forums/thread/105
41 aka = push(bytes(dic.get('apikey') + '=', 'utf-8'))
42 aka = getencoder('rot-13')(str((aka), 'utf-8'))[0]
43 dic.update({'apikey':aka})
45 def get_mpd_environ():
47 Retrieve MPD env. var.
50 mpd_host_env = environ.get('MPD_HOST')
53 # mpd_host_env = ['pass', 'host'] because MPD_HOST=pass@host
54 mpd_host_env = mpd_host_env.split('@')
55 mpd_host_env.reverse()
56 host = mpd_host_env[0]
57 if len(mpd_host_env) > 1 and mpd_host_env[1]:
58 passwd = mpd_host_env[1]
59 return (host, environ.get('MPD_PORT', None), passwd)
61 def normalize_path(path):
65 return normpath(join(getcwd(), path))
69 """Log unknown exceptions"""
71 log = logging.getLogger('sima')
72 log.error('Unhandled Exception!!!')
73 log.error(''.join(traceback.format_exc()))
74 log.info('Please report the previous message'
75 ' along with some log entries right before the crash.')
76 log.info('thanks for your help :)')
77 log.info('Quiting now!')
80 class SigHup(Exception):
84 class Obsolete(Action):
85 # pylint: disable=R0903
86 """Deal with obsolete arguments
88 def __call__(self, parser, namespace, values, option_string=None):
89 raise ArgumentError(self, 'obsolete argument')
91 class FileAction(Action):
92 """Generic class to inherit from for ArgParse action on file/dir
94 # pylint: disable=R0903
95 def __call__(self, parser, namespace, values, option_string=None):
96 self._file = normalize_path(values)
97 self._dir = dirname(self._file)
100 setattr(namespace, self.dest, self._file)
107 class Wfile(FileAction):
108 # pylint: disable=R0903
112 if not exists(self._dir):
113 #raise ArgumentError(self, '"{0}" does not exist'.format(self._dir))
114 self.parser.error('file does not exist: {0}'.format(self._dir))
115 if not exists(self._file):
116 # Is parent directory writable then
117 if not access(self._dir, W_OK):
118 self.parser.error('no write access to "{0}"'.format(self._dir))
120 if not access(self._file, W_OK):
121 self.parser.error('no write access to "{0}"'.format(self._file))
123 class Rfile(FileAction):
124 # pylint: disable=R0903
128 if not exists(self._file):
129 self.parser.error('file does not exist: {0}'.format(self._file))
130 if not isfile(self._file):
131 self.parser.error('not a file: {0}'.format(self._file))
132 if not access(self._file, R_OK):
133 self.parser.error('no read access to "{0}"'.format(self._file))
135 class Wdir(FileAction):
136 # pylint: disable=R0903
137 """Is directory writable
140 if not exists(self._file):
141 self.parser.error('directory does not exist: {0}'.format(self._file))
142 if not isdir(self._file):
143 self.parser.error('not a directory: {0}'.format(self._file))
144 if not access(self._file, W_OK):
145 self.parser.error('no write access to "{0}"'.format(self._file))
148 def __init__(self, wait):
150 self.last_called = datetime.now()
152 def __call__(self, func):
153 def wrapper(*args, **kwargs):
154 while self.last_called + self.wait > datetime.now():
156 result = func(*args, **kwargs)
157 self.last_called = datetime.now()
162 def __init__(self, elem, last=None):
164 self.requestdate = last
166 self.requestdate = datetime.utcnow()
169 return self.requestdate
175 # vim: ai ts=4 sw=4 sts=4 expandtab