From e0c02773368be582398e7e604b45e61bd5aeed1c Mon Sep 17 00:00:00 2001 From: Abhinav Sarkar Date: Tue, 7 Apr 2009 11:14:42 +0000 Subject: [PATCH] * renamed lastfm.mixins module to lastfm.mixin * group utility modules in lastfm.util module * made related changes --- lastfm/__init__.py | 2 +- lastfm/album.py | 2 +- lastfm/api.py | 7 +- lastfm/artist.py | 2 +- lastfm/base.py | 2 - lastfm/chart.py | 2 +- lastfm/decorators.py | 2 +- lastfm/event.py | 2 +- lastfm/geo.py | 2 +- lastfm/group.py | 2 +- lastfm/mixin/__init__.py | 17 ++ lastfm/mixin/_cacheable.py | 53 ++++ lastfm/mixin/_chartable.py | 451 +++++++++++++++++++++++++++++++ lastfm/mixin/_crawlable.py | 38 +++ lastfm/mixin/_searchable.py | 53 ++++ lastfm/mixin/_sharable.py | 38 +++ lastfm/mixin/_shoutable.py | 51 ++++ lastfm/mixin/_taggable.py | 81 ++++++ lastfm/playlist.py | 2 +- lastfm/shout.py | 2 +- lastfm/tag.py | 2 +- lastfm/track.py | 2 +- lastfm/user.py | 2 +- lastfm/util/__init__.py | 13 + lastfm/{ => util}/filecache.py | 2 +- lastfm/{ => util}/lazylist.py | 1 + lastfm/{ => util}/objectcache.py | 2 +- lastfm/{ => util}/safelist.py | 2 +- lastfm/venue.py | 2 +- 29 files changed, 815 insertions(+), 24 deletions(-) create mode 100644 lastfm/mixin/__init__.py create mode 100644 lastfm/mixin/_cacheable.py create mode 100644 lastfm/mixin/_chartable.py create mode 100644 lastfm/mixin/_crawlable.py create mode 100644 lastfm/mixin/_searchable.py create mode 100644 lastfm/mixin/_sharable.py create mode 100644 lastfm/mixin/_shoutable.py create mode 100644 lastfm/mixin/_taggable.py create mode 100644 lastfm/util/__init__.py rename lastfm/{ => util}/filecache.py (99%) rename lastfm/{ => util}/lazylist.py (96%) rename lastfm/{ => util}/objectcache.py (94%) rename lastfm/{ => util}/safelist.py (98%) diff --git a/lastfm/__init__.py b/lastfm/__init__.py index bb84046..d1496b6 100644 --- a/lastfm/__init__.py +++ b/lastfm/__init__.py @@ -23,7 +23,7 @@ from lastfm.event import Event from lastfm.geo import Location, Country from lastfm.group import Group from lastfm.playlist import Playlist -from lastfm.objectcache import ObjectCache +from lastfm.util import ObjectCache from lastfm.tag import Tag from lastfm.tasteometer import Tasteometer from lastfm.track import Track diff --git a/lastfm/album.py b/lastfm/album.py index dc1185e..a1f0a54 100644 --- a/lastfm/album.py +++ b/lastfm/album.py @@ -7,7 +7,7 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" from lastfm.base import LastfmBase -from lastfm.mixins import cacheable, searchable, taggable, crawlable +from lastfm.mixin import cacheable, searchable, taggable, crawlable from lastfm.decorators import cached_property, top_property @crawlable diff --git a/lastfm/api.py b/lastfm/api.py index eac8b77..5975535 100644 --- a/lastfm/api.py +++ b/lastfm/api.py @@ -656,10 +656,7 @@ class Api(object): self._last_fetch_time = datetime.now() return url_data - def _fetch_url(self, - url, - parameters = None, - no_cache = False): + def _fetch_url(self, url, parameters = None, no_cache = False): # Add key/value parameters to the query string of the url url = self._build_url(url, extra_params=parameters) if self._debug: @@ -780,7 +777,7 @@ from lastfm.artist import Artist from lastfm.error import error_map, LastfmError, OperationFailedError, AuthenticationFailedError,\ InvalidParametersError from lastfm.event import Event -from lastfm.filecache import FileCache +from lastfm.util import FileCache from lastfm.geo import Location, Country from lastfm.group import Group from lastfm.playlist import Playlist diff --git a/lastfm/artist.py b/lastfm/artist.py index b030b46..a2ec4fd 100644 --- a/lastfm/artist.py +++ b/lastfm/artist.py @@ -7,7 +7,7 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" from lastfm.base import LastfmBase -from lastfm.mixins import cacheable, searchable, sharable, shoutable, taggable, crawlable +from lastfm.mixin import cacheable, searchable, sharable, shoutable, taggable, crawlable from lastfm.decorators import cached_property, top_property @crawlable diff --git a/lastfm/base.py b/lastfm/base.py index 4302f0c..3bbe7b9 100644 --- a/lastfm/base.py +++ b/lastfm/base.py @@ -6,8 +6,6 @@ __version__ = "0.2" __license__ = "GNU Lesser General Public License" __package__ = "lastfm" -from lastfm.lazylist import lazylist - class LastfmBase(object): """Base class for all the classes in this package""" diff --git a/lastfm/chart.py b/lastfm/chart.py index 63b4f79..fb558f8 100644 --- a/lastfm/chart.py +++ b/lastfm/chart.py @@ -7,7 +7,7 @@ __package__ = "lastfm" from functools import reduce from lastfm.base import LastfmBase -from lastfm.mixins import cacheable +from lastfm.mixin import cacheable from operator import xor @cacheable diff --git a/lastfm/decorators.py b/lastfm/decorators.py index e44b766..b0d2635 100644 --- a/lastfm/decorators.py +++ b/lastfm/decorators.py @@ -112,7 +112,7 @@ def depaginate(func, *args, **kwargs): a L{lazylist} of all search results (all pages) @rtype: C{function} """ - from lastfm.lazylist import lazylist + from lastfm.util import lazylist @lazylist def generator(lst): gen = func(*args, **kwargs) diff --git a/lastfm/event.py b/lastfm/event.py index 9d3c940..7489162 100644 --- a/lastfm/event.py +++ b/lastfm/event.py @@ -7,7 +7,7 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" from lastfm.base import LastfmBase -from lastfm.mixins import cacheable, sharable, shoutable, crawlable +from lastfm.mixin import cacheable, sharable, shoutable, crawlable @crawlable @shoutable diff --git a/lastfm/geo.py b/lastfm/geo.py index b98158a..6fff487 100644 --- a/lastfm/geo.py +++ b/lastfm/geo.py @@ -8,7 +8,7 @@ __package__ = "lastfm" from functools import reduce from lastfm.base import LastfmBase -from lastfm.mixins import cacheable, crawlable +from lastfm.mixin import cacheable, crawlable from lastfm.decorators import cached_property, top_property, depaginate class Geo(object): diff --git a/lastfm/group.py b/lastfm/group.py index 13e6506..876dae3 100644 --- a/lastfm/group.py +++ b/lastfm/group.py @@ -7,7 +7,7 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" from lastfm.base import LastfmBase -from lastfm.mixins import cacheable, chartable +from lastfm.mixin import cacheable, chartable from lastfm.decorators import cached_property, depaginate @chartable(['album', 'artist', 'track', 'tag']) diff --git a/lastfm/mixin/__init__.py b/lastfm/mixin/__init__.py new file mode 100644 index 0000000..ff2c949 --- /dev/null +++ b/lastfm/mixin/__init__.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +__author__ = "Abhinav Sarkar " +__version__ = "0.2" +__license__ = "GNU Lesser General Public License" +__package__ = "lastfm.mixin" + +from lastfm.mixin._cacheable import cacheable +from lastfm.mixin._searchable import searchable +from lastfm.mixin._sharable import sharable +from lastfm.mixin._shoutable import shoutable +from lastfm.mixin._taggable import taggable +from lastfm.mixin._chartable import chartable +from lastfm.mixin._crawlable import crawlable + +__all__ = ['cacheable', 'searchable', 'sharable', 'shoutable', 'taggable' + 'chartable','crawlable'] \ No newline at end of file diff --git a/lastfm/mixin/_cacheable.py b/lastfm/mixin/_cacheable.py new file mode 100644 index 0000000..69cafa4 --- /dev/null +++ b/lastfm/mixin/_cacheable.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +__author__ = "Abhinav Sarkar " +__version__ = "0.2" +__license__ = "GNU Lesser General Public License" +__package__ = "lastfm.mixin" + +try: + from threading import Lock +except ImportError: + from dummy_threading import Lock +from lastfm.util import ObjectCache + +_lock = Lock() + +def cacheable(cls): + @classmethod + def __new__(cls, *args, **kwds): + args = args[1:] + subject = None + if 'subject' in kwds and not 'Weekly' in cls.__name__: + subject = kwds['subject'] + #del kwds['subject'] + + if 'bypass_registry' in kwds: + del kwds['bypass_registry'] + inst = object.__new__(cls) + inst.init(*args, **kwds) + return inst + + key = cls._hash_func(*args, **kwds) + if subject is not None: + key = (hash(subject), key) + + with _lock: + inst, already_registered = ObjectCache.register(object.__new__(cls), key) + if not already_registered: + inst.init(*args, **kwds) + return inst + + @staticmethod + def _hash_func(*args, **kwds): + raise NotImplementedError("The subclass must override this method") + + cls.__new__ = __new__ + if not hasattr(cls, '_hash_func'): + cls._hash_func = _hash_func + + if not hasattr(cls, '_mixins'): + cls._mixins = [] + cls._mixins.append('__new__') + + return cls diff --git a/lastfm/mixin/_chartable.py b/lastfm/mixin/_chartable.py new file mode 100644 index 0000000..4c48068 --- /dev/null +++ b/lastfm/mixin/_chartable.py @@ -0,0 +1,451 @@ +#!/usr/bin/env python + +__author__ = "Abhinav Sarkar " +__version__ = "0.2" +__license__ = "GNU Lesser General Public License" +__package__ = "lastfm.mixin" + +from lastfm.util 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 + + if not hasattr(cls, '_mixins'): + cls._mixins = [] + cls._mixins.extend(['weekly_chart_list', 'monthly_chart_list']) + + 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]) + cls._mixins.append(method_name % chart_type) + + return cls + return wrapper + +from lastfm.error import LastfmError + \ No newline at end of file diff --git a/lastfm/mixin/_crawlable.py b/lastfm/mixin/_crawlable.py new file mode 100644 index 0000000..235c1dd --- /dev/null +++ b/lastfm/mixin/_crawlable.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +__author__ = "Abhinav Sarkar " +__version__ = "0.2" +__license__ = "GNU Lesser General Public License" +__package__ = "lastfm.mixin" + +from lastfm.util import lazylist + +def crawlable(cls): + _get_all = cls._get_all + @staticmethod + def get_all(seed): + seed, hash_attrs, spider_func = _get_all(seed) + @lazylist + def gen(lst): + seen = [] + api = seed._api + + def hash_dict(item): + return dict((a, getattr(item, a)) for a in hash_attrs) + + seen.append(hash_dict(seed)) + yield seed + for hsh in seen: + for n in spider_func(api, hsh): + if hash_dict(n) not in seen: + seen.append(hash_dict(n)) + yield n + return gen() + + cls.get_all = get_all + delattr(cls, '_get_all') + + if not hasattr(cls, '_mixins'): + cls._mixins = [] + cls._mixins.append('get_all') + return cls \ No newline at end of file diff --git a/lastfm/mixin/_searchable.py b/lastfm/mixin/_searchable.py new file mode 100644 index 0000000..d10683d --- /dev/null +++ b/lastfm/mixin/_searchable.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +__author__ = "Abhinav Sarkar " +__version__ = "0.2" +__license__ = "GNU Lesser General Public License" +__package__ = "lastfm.mixin" + +from lastfm.decorators import depaginate + +def searchable(cls): + @classmethod + @depaginate + def search(cls, + api, + search_item, + limit = None, + page = None, + **kwds): + from lastfm.api import Api + cls_name = cls.__name__.lower() + params = { + 'method': '%s.search'%cls_name, + cls_name: search_item + } + for kwd in kwds: + if kwds[kwd] is not None: + params[kwd] = kwds[kwd] + + if limit: + params.update({'limit': limit}) + if page is not None: + params.update({'page': page}) + + data = api._fetch_data(params).find('results') + total_pages = int(data.findtext("{%s}totalResults" % Api.SEARCH_XMLNS))/ \ + int(data.findtext("{%s}itemsPerPage" % Api.SEARCH_XMLNS)) + 1 + yield total_pages + for a in data.findall('%smatches/%s'%(cls_name, cls_name)): + yield cls._search_yield_func(api, a) + + @staticmethod + def _search_yield_func(api, search_term): + 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 + + if not hasattr(cls, '_mixins'): + cls._mixins = [] + cls._mixins.append('search') + + return cls diff --git a/lastfm/mixin/_sharable.py b/lastfm/mixin/_sharable.py new file mode 100644 index 0000000..b0fb885 --- /dev/null +++ b/lastfm/mixin/_sharable.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +__author__ = "Abhinav Sarkar " +__version__ = "0.2" +__license__ = "GNU Lesser General Public License" +__package__ = "lastfm.mixin" + +def sharable(cls): + def share(self, recipient, message = None): + from lastfm.user import User + params = self._default_params({'method': '%s.share' % self.__class__.__name__.lower()}) + if message is not None: + params['message'] = message + + if not isinstance(recipient, list): + recipient = [recipient] + + for i in xrange(len(recipient)): + if isinstance(recipient[i], User): + recipient[i] = recipient[i].name + params['recipient'] = ",".join(recipient) + self._api._post_data(params) + + def _default_params(self, extra_params = None): + if extra_params is not None: + return extra_params + else: + return {} + + cls.share = share + if not hasattr(cls, '_default_params'): + cls._default_params = _default_params + + if not hasattr(cls, '_mixins'): + cls._mixins = [] + cls._mixins.append('share') + + return cls \ No newline at end of file diff --git a/lastfm/mixin/_shoutable.py b/lastfm/mixin/_shoutable.py new file mode 100644 index 0000000..dad0233 --- /dev/null +++ b/lastfm/mixin/_shoutable.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +__author__ = "Abhinav Sarkar " +__version__ = "0.2" +__license__ = "GNU Lesser General Public License" +__package__ = "lastfm.mixin" + +from lastfm.decorators import cached_property, top_property + +def shoutable(cls): + @cached_property + def shouts(self): + """shouts for this %s""" % cls.__name__.lower() + from lastfm.shout import Shout + from lastfm.user import User + params = self._default_params({'method': '%s.getShouts' % self.__class__.__name__.lower()}) + data = self._api._fetch_data(params).find('shouts') + return [ + Shout( + body = s.findtext('body'), + author = User(self._api, name = s.findtext('author')), + date = s.findtext('date') and s.findtext('date').strip() and \ + datetime(*(time.strptime(s.findtext('date').strip(), '%a, %d %b %Y %H:%M:%S')[0:6])) + ) + for s in data.findall('shout') + ] + + @top_property("shouts") + def recent_shout(self): + """recent shout for this %s""" % cls.__name__.lower() + pass + + def _default_params(self, extra_params = None): + if extra_params is not None: + return extra_params + else: + return {} + + cls.shouts = shouts + cls.recent_shout = recent_shout + if not hasattr(cls, '_default_params'): + cls._default_params = _default_params + + if not hasattr(cls, '_mixins'): + cls._mixins = [] + cls._mixins.extend(['shouts', 'recent_shout']) + + return cls + +from datetime import datetime +import time \ No newline at end of file diff --git a/lastfm/mixin/_taggable.py b/lastfm/mixin/_taggable.py new file mode 100644 index 0000000..ab34f8e --- /dev/null +++ b/lastfm/mixin/_taggable.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python + +__author__ = "Abhinav Sarkar " +__version__ = "0.2" +__license__ = "GNU Lesser General Public License" +__package__ = "lastfm.mixin" + +from lastfm.util import SafeList +from lastfm.decorators import cached_property, authentication_required + +def taggable(cls): + @cached_property + @authentication_required + def tags(self): + from lastfm.tag import Tag + params = self._default_params({'method': '%s.getTags' % self.__class__.__name__.lower()}) + data = self._api._fetch_data(params, sign = True, session = True, no_cache = True).find('tags') + return SafeList([ + Tag( + self._api, + name = t.findtext('name'), + url = t.findtext('url') + ) + for t in data.findall('tag') + ], + self.add_tags, self.remove_tag) + + @authentication_required + def add_tags(self, tags): + from lastfm.tag import Tag + while(len(tags) > 10): + section = tags[0:9] + tags = tags[9:] + self.add_tags(section) + + if len(tags) == 0: return + + tagnames = [] + for tag in tags: + if isinstance(tag, Tag): + tagnames.append(tag.name) + elif isinstance(tag, str): + tagnames.append(tag) + + params = self._default_params({ + 'method': '%s.addTags' % self.__class__.__name__.lower(), + 'tags': ",".join(tagnames) + }) + self._api._post_data(params) + self._tags = None + + @authentication_required + def remove_tag(self, tag): + from lastfm.tag import Tag + if isinstance(tag, Tag): + tag = tag.name + + params = self._default_params({ + 'method': '%s.removeTag' % self.__class__.__name__.lower(), + 'tag': tag + }) + self._api._post_data(params) + self._tags = None + + def _default_params(self, extra_params = None): + if extra_params is not None: + return extra_params + else: + 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 + + if not hasattr(cls, '_mixins'): + cls._mixins = [] + cls._mixins.extend(['tags', 'add_tags', 'remove_tag']) + + return cls \ No newline at end of file diff --git a/lastfm/playlist.py b/lastfm/playlist.py index 61ce0f0..6b0fe37 100644 --- a/lastfm/playlist.py +++ b/lastfm/playlist.py @@ -6,7 +6,7 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" from lastfm.base import LastfmBase -from lastfm.mixins import cacheable +from lastfm.mixin import cacheable from lastfm.decorators import cached_property @cacheable diff --git a/lastfm/shout.py b/lastfm/shout.py index 7db5cab..6af5717 100644 --- a/lastfm/shout.py +++ b/lastfm/shout.py @@ -6,7 +6,7 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" from lastfm.base import LastfmBase -from lastfm.mixins import cacheable +from lastfm.mixin import cacheable from lastfm.decorators import cached_property @cacheable diff --git a/lastfm/tag.py b/lastfm/tag.py index 24b6541..674b92d 100644 --- a/lastfm/tag.py +++ b/lastfm/tag.py @@ -6,7 +6,7 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" from lastfm.base import LastfmBase -from lastfm.mixins import cacheable, searchable, chartable, crawlable +from lastfm.mixin import cacheable, searchable, chartable, crawlable from lastfm.decorators import cached_property, top_property @crawlable diff --git a/lastfm/track.py b/lastfm/track.py index 397d33f..ac13a4a 100644 --- a/lastfm/track.py +++ b/lastfm/track.py @@ -6,7 +6,7 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" from lastfm.base import LastfmBase -from lastfm.mixins import cacheable, searchable, sharable, taggable, crawlable +from lastfm.mixin import cacheable, searchable, sharable, taggable, crawlable from lastfm.decorators import cached_property, top_property @crawlable diff --git a/lastfm/user.py b/lastfm/user.py index 0979250..4621125 100644 --- a/lastfm/user.py +++ b/lastfm/user.py @@ -6,7 +6,7 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" from lastfm.base import LastfmBase -from lastfm.mixins import cacheable, shoutable, chartable, crawlable +from lastfm.mixin import cacheable, shoutable, chartable, crawlable import lastfm.playlist from lastfm.decorators import ( cached_property, top_property, authentication_required, depaginate) diff --git a/lastfm/util/__init__.py b/lastfm/util/__init__.py new file mode 100644 index 0000000..2544d71 --- /dev/null +++ b/lastfm/util/__init__.py @@ -0,0 +1,13 @@ +__author__ = "Abhinav Sarkar " +__version__ = "0.2" +__license__ = "GNU Lesser General Public License" +__package__ = "lastfm.util" + +from lastfm.util.wormhole import Wormhole +from lastfm.util.lazylist import lazylist +from lastfm.util.safelist import SafeList +from lastfm.util.filecache import FileCache +from lastfm.util.objectcache import ObjectCache + +__all__ = ['Wormhole', 'lazylist', 'SafeList', + 'FileCache', 'ObjectCache'] \ No newline at end of file diff --git a/lastfm/filecache.py b/lastfm/util/filecache.py similarity index 99% rename from lastfm/filecache.py rename to lastfm/util/filecache.py index 6e3c0e8..6b8d0dc 100644 --- a/lastfm/filecache.py +++ b/lastfm/util/filecache.py @@ -4,7 +4,7 @@ __author__ = "Abhinav Sarkar " __version__ = "0.2" __license__ = "GNU Lesser General Public License" -__package__ = "lastfm" +__package__ = "lastfm.util" import sys if sys.version < '2.6': diff --git a/lastfm/lazylist.py b/lastfm/util/lazylist.py similarity index 96% rename from lastfm/lazylist.py rename to lastfm/util/lazylist.py index e72dcd9..9f7f0e7 100644 --- a/lastfm/lazylist.py +++ b/lastfm/util/lazylist.py @@ -8,6 +8,7 @@ Backport to python 2.5 by Michael Pust __author__ = 'Dan Spitz' __all__ = ('LazyList', 'RecursiveLazyList', 'lazylist') +__package__ = "lastfm.util" import itertools diff --git a/lastfm/objectcache.py b/lastfm/util/objectcache.py similarity index 94% rename from lastfm/objectcache.py rename to lastfm/util/objectcache.py index e35d6f6..8d6d264 100644 --- a/lastfm/objectcache.py +++ b/lastfm/util/objectcache.py @@ -3,7 +3,7 @@ __author__ = "Abhinav Sarkar " __version__ = "0.2" __license__ = "GNU Lesser General Public License" -__package__ = "lastfm" +__package__ = "lastfm.util" _registry = {} diff --git a/lastfm/safelist.py b/lastfm/util/safelist.py similarity index 98% rename from lastfm/safelist.py rename to lastfm/util/safelist.py index d9ce597..5bc977f 100644 --- a/lastfm/safelist.py +++ b/lastfm/util/safelist.py @@ -3,7 +3,7 @@ __author__ = "Abhinav Sarkar " __version__ = "0.2" __license__ = "GNU Lesser General Public License" -__package__ = "lastfm" +__package__ = "lastfm.util" import sys class SafeList(object): diff --git a/lastfm/venue.py b/lastfm/venue.py index 3609436..f676007 100644 --- a/lastfm/venue.py +++ b/lastfm/venue.py @@ -6,7 +6,7 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" from lastfm.base import LastfmBase -from lastfm.mixins import cacheable, searchable, crawlable +from lastfm.mixin import cacheable, searchable, crawlable from lastfm.decorators import cached_property, depaginate @crawlable