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, timedelta
31 from os import environ, access, getcwd, W_OK, R_OK
32 from os.path import dirname, isabs, join, normpath, exists, isdir, isfile
33 from time import sleep
38 Decode Obfuscated api key.
39 Only preventing API keys harvesting over the network
40 https://developer.echonest.com/forums/thread/105
42 aka = push(bytes(dic.get('apikey') + '=', 'utf-8'))
43 aka = getencoder('rot-13')(str((aka), 'utf-8'))[0]
44 dic.update({'apikey':aka})
46 def get_mpd_environ():
48 Retrieve MPD env. var.
51 mpd_host_env = environ.get('MPD_HOST')
54 # mpd_host_env = ['pass', 'host'] because MPD_HOST=pass@host
55 mpd_host_env = mpd_host_env.split('@')
56 mpd_host_env.reverse()
57 host = mpd_host_env[0]
58 if len(mpd_host_env) > 1 and mpd_host_env[1]:
59 passwd = mpd_host_env[1]
60 return (host, environ.get('MPD_PORT', None), passwd)
62 def normalize_path(path):
66 return normpath(join(getcwd(), path))
70 """Log unknown exceptions"""
72 log = logging.getLogger('sima')
73 log.error('Unhandled Exception!!!')
74 log.error(''.join(traceback.format_exc()))
75 log.info('Please report the previous message'
76 ' along with some log entries right before the crash.')
77 log.info('thanks for your help :)')
78 log.info('Quiting now!')
81 def purge_cache(obj, age=4):
82 now = datetime.utcnow()
83 if now.hour == obj.timestamp.hour:
85 obj.timestamp = datetime.utcnow()
87 delta = timedelta(hours=age)
88 for url in list(cache.keys()):
89 timestamp = cache.get(url).created()
90 if now - timestamp > delta:
94 class SigHup(Exception):
98 class Obsolete(Action):
99 # pylint: disable=R0903
100 """Deal with obsolete arguments
102 def __call__(self, parser, namespace, values, option_string=None):
103 raise ArgumentError(self, 'obsolete argument')
105 class FileAction(Action):
106 """Generic class to inherit from for ArgParse action on file/dir
108 # pylint: disable=R0903
109 def __call__(self, parser, namespace, values, option_string=None):
110 self._file = normalize_path(values)
111 self._dir = dirname(self._file)
114 setattr(namespace, self.dest, self._file)
121 class Wfile(FileAction):
122 # pylint: disable=R0903
126 if not exists(self._dir):
127 #raise ArgumentError(self, '"{0}" does not exist'.format(self._dir))
128 self.parser.error('file does not exist: {0}'.format(self._dir))
129 if not exists(self._file):
130 # Is parent directory writable then
131 if not access(self._dir, W_OK):
132 self.parser.error('no write access to "{0}"'.format(self._dir))
134 if not access(self._file, W_OK):
135 self.parser.error('no write access to "{0}"'.format(self._file))
137 class Rfile(FileAction):
138 # pylint: disable=R0903
142 if not exists(self._file):
143 self.parser.error('file does not exist: {0}'.format(self._file))
144 if not isfile(self._file):
145 self.parser.error('not a file: {0}'.format(self._file))
146 if not access(self._file, R_OK):
147 self.parser.error('no read access to "{0}"'.format(self._file))
149 class Wdir(FileAction):
150 # pylint: disable=R0903
151 """Is directory writable
154 if not exists(self._file):
155 self.parser.error('directory does not exist: {0}'.format(self._file))
156 if not isdir(self._file):
157 self.parser.error('not a directory: {0}'.format(self._file))
158 if not access(self._file, W_OK):
159 self.parser.error('no write access to "{0}"'.format(self._file))
162 def __init__(self, wait):
164 self.last_called = datetime.now()
166 def __call__(self, func):
167 def wrapper(*args, **kwargs):
168 while self.last_called + self.wait > datetime.now():
170 result = func(*args, **kwargs)
171 self.last_called = datetime.now()
176 def __init__(self, elem, last=None):
178 self.requestdate = last
180 self.requestdate = datetime.utcnow()
183 return self.requestdate
189 # http client exceptions (for webservices)
191 class WSError(Exception):
194 class WSNotFound(WSError):
197 class WSTimeout(WSError):
200 class WSHTTPError(WSError):
205 # vim: ai ts=4 sw=4 sts=4 expandtab