annotate musixmatch-master/build/lib/musixmatch/base.py @ 13:844d341cf643 tip

Back up before ISMIR
author Yading Song <yading.song@eecs.qmul.ac.uk>
date Thu, 31 Oct 2013 13:17:06 +0000
parents 8c29444cb5fd
children
rev   line source
yading@7 1 """
yading@7 2 This module contains tha base classes for the Musixmatch API generated content:
yading@7 3
yading@7 4 * :py:class:`musixmatch.artist.Artist`
yading@7 5 * :py:class:`musixmatch.artist.Album`
yading@7 6 * :py:class:`musixmatch.track.Track`
yading@7 7 * :py:class:`musixmatch.lyrics.Lyrics`
yading@7 8 * :py:class:`musixmatch.subtitle.Subtitle`
yading@7 9 """
yading@7 10 import musixmatch
yading@7 11 __license__ = musixmatch.__license__
yading@7 12 __author__ = musixmatch.__author__
yading@7 13
yading@7 14 from musixmatch import api
yading@7 15 import pprint
yading@7 16
yading@7 17 class Base(object):
yading@7 18 """
yading@7 19 The very base (abstract) class of the musixmatch package. I want all
yading@7 20 classes to implement :py:meth:`__str__` and :py:meth:`__repr__`.
yading@7 21
yading@7 22 Casting an :py:class:`Base` into a :py:class:`str` returns a pretty printed
yading@7 23 (maybe using :py:mod:`pprint`) string representing the object.
yading@7 24
yading@7 25 Using :py:func:`repr` on an :py:class:`Base` returns an evaluable string
yading@7 26 representing the instance, whenever it is reasonable.
yading@7 27
yading@7 28 :py:class:`Base` instances are hashable.
yading@7 29 """
yading@7 30
yading@7 31 @classmethod
yading@7 32 def label(cls):
yading@7 33 """
yading@7 34 Returns the label that should be used as keyword in the
yading@7 35 :py:class:`musixmatch.api.JsonResponseMessage` body.
yading@7 36 """
yading@7 37 return getattr(cls, '__label__', cls.__name__.lower())
yading@7 38
yading@7 39 @classmethod
yading@7 40 def apiMethod(cls):
yading@7 41 """
yading@7 42 Returns the :py:class:`musixmatch.api.Method` that should be used to
yading@7 43 build the object. Defaults to *label.get* where *label* is the result
yading@7 44 from :py:meth:`label`
yading@7 45 """
yading@7 46 api_method = getattr(cls, '__api_method__', '%s.%s' % (cls.label(),'get'))
yading@7 47 if not isinstance(api_method, api.Method):
yading@7 48 api_method = api.Method(str(api_method))
yading@7 49 return api_method
yading@7 50
yading@7 51 def __str__(self):
yading@7 52 raise NotImplementedError
yading@7 53
yading@7 54 def __repr__(self):
yading@7 55 raise NotImplementedError
yading@7 56
yading@7 57 class Item(Base, dict):
yading@7 58 """
yading@7 59 This is the base class for any entity in musixmatch package. Even if
yading@7 60 response messages may have XML format, the JSON representation will be the
yading@7 61 main data format, so the :py:class:`dict` sounds like the best base class.
yading@7 62 It fetches the item data by guessing the :py:class:`musixmatch.api.Method`
yading@7 63 and building the query based on a given keyword argument. Positional
yading@7 64 argument is meant to be used by collection classes. Use only keyword
yading@7 65 arguments.
yading@7 66 """
yading@7 67 __api_method__ = None
yading@7 68
yading@7 69 def __init__(self, dictionary=None, **keywords):
yading@7 70 if dictionary:
yading@7 71 dict.update(self, dictionary)
yading@7 72 elif keywords:
yading@7 73 message = self.apiMethod()(**keywords)
yading@7 74 dict.update(self, self.fromResponseMessage(message))
yading@7 75
yading@7 76 def __str__(self):
yading@7 77 return pprint.pformat(dict(self),4,1)
yading@7 78
yading@7 79 def __repr__(self):
yading@7 80 return '%s(%r)' % (type(self).__name__, dict(self))
yading@7 81
yading@7 82 def __hash__(self):
yading@7 83 return int(self['%s_id' % self.label()])
yading@7 84
yading@7 85 @classmethod
yading@7 86 def fromResponseMessage(cls, message):
yading@7 87 """
yading@7 88 Returns an object instance, built from a
yading@7 89 :py:class:`musixmatch.api.ResponseMessage`
yading@7 90 """
yading@7 91 if not message.status_code:
yading@7 92 raise api.Error(str(message.status_code))
yading@7 93 return cls.fromDictionary(message['body'][cls.label()])
yading@7 94
yading@7 95 @classmethod
yading@7 96 def fromDictionary(cls, dictionary, **keywords):
yading@7 97 """
yading@7 98 Returns an object instance, built from a :py:class:`dict`
yading@7 99 """
yading@7 100 item = cls()
yading@7 101 dict.update(item, dictionary, **keywords)
yading@7 102 return item
yading@7 103
yading@7 104 class ItemsCollection(Base, list):
yading@7 105 """
yading@7 106 This is the base class for collections of items, like search results, or
yading@7 107 charts. It behaves like :py:class:`list`, but enforce new items to be
yading@7 108 instance of appropriate class checking against :py:meth:`allowedin`.
yading@7 109 """
yading@7 110
yading@7 111 __allowedin__ = Item
yading@7 112
yading@7 113 def __init__(self, *items):
yading@7 114 self.extend(items)
yading@7 115
yading@7 116 def __repr__(self):
yading@7 117 items = [ repr(i) for i in self ]
yading@7 118 return '%s(%s)' % (type(self).__name__, ', '.join(items))
yading@7 119
yading@7 120 def __str__(self):
yading@7 121 return list.__str__(self)
yading@7 122
yading@7 123 def __add__(self, iterable):
yading@7 124 collection = self.copy()
yading@7 125 collection.extend(iterable)
yading@7 126 return collection
yading@7 127
yading@7 128 def __iadd__(self, iterable):
yading@7 129 raise NotImplementedError
yading@7 130
yading@7 131 def __mul__(self, by):
yading@7 132 raise NotImplementedError
yading@7 133
yading@7 134 def __imul__(self, by):
yading@7 135 raise NotImplementedError
yading@7 136
yading@7 137 def __setitem__(self, key, item):
yading@7 138 raise NotImplementedError
yading@7 139
yading@7 140 def __setslice__(self, *indices):
yading@7 141 raise NotImplementedError
yading@7 142
yading@7 143 def __getslice__(self, i=0, j=-1):
yading@7 144 return self.__getitem__(slice(i,j))
yading@7 145
yading@7 146 def __getitem__(self, i):
yading@7 147 if type(i) is int:
yading@7 148 return list.__getitem__(self, i)
yading@7 149 elif type(i) is slice:
yading@7 150 collection = type(self)()
yading@7 151 list.extend(collection, list.__getitem__(self, i))
yading@7 152 return collection
yading@7 153 else:
yading@7 154 raise TypeError, i
yading@7 155
yading@7 156 def append(self, item):
yading@7 157 self.insert(len(self), item)
yading@7 158
yading@7 159 def extend(self, iterable):
yading@7 160 for item in iterable:
yading@7 161 self.append(item)
yading@7 162
yading@7 163 def count(self, item):
yading@7 164 return int(item in self)
yading@7 165
yading@7 166 def copy(self):
yading@7 167 """Returns a shallow copy of the collection."""
yading@7 168 collection = type(self)()
yading@7 169 list.extend(collection, self)
yading@7 170 return collection
yading@7 171
yading@7 172 def index(self, item, *indices):
yading@7 173 return list.index(self, item, *indices[:2])
yading@7 174
yading@7 175 def insert(self, key, item):
yading@7 176 allowed = self.allowedin()
yading@7 177 if not isinstance(item, allowed):
yading@7 178 item = allowed.fromDictionary(item)
yading@7 179 if not item in self:
yading@7 180 list.insert(self, key, item)
yading@7 181
yading@7 182 def paged(self, page_size=3):
yading@7 183 """
yading@7 184 Returns self, paged by **page_size**. That is, a list of
yading@7 185 sub-collections which contain "at most" **page_size** items.
yading@7 186 """
yading@7 187 return [ self.page(i,page_size)
yading@7 188 for i in range(self.pages(page_size)) ]
yading@7 189
yading@7 190 def page(self, page_index, page_size=3):
yading@7 191 """
yading@7 192 Returns a specific page, considering pages that contain "at most"
yading@7 193 **page_size** items.
yading@7 194 """
yading@7 195 page = type(self)()
yading@7 196 i = page_index * page_size
yading@7 197 list.extend(page, self[i:i+page_size])
yading@7 198 return page
yading@7 199
yading@7 200 def pager(self, page_size=3):
yading@7 201 """
yading@7 202 A generator of pages, considering pages that contain "at most"
yading@7 203 **page_size** items.
yading@7 204 """
yading@7 205 for i in xrange(self.pages(page_size)):
yading@7 206 yield self.page(i, page_size)
yading@7 207
yading@7 208 def pages(self,page_size=3):
yading@7 209 """
yading@7 210 Returns the number of pages, considering pages that contain "at most"
yading@7 211 **page_size** items.
yading@7 212 """
yading@7 213 pages, more = divmod(len(self), page_size)
yading@7 214 return more and pages + 1 or pages
yading@7 215
yading@7 216 @classmethod
yading@7 217 def fromResponseMessage(cls, message):
yading@7 218 """
yading@7 219 Returns an object instance, built on a
yading@7 220 :py:class:`musixmatch.api.ResponseMessage`
yading@7 221 """
yading@7 222 if not message.status_code:
yading@7 223 raise api.Error(str(message.status_code))
yading@7 224 list_label = cls.label()
yading@7 225 item_label = cls.allowedin().label()
yading@7 226 items = [ i[item_label] for i in message['body'][list_label] ]
yading@7 227 return cls(*items)
yading@7 228
yading@7 229 @classmethod
yading@7 230 def allowedin(cls):
yading@7 231 """
yading@7 232 Returns the allowed content class. Defaults to :py:class:`Item`
yading@7 233 """
yading@7 234 return cls.__allowedin__
yading@7 235
yading@7 236 @classmethod
yading@7 237 def label(cls):
yading@7 238 item_name = cls.allowedin().label()
yading@7 239 return '%s_list' % item_name
yading@7 240