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