1 # -*- coding: utf-8 -*-
3 # Copyright (c) 2010, 2011, 2013, 2014 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
23 # pylint: disable=C0111
28 from argparse import ArgumentError, Action
29 from base64 import b64decode as push
30 from codecs import getencoder
31 from datetime import datetime
32 from os import environ, access, getcwd, W_OK, R_OK
33 from os.path import dirname, isabs, join, normpath, exists, isdir, isfile
34 from time import sleep
39 Decode Obfuscated api key.
40 Only preventing API keys harvesting over the network
41 https://developer.echonest.com/forums/thread/105
43 aka = push(bytes(dic.get('apikey') + '=', 'utf-8'))
44 aka = getencoder('rot-13')(str((aka), 'utf-8'))[0]
45 dic.update({'apikey':aka})
47 def get_mpd_environ():
49 Retrieve MPD env. var.
52 mpd_host_env = environ.get('MPD_HOST')
55 # mpd_host_env = ['pass', 'host'] because MPD_HOST=pass@host
56 mpd_host_env = mpd_host_env.split('@')
57 mpd_host_env.reverse()
58 host = mpd_host_env[0]
59 if len(mpd_host_env) > 1 and mpd_host_env[1]:
60 passwd = mpd_host_env[1]
61 return (host, environ.get('MPD_PORT', None), passwd)
63 def normalize_path(path):
67 return normpath(join(getcwd(), path))
71 """Log unknown exceptions"""
73 log = logging.getLogger('sima')
74 log.error('Unhandled Exception!!!')
75 log.error(''.join(traceback.format_exc()))
76 log.info('Please report the previous message'
77 ' along with some log entries right before the crash.')
78 log.info('thanks for your help :)')
79 log.info('Quiting now!')
83 class SigHup(Exception):
84 """SIGHUP raises this Exception"""
88 class Obsolete(Action):
89 # pylint: disable=R0903
90 """Deal with obsolete arguments
92 def __call__(self, parser, namespace, values, option_string=None):
93 raise ArgumentError(self, 'obsolete argument')
95 class FileAction(Action):
96 """Generic class to inherit from for ArgParse action on file/dir
98 # pylint: disable=R0903
99 def __call__(self, parser, namespace, values, option_string=None):
100 self._file = normalize_path(values)
101 self._dir = dirname(self._file)
104 setattr(namespace, self.dest, self._file)
111 class Wfile(FileAction):
112 # pylint: disable=R0903
116 if not exists(self._dir):
117 #raise ArgumentError(self, '"{0}" does not exist'.format(self._dir))
118 self.parser.error('file does not exist: {0}'.format(self._dir))
119 if not exists(self._file):
120 # Is parent directory writable then
121 if not access(self._dir, W_OK):
122 self.parser.error('no write access to "{0}"'.format(self._dir))
124 if not access(self._file, W_OK):
125 self.parser.error('no write access to "{0}"'.format(self._file))
127 class Rfile(FileAction):
128 # pylint: disable=R0903
132 if not exists(self._file):
133 self.parser.error('file does not exist: {0}'.format(self._file))
134 if not isfile(self._file):
135 self.parser.error('not a file: {0}'.format(self._file))
136 if not access(self._file, R_OK):
137 self.parser.error('no read access to "{0}"'.format(self._file))
139 class Wdir(FileAction):
140 # pylint: disable=R0903
141 """Is directory writable
144 if not exists(self._file):
145 self.parser.error('directory does not exist: {0}'.format(self._file))
146 if not isdir(self._file):
147 self.parser.error('not a directory: {0}'.format(self._file))
148 if not access(self._file, W_OK):
149 self.parser.error('no write access to "{0}"'.format(self._file))
152 """throttle decorator"""
153 def __init__(self, wait):
155 self.last_called = datetime.now()
157 def __call__(self, func):
158 def wrapper(*args, **kwargs):
159 while self.last_called + self.wait > datetime.now():
161 result = func(*args, **kwargs)
162 self.last_called = datetime.now()
166 # http client exceptions (for webservices)
168 class WSError(Exception):
171 class WSNotFound(WSError):
174 class WSTimeout(WSError):
177 class WSHTTPError(WSError):
182 # vim: ai ts=4 sw=4 sts=4 expandtab