* converted all mixin classes to class decorator functions

* renamed all mixin modules to avoid name conflict
* removed mixin classes from class inheritence list and put then as class decorators
master
Abhinav Sarkar 2009-03-31 03:39:23 +00:00
parent d709434a7b
commit c36d689690
21 changed files with 585 additions and 547 deletions

View File

@ -7,10 +7,13 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixins import Cacheable, Searchable, Taggable from lastfm.mixins import cacheable, searchable, taggable
from lastfm.decorators import cached_property, top_property from lastfm.decorators import cached_property, top_property
class Album(LastfmBase, Cacheable, Searchable, Taggable): @taggable
@searchable
@cacheable
class Album(LastfmBase):
"""A class representing an album.""" """A class representing an album."""
def init(self, def init(self,
api, api,
@ -58,7 +61,6 @@ class Album(LastfmBase, Cacheable, Searchable, Taggable):
""" """
if not isinstance(api, Api): if not isinstance(api, Api):
raise InvalidParametersError("api reference must be supplied as an argument") raise InvalidParametersError("api reference must be supplied as an argument")
Taggable.init(self, api)
self._api = api self._api = api
self._name = name self._name = name
self._artist = artist self._artist = artist

View File

@ -7,10 +7,15 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixins import Cacheable, Searchable, Sharable, Shoutable, Taggable from lastfm.mixins import cacheable, searchable, sharable, shoutable, taggable
from lastfm.decorators import cached_property, top_property from lastfm.decorators import cached_property, top_property
class Artist(LastfmBase, Cacheable, Sharable, Shoutable, Searchable, Taggable): @shoutable
@sharable
@taggable
@searchable
@cacheable
class Artist(LastfmBase):
"""A class representing an artist.""" """A class representing an artist."""
def init(self, def init(self,
api, api,
@ -55,9 +60,6 @@ class Artist(LastfmBase, Cacheable, Sharable, Shoutable, Searchable, Taggable):
""" """
if not isinstance(api, Api): if not isinstance(api, Api):
raise InvalidParametersError("api reference must be supplied as an argument") raise InvalidParametersError("api reference must be supplied as an argument")
Sharable.init(self, api)
Shoutable.init(self, api)
Taggable.init(self, api)
self._api = api self._api = api
self._name = name self._name = name
@ -402,16 +404,16 @@ class Artist(LastfmBase, Cacheable, Sharable, Shoutable, Searchable, Taggable):
listeners = int(data.findtext('stats/listeners')), listeners = int(data.findtext('stats/listeners')),
playcount = int(data.findtext('stats/playcount')) playcount = int(data.findtext('stats/playcount'))
) )
self._similar = [ # self._similar = [
Artist( # Artist(
self._api, # self._api,
subject = self, # subject = self,
name = a.findtext('name'), # name = a.findtext('name'),
url = a.findtext('url'), # url = a.findtext('url'),
image = dict([(i.get('size'), i.text) for i in a.findall('image')]) # image = dict([(i.get('size'), i.text) for i in a.findall('image')])
) # )
for a in data.findall('similar/artist') # for a in data.findall('similar/artist')
] # ]
self._top_tags = [ self._top_tags = [
Tag( Tag(
self._api, self._api,

View File

@ -6,10 +6,11 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixins import Cacheable from lastfm.mixins import cacheable
from operator import xor from operator import xor
class Chart(LastfmBase, Cacheable): @cacheable
class Chart(LastfmBase):
"""The base class for all the chart classes""" """The base class for all the chart classes"""
def init(self, subject, start, end, stats = None): def init(self, subject, start, end, stats = None):
@ -435,7 +436,7 @@ class RollingChart(Chart):
end = end, end = end,
stats = Stats( stats = Stats(
subject = subject, subject = subject,
**{count_attribute[1:]: sum([a.stats.__dict__[count_attribute] for a in items])} **{count_attribute[1:]: sum(a.stats.__dict__[count_attribute] for a in items)}
), ),
**{"%ss" % chart_type: items} **{"%ss" % chart_type: items}
) )
@ -467,7 +468,7 @@ class RollingTagChart(TagChart):
key_func = lambda tag: tag.name key_func = lambda tag: tag.name
chart = super(cls.mro()[3], cls).create_from_data( chart = super(cls.mro()[3], cls).create_from_data(
subject, key_func, start, end) subject, key_func, start, end)
count_sum = sum([t.stats.count for t in chart.tags]) count_sum = sum(t.stats.count for t in chart.tags)
for t in chart.tags: for t in chart.tags:
t.stats.__dict__['_count'] /= count_sum t.stats.__dict__['_count'] /= count_sum
return chart return chart

View File

@ -7,9 +7,12 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixins import Cacheable, Sharable, Shoutable from lastfm.mixins import cacheable, sharable, shoutable
class Event(LastfmBase, Cacheable, Sharable, Shoutable): @shoutable
@sharable
@cacheable
class Event(LastfmBase):
"""A class representing an event.""" """A class representing an event."""
STATUS_ATTENDING = 0 STATUS_ATTENDING = 0
STATUS_MAYBE = 1 STATUS_MAYBE = 1
@ -59,8 +62,6 @@ class Event(LastfmBase, Cacheable, Sharable, Shoutable):
""" """
if not isinstance(api, Api): if not isinstance(api, Api):
raise InvalidParametersError("api reference must be supplied as an argument") raise InvalidParametersError("api reference must be supplied as an argument")
Sharable.init(self, api)
Shoutable.init(self, api)
self._api = api self._api = api
self._id = id self._id = id

View File

@ -7,7 +7,7 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixins import Cacheable from lastfm.mixins import cacheable
from lastfm.decorators import cached_property, top_property, depaginate from lastfm.decorators import cached_property, top_property, depaginate
class Geo(object): class Geo(object):
@ -145,7 +145,8 @@ class Geo(object):
for t in data.findall('track') for t in data.findall('track')
] ]
class Location(LastfmBase, Cacheable): @cacheable
class Location(LastfmBase):
"""A class representing a location of an event""" """A class representing a location of an event"""
XMLNS = "http://www.w3.org/2003/01/geo/wgs84_pos#" XMLNS = "http://www.w3.org/2003/01/geo/wgs84_pos#"
@ -324,7 +325,8 @@ class Location(LastfmBase, Cacheable):
else: else:
return "<lastfm.geo.Location: %s>" % self.city return "<lastfm.geo.Location: %s>" % self.city
class Country(LastfmBase, Cacheable): @cacheable
class Country(LastfmBase):
"""A class representing a country.""" """A class representing a country."""
ISO_CODES = { ISO_CODES = {
'AD': 'Andorra', 'AD': 'Andorra',

View File

@ -7,13 +7,12 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixins import ( from lastfm.mixins import cacheable, chartable
Cacheable, AlbumChartable, ArtistChartable,
TrackChartable, TagChartable)
from lastfm.decorators import cached_property, depaginate from lastfm.decorators import cached_property, depaginate
class Group(LastfmBase, Cacheable, AlbumChartable, @chartable(['album', 'artist', 'track', 'tag'])
ArtistChartable, TrackChartable, TagChartable): @cacheable
class Group(LastfmBase):
"""A class representing a group on last.fm.""" """A class representing a group on last.fm."""
def init(self, api, name = None, **kwargs): def init(self, api, name = None, **kwargs):
""" """
@ -29,11 +28,7 @@ class Group(LastfmBase, Cacheable, AlbumChartable,
""" """
if not isinstance(api, Api): if not isinstance(api, Api):
raise InvalidParametersError("api reference must be supplied as an argument") raise InvalidParametersError("api reference must be supplied as an argument")
AlbumChartable.init(self, api)
ArtistChartable.init(self, api)
TrackChartable.init(self, api)
TagChartable.init(self, api)
self._api = api self._api = api
self._name = name self._name = name

View File

@ -5,13 +5,12 @@ __version__ = "0.2"
__license__ = "GNU Lesser General Public License" __license__ = "GNU Lesser General Public License"
__package__ = "lastfm.mixins" __package__ = "lastfm.mixins"
from lastfm.mixins.cacheable import Cacheable from lastfm.mixins._cacheable import cacheable
from lastfm.mixins.searchable import Searchable from lastfm.mixins._searchable import searchable
from lastfm.mixins.sharable import Sharable from lastfm.mixins._sharable import sharable
from lastfm.mixins.shoutable import Shoutable from lastfm.mixins._shoutable import shoutable
from lastfm.mixins.taggable import Taggable from lastfm.mixins._taggable import taggable
from lastfm.mixins.chartable import ( from lastfm.mixins._chartable import chartable
AlbumChartable, ArtistChartable, TrackChartable, TagChartable)
__all__ = ['Cacheable', 'Searchable', 'Sharable', 'Shoutable', 'Taggable' __all__ = ['cacheable', 'searchable', 'sharable', 'shoutable', 'taggable'
'AlbumChartable', 'ArtistChartable', 'TrackChartable', 'TagChartable'] 'chartable']

View File

@ -10,11 +10,25 @@ try:
except ImportError: except ImportError:
from dummy_threading import Lock from dummy_threading import Lock
class Cacheable(object): registry = {}
registry = {} _lock = Lock()
_lock = Lock()
def register(ob, key):
if not ob.__class__ in registry:
registry[ob.__class__] = {}
if key in registry[ob.__class__]:
ob = registry[ob.__class__][key]
#print "already registered: %s" % repr(ob)
return (ob, True)
else:
#print "not already registered: %s" % ob.__class__
registry[ob.__class__][key] = ob
return (ob, False)
def cacheable(cls):
@classmethod
def __new__(cls, *args, **kwds): def __new__(cls, *args, **kwds):
args = args[1:]
subject = None subject = None
if 'subject' in kwds and not cls.__name__.startswith('Weekly'): if 'subject' in kwds and not cls.__name__.startswith('Weekly'):
subject = kwds['subject'] subject = kwds['subject']
@ -29,29 +43,19 @@ class Cacheable(object):
key = cls._hash_func(*args, **kwds) key = cls._hash_func(*args, **kwds)
if subject is not None: if subject is not None:
key = (hash(subject), key) key = (hash(subject), key)
Cacheable._lock.acquire() with _lock:
try: inst, already_registered = register(object.__new__(cls), key)
inst, already_registered = Cacheable.register(object.__new__(cls), key)
if not already_registered: if not already_registered:
inst.init(*args, **kwds) inst.init(*args, **kwds)
finally:
Cacheable._lock.release()
return inst return inst
@staticmethod
def register(ob, key):
if not ob.__class__ in Cacheable.registry:
Cacheable.registry[ob.__class__] = {}
if key in Cacheable.registry[ob.__class__]:
ob = Cacheable.registry[ob.__class__][key]
#print "already registered: %s" % repr(ob)
return (ob, True)
else:
#print "not already registered: %s" % ob.__class__
Cacheable.registry[ob.__class__][key] = ob
return (ob, False)
@staticmethod @staticmethod
def _hash_func(*args, **kwds): def _hash_func(*args, **kwds):
raise NotImplementedError("The subclass must override this method") raise NotImplementedError("The subclass must override this method")
cls.__new__ = __new__
if not hasattr(cls, '_hash_func'):
cls._hash_func = _hash_func
return cls

445
lastfm/mixins/_chartable.py Normal file
View File

@ -0,0 +1,445 @@
#!/usr/bin/env python
__author__ = "Abhinav Sarkar <abhinav@abhinavsarkar.net>"
__version__ = "0.2"
__license__ = "GNU Lesser General Public License"
__package__ = "lastfm.mixins"
from lastfm.lazylist import lazylist
from lastfm.decorators import cached_property
def chartable(chart_types):
def wrapper(cls):
@cached_property
def weekly_chart_list(self):
"""
a list of available weekly charts for this group
@rtype: L{list} of L{WeeklyChart}
"""
from lastfm.chart import WeeklyChart
params = self._default_params(
{'method': '%s.getWeeklyChartList' % self.__class__.__name__.lower()})
data = self._api._fetch_data(params).find('weeklychartlist')
return [
WeeklyChart.create_from_data(self._api, self, c)
for c in data.findall('chart')
]
@cached_property
def monthly_chart_list(self):
from lastfm.chart import MonthlyChart
return MonthlyChart.get_chart_list(self)
def _default_params(self, extra_params = None):
if extra_params is not None:
return extra_params
else:
return {}
def get_weekly_album_chart(self, start = None, end = None):
"""
Get an album chart for the group, for a given date range.
If no date range is supplied, it will return the most
recent album chart for the group.
@param start: the date at which the chart should start from (optional)
@type start: C{datetime.datetime}
@param end: the date at which the chart should end on (optional)
@type end: C{datetime.datetime}
@return: an album chart for the group
@rtype: L{WeeklyAlbumChart}
@raise InvalidParametersError: Both start and end parameter have to be either
provided or not provided. Providing only one of
them will raise an exception.
"""
from lastfm.chart import WeeklyChart, WeeklyAlbumChart
params = self._default_params(
{'method': '%s.getWeeklyAlbumChart' % self.__class__.__name__.lower()})
params = WeeklyChart._check_chart_params(params, self, start, end)
data = self._api._fetch_data(params).find('weeklyalbumchart')
return WeeklyAlbumChart.create_from_data(self._api, self, data)
@cached_property
def recent_weekly_album_chart(self):
"""
most recent album chart for the group
@rtype: L{WeeklyAlbumChart}
"""
return self.get_weekly_album_chart()
@cached_property
def weekly_album_chart_list(self):
"""
a list of all album charts for this group in reverse-chronological
order. (that means 0th chart is the most recent chart)
@rtype: L{lazylist} of L{WeeklyAlbumChart}
"""
wcl = list(self.weekly_chart_list)
wcl.reverse()
@lazylist
def gen(lst):
for wc in wcl:
try:
yield self.get_weekly_album_chart(wc.start, wc.end)
except LastfmError:
pass
return gen()
def get_monthly_album_chart(self, start = None, end = None):
from lastfm.chart import MonthlyAlbumChart
return MonthlyAlbumChart.create_from_data(self, start, end)
@cached_property
def recent_monthly_album_chart(self):
return self.get_monthly_album_chart()
@cached_property
def monthly_album_chart_list(self):
mcl = list(self.monthly_chart_list)
mcl.reverse()
@lazylist
def gen(lst):
for mc in mcl:
try:
yield self.get_monthly_album_chart(mc.start, mc.end)
except LastfmError:
pass
return gen()
def get_quaterly_album_chart(self, start = None, end = None):
from lastfm.chart import QuaterlyAlbumChart
return QuaterlyAlbumChart.create_from_data(self, start, end)
@cached_property
def recent_quaterly_album_chart(self):
return self.get_quaterly_album_chart()
def get_half_yearly_album_chart(self, start = None, end = None):
from lastfm.chart import HalfYearlyAlbumChart
return HalfYearlyAlbumChart.create_from_data(self, start, end)
@cached_property
def recent_half_yearly_album_chart(self):
return self.get_half_yearly_album_chart()
def get_yearly_album_chart(self, start = None, end = None):
from lastfm.chart import YearlyAlbumChart
return YearlyAlbumChart.create_from_data(self, start, end)
@cached_property
def recent_yearly_album_chart(self):
return self.get_yearly_album_chart()
def get_weekly_artist_chart(self, start = None, end = None):
"""
Get an artist chart for the group, for a given date range.
If no date range is supplied, it will return the most
recent artist chart for the group.
@param start: the date at which the chart should start from (optional)
@type start: C{datetime.datetime}
@param end: the date at which the chart should end on (optional)
@type end: C{datetime.datetime}
@return: an artist chart for the group
@rtype: L{WeeklyArtistChart}
@raise InvalidParametersError: Both start and end parameter have to be either
provided or not provided. Providing only one of
them will raise an exception.
"""
from lastfm.chart import WeeklyChart, WeeklyArtistChart
params = self._default_params(
{'method': '%s.getWeeklyArtistChart' % self.__class__.__name__.lower()})
params = WeeklyChart._check_chart_params(params, self, start, end)
data = self._api._fetch_data(params).find('weeklyartistchart')
return WeeklyArtistChart.create_from_data(self._api, self, data)
@cached_property
def recent_weekly_artist_chart(self):
"""
most recent artist chart for the group
@rtype: L{WeeklyArtistChart}
"""
return self.get_weekly_artist_chart()
@cached_property
def weekly_artist_chart_list(self):
"""
a list of all artist charts for this group in reverse-chronological
order. (that means 0th chart is the most recent chart)
@rtype: L{lazylist} of L{WeeklyArtistChart}
"""
wcl = list(self.weekly_chart_list)
wcl.reverse()
@lazylist
def gen(lst):
for wc in wcl:
try:
yield self.get_weekly_artist_chart(wc.start, wc.end)
except LastfmError:
pass
return gen()
def get_monthly_artist_chart(self, start = None, end = None):
from lastfm.chart import MonthlyArtistChart
return MonthlyArtistChart.create_from_data(self, start, end)
@cached_property
def recent_monthly_artist_chart(self):
return self.get_monthly_artist_chart()
@cached_property
def monthly_artist_chart_list(self):
mcl = list(self.monthly_chart_list)
mcl.reverse()
@lazylist
def gen(lst):
for mc in mcl:
try:
yield self.get_monthly_artist_chart(mc.start, mc.end)
except LastfmError:
pass
return gen()
def get_quaterly_artist_chart(self, start = None, end = None):
from lastfm.chart import QuaterlyArtistChart
return QuaterlyArtistChart.create_from_data(self, start, end)
@cached_property
def recent_quaterly_artist_chart(self):
return self.get_quaterly_artist_chart()
def get_half_yearly_artist_chart(self, start = None, end = None):
from lastfm.chart import HalfYearlyArtistChart
return HalfYearlyArtistChart.create_from_data(self, start, end)
@cached_property
def recent_half_yearly_artist_chart(self):
return self.get_half_yearly_artist_chart()
def get_yearly_artist_chart(self, start = None, end = None):
from lastfm.chart import YearlyArtistChart
return YearlyArtistChart.create_from_data(self, start, end)
@cached_property
def recent_yearly_artist_chart(self):
return self.get_yearly_artist_chart()
def get_weekly_track_chart(self, start = None, end = None):
"""
Get a track chart for the group, for a given date range.
If no date range is supplied, it will return the most
recent artist chart for the group.
@param start: the date at which the chart should start from (optional)
@type start: C{datetime.datetime}
@param end: the date at which the chart should end on (optional)
@type end: C{datetime.datetime}
@return: a track chart for the group
@rtype: L{WeeklyTrackChart}
@raise InvalidParametersError: Both start and end parameter have to be either
provided or not provided. Providing only one of
them will raise an exception.
"""
from lastfm.chart import WeeklyChart, WeeklyTrackChart
params = self._default_params(
{'method': '%s.getWeeklyTrackChart' % self.__class__.__name__.lower()})
params = WeeklyChart._check_chart_params(params, self, start, end)
data = self._api._fetch_data(params).find('weeklytrackchart')
return WeeklyTrackChart.create_from_data(self._api, self, data)
@cached_property
def recent_weekly_track_chart(self):
"""
most recent track chart for the group
@rtype: L{WeeklyTrackChart}
"""
return self.get_weekly_track_chart()
@cached_property
def weekly_track_chart_list(self):
"""
a list of all track charts for this group in reverse-chronological
order. (that means 0th chart is the most recent chart)
@rtype: L{lazylist} of L{WeeklyTrackChart}
"""
wcl = list(self.weekly_chart_list)
wcl.reverse()
@lazylist
def gen(lst):
for wc in wcl:
try:
yield self.get_weekly_track_chart(wc.start, wc.end)
except LastfmError:
pass
return gen()
def get_monthly_track_chart(self, start = None, end = None):
from lastfm.chart import MonthlyTrackChart
return MonthlyTrackChart.create_from_data(self, start, end)
@cached_property
def recent_monthly_track_chart(self):
return self.get_monthly_track_chart()
@cached_property
def monthly_track_chart_list(self):
mcl = list(self.monthly_chart_list)
mcl.reverse()
@lazylist
def gen(lst):
for mc in mcl:
try:
yield self.get_monthly_track_chart(mc.start, mc.end)
except LastfmError:
pass
return gen()
def get_quaterly_track_chart(self, start = None, end = None):
from lastfm.chart import QuaterlyTrackChart
return QuaterlyTrackChart.create_from_data(self, start, end)
@cached_property
def recent_quaterly_track_chart(self):
return self.get_quaterly_track_chart()
def get_half_yearly_track_chart(self, start = None, end = None):
from lastfm.chart import HalfYearlyTrackChart
return HalfYearlyTrackChart.create_from_data(self, start, end)
@cached_property
def recent_half_yearly_track_chart(self):
return self.get_half_yearly_track_chart()
def get_yearly_track_chart(self, start = None, end = None):
from lastfm.chart import YearlyTrackChart
return YearlyTrackChart.create_from_data(self, start, end)
@cached_property
def recent_yearly_track_chart(self):
return self.get_yearly_track_chart()
def get_weekly_tag_chart(self, start = None, end = None):
"""
Get a tag chart for the group, for a given date range.
If no date range is supplied, it will return the most
recent tag chart for the group.
@param start: the date at which the chart should start from (optional)
@type start: C{datetime.datetime}
@param end: the date at which the chart should end on (optional)
@type end: C{datetime.datetime}
@return: a tag chart for the group
@rtype: L{WeeklyTagChart}
@raise InvalidParametersError: Both start and end parameter have to be either
provided or not provided. Providing only one of
them will raise an exception.
@note: This method is a composite method. It is not provided directly by the
last.fm API. It uses other methods to collect the data, analyzes it and
creates a chart. So this method is a little heavy to call, as it does
mulitple calls to the API.
"""
from lastfm.chart import WeeklyChart, WeeklyTagChart
WeeklyChart._check_chart_params({}, self, start, end)
return WeeklyTagChart.create_from_data(self._api, self, start, end)
@cached_property
def recent_weekly_tag_chart(self):
"""
most recent tag chart for the group
@rtype: L{WeeklyTagChart}
"""
return self.get_weekly_tag_chart()
@cached_property
def weekly_tag_chart_list(self):
"""
a list of all tag charts for this group in reverse-chronological
order. (that means 0th chart is the most recent chart)
@rtype: L{lazylist} of L{WeeklyTagChart}
"""
wcl = list(self.weekly_chart_list)
wcl.reverse()
@lazylist
def gen(lst):
for wc in wcl:
try:
yield self.get_weekly_tag_chart(wc.start, wc.end)
except LastfmError:
pass
return gen()
def get_monthly_tag_chart(self, start = None, end = None):
from lastfm.chart import MonthlyTagChart
return MonthlyTagChart.create_from_data(self, start, end)
@cached_property
def recent_monthly_tag_chart(self):
return self.get_monthly_tag_chart()
@cached_property
def monthly_tag_chart_list(self):
mcl = list(self.monthly_chart_list)
mcl.reverse()
@lazylist
def gen(lst):
for mc in mcl:
try:
yield self.get_monthly_tag_chart(mc.start, mc.end)
except LastfmError:
pass
return gen()
def get_quaterly_tag_chart(self, start = None, end = None):
from lastfm.chart import QuaterlyTagChart
return QuaterlyTagChart.create_from_data(self, start, end)
@cached_property
def recent_quaterly_tag_chart(self):
return self.get_quaterly_tag_chart()
def get_half_yearly_tag_chart(self, start = None, end = None):
from lastfm.chart import HalfYearlyTagChart
return HalfYearlyTagChart.create_from_data(self, start, end)
@cached_property
def recent_half_yearly_tag_chart(self):
return self.get_half_yearly_tag_chart()
def get_yearly_tag_chart(self, start = None, end = None):
from lastfm.chart import YearlyTagChart
return YearlyTagChart.create_from_data(self, start, end)
@cached_property
def recent_yearly_tag_chart(self):
return self.get_yearly_tag_chart()
cls.weekly_chart_list = weekly_chart_list
cls.monthly_chart_list = monthly_chart_list
if not hasattr(cls, '_default_params'):
cls._default_params = _default_params
method_names = [
'get_weekly_%s_chart', 'recent_weekly_%s_chart', 'weekly_%s_chart_list',
'get_monthly_%s_chart', 'recent_monthly_%s_chart', 'monthly_%s_chart_list',
'get_quaterly_%s_chart', 'recent_quaterly_%s_chart',
'get_half_yearly_%s_chart', 'recent_half_yearly_%s_chart',
'get_yearly_%s_chart', 'recent_yearly_%s_chart'
]
for chart_type in chart_types:
for method_name in method_names:
setattr(cls, method_name % chart_type, locals()[method_name % chart_type])
return cls
return wrapper
from lastfm.error import LastfmError

View File

@ -7,7 +7,7 @@ __package__ = "lastfm.mixins"
from lastfm.decorators import depaginate from lastfm.decorators import depaginate
class Searchable(object): def searchable(cls):
@classmethod @classmethod
@depaginate @depaginate
def search(cls, def search(cls,
@ -40,4 +40,10 @@ class Searchable(object):
@staticmethod @staticmethod
def _search_yield_func(api, search_term): def _search_yield_func(api, search_term):
pass raise NotImplementedError("the subclass should implement this method")
cls.search = search
if not hasattr(cls, '_search_yield_func'):
cls._search_yield_func = _search_yield_func
return cls

View File

@ -5,10 +5,7 @@ __version__ = "0.2"
__license__ = "GNU Lesser General Public License" __license__ = "GNU Lesser General Public License"
__package__ = "lastfm.mixins" __package__ = "lastfm.mixins"
class Sharable(object): def sharable(cls):
def init(self, api):
self._api = api
def share(self, recipient, message = None): def share(self, recipient, message = None):
from lastfm.user import User from lastfm.user import User
params = self._default_params({'method': '%s.share' % self.__class__.__name__.lower()}) params = self._default_params({'method': '%s.share' % self.__class__.__name__.lower()})
@ -28,4 +25,10 @@ class Sharable(object):
if extra_params is not None: if extra_params is not None:
return extra_params return extra_params
else: else:
return {} return {}
cls.share = share
if not hasattr(cls, '_default_params'):
cls._default_params = _default_params
return cls

View File

@ -7,13 +7,10 @@ __package__ = "lastfm.mixins"
from lastfm.decorators import cached_property, top_property from lastfm.decorators import cached_property, top_property
class Shoutable(object): def shoutable(cls):
def init(self, api):
self._api = api
@cached_property @cached_property
def shouts(self): def shouts(self):
"""shouts for this ssubject""" """shouts for this %s""" % cls.__name__.lower()
from lastfm.shout import Shout from lastfm.shout import Shout
from lastfm.user import User from lastfm.user import User
params = self._default_params({'method': '%s.getShouts' % self.__class__.__name__.lower()}) params = self._default_params({'method': '%s.getShouts' % self.__class__.__name__.lower()})
@ -30,7 +27,7 @@ class Shoutable(object):
@top_property("shouts") @top_property("shouts")
def recent_shout(self): def recent_shout(self):
"""recent shout for this subject""" """recent shout for this %s""" % cls.__name__.lower()
pass pass
def _default_params(self, extra_params = None): def _default_params(self, extra_params = None):
@ -38,6 +35,13 @@ class Shoutable(object):
return extra_params return extra_params
else: else:
return {} return {}
cls.shouts = shouts
cls.recent_shout = recent_shout
if not hasattr(cls, '_default_params'):
cls._default_params = _default_params
return cls
from datetime import datetime from datetime import datetime
import time import time

View File

@ -8,10 +8,7 @@ __package__ = "lastfm.mixins"
from lastfm.safelist import SafeList from lastfm.safelist import SafeList
from lastfm.decorators import cached_property, authentication_required from lastfm.decorators import cached_property, authentication_required
class Taggable(object): def taggable(cls):
def init(self, api):
self._api = api
@cached_property @cached_property
@authentication_required @authentication_required
def tags(self): def tags(self):
@ -69,4 +66,12 @@ class Taggable(object):
if extra_params is not None: if extra_params is not None:
return extra_params return extra_params
else: else:
return {} return {}
cls.tags = tags
cls.add_tags = add_tags
cls.remove_tag = remove_tag
if not hasattr(cls, '_default_params'):
cls._default_params = _default_params
return cls

View File

@ -1,432 +0,0 @@
#!/usr/bin/env python
__author__ = "Abhinav Sarkar <abhinav@abhinavsarkar.net>"
__version__ = "0.2"
__license__ = "GNU Lesser General Public License"
__package__ = "lastfm.mixins"
from lastfm.lazylist import lazylist
from lastfm.decorators import cached_property
class Chartable(object):
def init(self, api):
self._api = api
@cached_property
def weekly_chart_list(self):
"""
a list of available weekly charts for this group
@rtype: L{list} of L{WeeklyChart}
"""
from lastfm.chart import WeeklyChart
params = self._default_params(
{'method': '%s.getWeeklyChartList' % self.__class__.__name__.lower()})
data = self._api._fetch_data(params).find('weeklychartlist')
return [
WeeklyChart.create_from_data(self._api, self, c)
for c in data.findall('chart')
]
@cached_property
def monthly_chart_list(self):
from lastfm.chart import MonthlyChart
return MonthlyChart.get_chart_list(self)
def _default_params(self, extra_params = None):
if extra_params is not None:
return extra_params
else:
return {}
class AlbumChartable(Chartable):
def get_weekly_album_chart(self, start = None, end = None):
"""
Get an album chart for the group, for a given date range.
If no date range is supplied, it will return the most
recent album chart for the group.
@param start: the date at which the chart should start from (optional)
@type start: C{datetime.datetime}
@param end: the date at which the chart should end on (optional)
@type end: C{datetime.datetime}
@return: an album chart for the group
@rtype: L{WeeklyAlbumChart}
@raise InvalidParametersError: Both start and end parameter have to be either
provided or not provided. Providing only one of
them will raise an exception.
"""
from lastfm.chart import WeeklyChart, WeeklyAlbumChart
params = self._default_params(
{'method': '%s.getWeeklyAlbumChart' % self.__class__.__name__.lower()})
params = WeeklyChart._check_chart_params(params, self, start, end)
data = self._api._fetch_data(params).find('weeklyalbumchart')
return WeeklyAlbumChart.create_from_data(self._api, self, data)
@cached_property
def recent_weekly_album_chart(self):
"""
most recent album chart for the group
@rtype: L{WeeklyAlbumChart}
"""
return self.get_weekly_album_chart()
@cached_property
def weekly_album_chart_list(self):
"""
a list of all album charts for this group in reverse-chronological
order. (that means 0th chart is the most recent chart)
@rtype: L{lazylist} of L{WeeklyAlbumChart}
"""
wcl = list(self.weekly_chart_list)
wcl.reverse()
@lazylist
def gen(lst):
for wc in wcl:
try:
yield self.get_weekly_album_chart(wc.start, wc.end)
except LastfmError:
pass
return gen()
def get_monthly_album_chart(self, start = None, end = None):
from lastfm.chart import MonthlyAlbumChart
return MonthlyAlbumChart.create_from_data(self, start, end)
@cached_property
def recent_monthly_album_chart(self):
return self.get_monthly_album_chart()
@cached_property
def monthly_album_chart_list(self):
mcl = list(self.monthly_chart_list)
mcl.reverse()
@lazylist
def gen(lst):
for mc in mcl:
try:
yield self.get_monthly_album_chart(mc.start, mc.end)
except LastfmError:
pass
return gen()
def get_quaterly_album_chart(self, start = None, end = None):
from lastfm.chart import QuaterlyAlbumChart
return QuaterlyAlbumChart.create_from_data(self, start, end)
@cached_property
def recent_quaterly_album_chart(self):
return self.get_quaterly_album_chart()
def get_half_yearly_album_chart(self, start = None, end = None):
from lastfm.chart import HalfYearlyAlbumChart
return HalfYearlyAlbumChart.create_from_data(self, start, end)
@cached_property
def recent_half_yearly_album_chart(self):
return self.get_half_yearly_album_chart()
def get_yearly_album_chart(self, start = None, end = None):
from lastfm.chart import YearlyAlbumChart
return YearlyAlbumChart.create_from_data(self, start, end)
@cached_property
def recent_yearly_album_chart(self):
return self.get_yearly_album_chart()
class ArtistChartable(Chartable):
def get_weekly_artist_chart(self, start = None, end = None):
"""
Get an artist chart for the group, for a given date range.
If no date range is supplied, it will return the most
recent artist chart for the group.
@param start: the date at which the chart should start from (optional)
@type start: C{datetime.datetime}
@param end: the date at which the chart should end on (optional)
@type end: C{datetime.datetime}
@return: an artist chart for the group
@rtype: L{WeeklyArtistChart}
@raise InvalidParametersError: Both start and end parameter have to be either
provided or not provided. Providing only one of
them will raise an exception.
"""
from lastfm.chart import WeeklyChart, WeeklyArtistChart
params = self._default_params(
{'method': '%s.getWeeklyArtistChart' % self.__class__.__name__.lower()})
params = WeeklyChart._check_chart_params(params, self, start, end)
data = self._api._fetch_data(params).find('weeklyartistchart')
return WeeklyArtistChart.create_from_data(self._api, self, data)
@cached_property
def recent_weekly_artist_chart(self):
"""
most recent artist chart for the group
@rtype: L{WeeklyArtistChart}
"""
return self.get_weekly_artist_chart()
@cached_property
def weekly_artist_chart_list(self):
"""
a list of all artist charts for this group in reverse-chronological
order. (that means 0th chart is the most recent chart)
@rtype: L{lazylist} of L{WeeklyArtistChart}
"""
wcl = list(self.weekly_chart_list)
wcl.reverse()
@lazylist
def gen(lst):
for wc in wcl:
try:
yield self.get_weekly_artist_chart(wc.start, wc.end)
except LastfmError:
pass
return gen()
def get_monthly_artist_chart(self, start = None, end = None):
from lastfm.chart import MonthlyArtistChart
return MonthlyArtistChart.create_from_data(self, start, end)
@cached_property
def recent_monthly_artist_chart(self):
return self.get_monthly_artist_chart()
@cached_property
def monthly_artist_chart_list(self):
mcl = list(self.monthly_chart_list)
mcl.reverse()
@lazylist
def gen(lst):
for mc in mcl:
try:
yield self.get_monthly_artist_chart(mc.start, mc.end)
except LastfmError:
pass
return gen()
def get_quaterly_artist_chart(self, start = None, end = None):
from lastfm.chart import QuaterlyArtistChart
return QuaterlyArtistChart.create_from_data(self, start, end)
@cached_property
def recent_quaterly_artist_chart(self):
return self.get_quaterly_artist_chart()
def get_half_yearly_artist_chart(self, start = None, end = None):
from lastfm.chart import HalfYearlyArtistChart
return HalfYearlyArtistChart.create_from_data(self, start, end)
@cached_property
def recent_half_yearly_artist_chart(self):
return self.get_half_yearly_artist_chart()
def get_yearly_artist_chart(self, start = None, end = None):
from lastfm.chart import YearlyArtistChart
return YearlyArtistChart.create_from_data(self, start, end)
@cached_property
def recent_yearly_album_chart(self):
return self.get_yearly_artist_chart()
class TrackChartable(Chartable):
def get_weekly_track_chart(self, start = None, end = None):
"""
Get a track chart for the group, for a given date range.
If no date range is supplied, it will return the most
recent artist chart for the group.
@param start: the date at which the chart should start from (optional)
@type start: C{datetime.datetime}
@param end: the date at which the chart should end on (optional)
@type end: C{datetime.datetime}
@return: a track chart for the group
@rtype: L{WeeklyTrackChart}
@raise InvalidParametersError: Both start and end parameter have to be either
provided or not provided. Providing only one of
them will raise an exception.
"""
from lastfm.chart import WeeklyChart, WeeklyTrackChart
params = self._default_params(
{'method': '%s.getWeeklyTrackChart' % self.__class__.__name__.lower()})
params = WeeklyChart._check_chart_params(params, self, start, end)
data = self._api._fetch_data(params).find('weeklytrackchart')
return WeeklyTrackChart.create_from_data(self._api, self, data)
@cached_property
def recent_weekly_track_chart(self):
"""
most recent track chart for the group
@rtype: L{WeeklyTrackChart}
"""
return self.get_weekly_track_chart()
@cached_property
def weekly_track_chart_list(self):
"""
a list of all track charts for this group in reverse-chronological
order. (that means 0th chart is the most recent chart)
@rtype: L{lazylist} of L{WeeklyTrackChart}
"""
wcl = list(self.weekly_chart_list)
wcl.reverse()
@lazylist
def gen(lst):
for wc in wcl:
try:
yield self.get_weekly_track_chart(wc.start, wc.end)
except LastfmError:
pass
return gen()
def get_monthly_track_chart(self, start = None, end = None):
from lastfm.chart import MonthlyTrackChart
return MonthlyTrackChart.create_from_data(self, start, end)
@cached_property
def recent_monthly_track_chart(self):
return self.get_monthly_track_chart()
@cached_property
def monthly_track_chart_list(self):
mcl = list(self.monthly_chart_list)
mcl.reverse()
@lazylist
def gen(lst):
for mc in mcl:
try:
yield self.get_monthly_track_chart(mc.start, mc.end)
except LastfmError:
pass
return gen()
def get_quaterly_track_chart(self, start = None, end = None):
from lastfm.chart import QuaterlyTrackChart
return QuaterlyTrackChart.create_from_data(self, start, end)
@cached_property
def recent_quaterly_track_chart(self):
return self.get_quaterly_track_chart()
def get_half_yearly_track_chart(self, start = None, end = None):
from lastfm.chart import HalfYearlyTrackChart
return HalfYearlyTrackChart.create_from_data(self, start, end)
@cached_property
def recent_half_yearly_artist_chart(self):
return self.get_half_yearly_track_chart()
def get_yearly_track_chart(self, start = None, end = None):
from lastfm.chart import YearlyTrackChart
return YearlyTrackChart.create_from_data(self, start, end)
@cached_property
def recent_yearly_album_chart(self):
return self.get_yearly_track_chart()
class TagChartable(Chartable):
def get_weekly_tag_chart(self, start = None, end = None):
"""
Get a tag chart for the group, for a given date range.
If no date range is supplied, it will return the most
recent tag chart for the group.
@param start: the date at which the chart should start from (optional)
@type start: C{datetime.datetime}
@param end: the date at which the chart should end on (optional)
@type end: C{datetime.datetime}
@return: a tag chart for the group
@rtype: L{WeeklyTagChart}
@raise InvalidParametersError: Both start and end parameter have to be either
provided or not provided. Providing only one of
them will raise an exception.
@note: This method is a composite method. It is not provided directly by the
last.fm API. It uses other methods to collect the data, analyzes it and
creates a chart. So this method is a little heavy to call, as it does
mulitple calls to the API.
"""
from lastfm.chart import WeeklyChart, WeeklyTagChart
WeeklyChart._check_chart_params({}, self, start, end)
return WeeklyTagChart.create_from_data(self._api, self, start, end)
@cached_property
def recent_weekly_tag_chart(self):
"""
most recent tag chart for the group
@rtype: L{WeeklyTagChart}
"""
return self.get_weekly_tag_chart()
@cached_property
def weekly_tag_chart_list(self):
"""
a list of all tag charts for this group in reverse-chronological
order. (that means 0th chart is the most recent chart)
@rtype: L{lazylist} of L{WeeklyTagChart}
"""
wcl = list(self.weekly_chart_list)
wcl.reverse()
@lazylist
def gen(lst):
for wc in wcl:
try:
yield self.get_weekly_tag_chart(wc.start, wc.end)
except LastfmError:
pass
return gen()
def get_monthly_tag_chart(self, start = None, end = None):
from lastfm.chart import MonthlyTagChart
return MonthlyTagChart.create_from_data(self, start, end)
@cached_property
def recent_monthly_tag_chart(self):
return self.get_monthly_tag_chart()
@cached_property
def monthly_tag_chart_list(self):
mcl = list(self.monthly_chart_list)
mcl.reverse()
@lazylist
def gen(lst):
for mc in mcl:
try:
yield self.get_monthly_tag_chart(mc.start, mc.end)
except LastfmError:
pass
return gen()
def get_quaterly_tag_chart(self, start = None, end = None):
from lastfm.chart import QuaterlyTagChart
return QuaterlyTagChart.create_from_data(self, start, end)
@cached_property
def recent_quaterly_tag_chart(self):
return self.get_quaterly_tag_chart()
def get_half_yearly_tag_chart(self, start = None, end = None):
from lastfm.chart import HalfYearlyTagChart
return HalfYearlyTagChart.create_from_data(self, start, end)
@cached_property
def recent_half_yearly_artist_chart(self):
return self.get_half_yearly_tag_chart()
def get_yearly_tag_chart(self, start = None, end = None):
from lastfm.chart import YearlyTagChart
return YearlyTagChart.create_from_data(self, start, end)
@cached_property
def recent_yearly_album_chart(self):
return self.get_yearly_tag_chart()
from lastfm.error import LastfmError

View File

@ -7,7 +7,7 @@ __package__ = "lastfm"
from lastfm.album import Album from lastfm.album import Album
from lastfm.artist import Artist from lastfm.artist import Artist
from lastfm.mixins import Cacheable from lastfm.mixins import _cacheable
from lastfm.error import InvalidParametersError from lastfm.error import InvalidParametersError
from lastfm.event import Event from lastfm.event import Event
from lastfm.geo import Location, Country from lastfm.geo import Location, Country
@ -29,7 +29,7 @@ class ObjectCache(object):
raise InvalidParametersError("Key does not correspond to a valid class") raise InvalidParametersError("Key does not correspond to a valid class")
else: else:
try: try:
vals = Cacheable.registry[eval(name)].values() vals = _cacheable.registry[eval(name)].values()
vals.sort() vals.sort()
return vals return vals
except KeyError: except KeyError:

View File

@ -6,10 +6,11 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixins import Cacheable from lastfm.mixins import cacheable
from lastfm.decorators import cached_property from lastfm.decorators import cached_property
class Playlist(LastfmBase, Cacheable): @cacheable
class Playlist(LastfmBase):
"""A class representing an XPSF playlist.""" """A class representing an XPSF playlist."""
def init(self, api, url, **kwargs): def init(self, api, url, **kwargs):
self._api = api self._api = api

View File

@ -6,10 +6,11 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixins import Cacheable from lastfm.mixins import cacheable
from lastfm.decorators import cached_property from lastfm.decorators import cached_property
class Shout(LastfmBase, Cacheable): @cacheable
class Shout(LastfmBase):
"""A class representing a shout.""" """A class representing a shout."""
def init(self, def init(self,

View File

@ -6,11 +6,13 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixins import ( from lastfm.mixins import cacheable, searchable, chartable
Cacheable, Searchable, ArtistChartable)
from lastfm.decorators import cached_property, top_property from lastfm.decorators import cached_property, top_property
class Tag(LastfmBase, Cacheable, Searchable, ArtistChartable): @chartable(['artist'])
@searchable
@cacheable
class Tag(LastfmBase):
"""A class representing a tag.""" """A class representing a tag."""
def init(self, def init(self,
api, api,
@ -21,7 +23,6 @@ class Tag(LastfmBase, Cacheable, Searchable, ArtistChartable):
**kwargs): **kwargs):
if not isinstance(api, Api): if not isinstance(api, Api):
raise InvalidParametersError("api reference must be supplied as an argument") raise InvalidParametersError("api reference must be supplied as an argument")
ArtistChartable.init(self, api)
self._api = api self._api = api
self._name = name self._name = name

View File

@ -6,10 +6,14 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixins import Cacheable, Searchable, Sharable, Taggable from lastfm.mixins import cacheable, searchable, sharable, taggable
from lastfm.decorators import cached_property, top_property from lastfm.decorators import cached_property, top_property
class Track(LastfmBase, Cacheable, Sharable, Searchable, Taggable): @sharable
@taggable
@searchable
@cacheable
class Track(LastfmBase):
"""A class representing a track.""" """A class representing a track."""
def init(self, def init(self,
api, api,
@ -30,8 +34,6 @@ class Track(LastfmBase, Cacheable, Sharable, Searchable, Taggable):
subject = None): subject = None):
if not isinstance(api, Api): if not isinstance(api, Api):
raise InvalidParametersError("api reference must be supplied as an argument") raise InvalidParametersError("api reference must be supplied as an argument")
Taggable.init(self, api)
Sharable.init(self, api)
self._api = api self._api = api
self._id = id self._id = id
self._name = name self._name = name

View File

@ -6,16 +6,15 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixins import ( from lastfm.mixins import cacheable, shoutable, chartable
Cacheable, Shoutable, AlbumChartable,
ArtistChartable, TrackChartable, TagChartable)
import lastfm.playlist import lastfm.playlist
from lastfm.decorators import ( from lastfm.decorators import (
cached_property, top_property, authentication_required, depaginate) cached_property, top_property, authentication_required, depaginate)
class User(LastfmBase, Cacheable, Shoutable, @chartable(['album', 'artist', 'track', 'tag'])
AlbumChartable, ArtistChartable, @shoutable
TrackChartable, TagChartable): @cacheable
class User(LastfmBase):
"""A class representing an user.""" """A class representing an user."""
def init(self, def init(self,
api, api,
@ -27,11 +26,6 @@ class User(LastfmBase, Cacheable, Shoutable,
**kwargs): **kwargs):
if not isinstance(api, Api): if not isinstance(api, Api):
raise InvalidParametersError("api reference must be supplied as an argument") raise InvalidParametersError("api reference must be supplied as an argument")
Shoutable.init(self, api)
AlbumChartable.init(self, api)
ArtistChartable.init(self, api)
TrackChartable.init(self, api)
TagChartable.init(self, api)
self._api = api self._api = api
self._name = name self._name = name

View File

@ -6,10 +6,12 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixins import Cacheable, Searchable from lastfm.mixins import cacheable, searchable
from lastfm.decorators import cached_property, depaginate from lastfm.decorators import cached_property, depaginate
class Venue(LastfmBase, Cacheable, Searchable): @searchable
@cacheable
class Venue(LastfmBase):
"""A class representing a venue of an event""" """A class representing a venue of an event"""
def init(self, def init(self,
api, api,