refactored pagination behaviour in 'paginate' decorator

master
Abhinav Sarkar 2009-03-17 18:34:06 +00:00
parent 4b1dc47b24
commit 394310c60f
6 changed files with 201 additions and 294 deletions

View File

@ -4,6 +4,7 @@
__author__ = "Abhinav Sarkar <abhinav@abhinavsarkar.net>" __author__ = "Abhinav Sarkar <abhinav@abhinavsarkar.net>"
__version__ = "0.2" __version__ = "0.2"
__license__ = "GNU Lesser General Public License" __license__ = "GNU Lesser General Public License"
__package__ = "lastfm"
def top_property(list_property_name): def top_property(list_property_name):
""" """
@ -95,6 +96,26 @@ def authenticate(func):
pass pass
raise AuthenticationFailedError( raise AuthenticationFailedError(
"user '%s' does not have permissions to access the service" % username) "user '%s' does not have permissions to access the service" % username)
wrapper.__doc__ = func.__doc__
return wrapper
def depaginate(func):
from lastfm.lazylist import lazylist
def wrapper(*args, **kwargs):
@lazylist
def generator(lst):
gen = func(*args, **kwargs)
total_pages = gen.next()
for e in gen:
yield e
for page in xrange(2, total_pages+1):
kwargs['page'] = page
gen = func(*args, **kwargs)
gen.next()
for e in gen:
yield e
return generator()
wrapper.__doc__ = func.__doc__
return wrapper return wrapper
import copy import copy

View File

@ -8,17 +8,18 @@ __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixins import Cacheable from lastfm.mixins import Cacheable
from lastfm.lazylist import lazylist from lastfm.decorators import cached_property, top_property, depaginate
from lastfm.decorators import cached_property, top_property
class Geo(object): class Geo(object):
"""A class representing an geographic location""" """A class representing an geographic location"""
@staticmethod @staticmethod
@depaginate
def get_events(api, def get_events(api,
location, location,
latitude = None, latitude = None,
longitude = None, longitude = None,
distance = None): distance = None,
page = None):
""" """
Get the events for a location. Get the events for a location.
@ -52,27 +53,16 @@ class Geo(object):
if latitude is not None and longitude is not None: if latitude is not None and longitude is not None:
params.update({'lat': latitude, 'long': longitude}) params.update({'lat': latitude, 'long': longitude})
if page is not None:
params.update({'page': page})
@lazylist
def gen(lst):
data = api._fetch_data(params).find('events') data = api._fetch_data(params).find('events')
total_pages = int(data.attrib['totalpages']) total_pages = int(data.attrib['totalpages'])
yield total_pages
@lazylist
def gen2(lst, data):
for e in data.findall('event'): for e in data.findall('event'):
yield Event.create_from_data(api, e) yield Event.create_from_data(api, e)
for e in gen2(data):
yield e
for page in xrange(2, total_pages+1):
params.update({'page': page})
data = api._fetch_data(params).find('events')
for e in gen2(data):
yield e
return gen()
@staticmethod @staticmethod
def get_top_artists(api, country): def get_top_artists(api, country):
""" """

View File

@ -9,7 +9,7 @@ __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixins import Cacheable from lastfm.mixins import Cacheable
from lastfm.lazylist import lazylist from lastfm.lazylist import lazylist
from lastfm.decorators import cached_property, top_property from lastfm.decorators import cached_property, top_property, depaginate
class Group(LastfmBase, Cacheable): class Group(LastfmBase, Cacheable):
"""A class representing a group on last.fm.""" """A class representing a group on last.fm."""
@ -194,20 +194,18 @@ class Group(LastfmBase, Cacheable):
return gen() return gen()
@cached_property @cached_property
def members(self): @depaginate
def members(self, page = None):
""" """
members of the group members of the group
@rtype: L{lazylist} of L{User} @rtype: L{lazylist} of L{User}
""" """
params = self._default_params({'method': 'group.getMembers'}) params = self._default_params({'method': 'group.getMembers'})
if page is not None:
@lazylist params.update({'page': page})
def gen(lst):
data = self._api._fetch_data(params).find('members') data = self._api._fetch_data(params).find('members')
total_pages = int(data.attrib['totalPages']) total_pages = int(data.attrib['totalPages'])
yield total_pages
@lazylist
def gen2(lst, data):
for u in data.findall('user'): for u in data.findall('user'):
yield User( yield User(
self._api, self._api,
@ -217,16 +215,6 @@ class Group(LastfmBase, Cacheable):
url = u.findtext('url') url = u.findtext('url')
) )
for u in gen2(data):
yield u
for page in xrange(1, total_pages+1):
params.update({'page': page})
data = self._api._fetch_data(params).find('members')
for u in gen2(data):
yield u
return gen()
def _default_params(self, extra_params = {}): def _default_params(self, extra_params = {}):
if not self.name: if not self.name:
raise InvalidParametersError("group has to be provided.") raise InvalidParametersError("group has to be provided.")

View File

@ -5,14 +5,16 @@ __version__ = "0.2"
__license__ = "GNU Lesser General Public License" __license__ = "GNU Lesser General Public License"
__package__ = "lastfm.mixins" __package__ = "lastfm.mixins"
from lastfm.lazylist import lazylist from lastfm.decorators import depaginate
class Searchable(object): class Searchable(object):
@classmethod @classmethod
@depaginate
def search(cls, def search(cls,
api, api,
search_item, search_item,
limit = None, limit = None,
page = None,
**kwds): **kwds):
from lastfm.api import Api from lastfm.api import Api
cls_name = cls.__name__.lower() cls_name = cls.__name__.lower()
@ -26,28 +28,16 @@ class Searchable(object):
if limit: if limit:
params.update({'limit': limit}) params.update({'limit': limit})
if page is not None:
params.update({'page': page})
@lazylist
def gen(lst):
data = api._fetch_data(params).find('results') data = api._fetch_data(params).find('results')
total_pages = int(data.findtext("{%s}totalResults" % Api.SEARCH_XMLNS))/ \ total_pages = int(data.findtext("{%s}totalResults" % Api.SEARCH_XMLNS))/ \
int(data.findtext("{%s}itemsPerPage" % Api.SEARCH_XMLNS)) + 1 int(data.findtext("{%s}itemsPerPage" % Api.SEARCH_XMLNS)) + 1
yield total_pages
@lazylist
def gen2(lst, data):
for a in data.findall('%smatches/%s'%(cls_name, cls_name)): for a in data.findall('%smatches/%s'%(cls_name, cls_name)):
yield cls._search_yield_func(api, a) yield cls._search_yield_func(api, a)
for a in gen2(data):
yield a
for page in xrange(2, total_pages+1):
params.update({'page': page})
data = api._fetch_data(params).find('results')
for a in gen2(data):
yield a
return gen()
@staticmethod @staticmethod
def _search_yield_func(api, search_term): def _search_yield_func(api, search_term):
pass pass

View File

@ -9,7 +9,7 @@ from lastfm.base import LastfmBase
from lastfm.mixins import Cacheable, Shoutable from lastfm.mixins import Cacheable, Shoutable
from lastfm.lazylist import lazylist from lastfm.lazylist import lazylist
import lastfm.playlist import lastfm.playlist
from lastfm.decorators import cached_property, top_property, authenticate from lastfm.decorators import cached_property, top_property, authenticate, depaginate
class User(LastfmBase, Cacheable, Shoutable): class User(LastfmBase, Cacheable, Shoutable):
"""A class representing an user.""" """A class representing an user."""
@ -111,61 +111,37 @@ class User(LastfmBase, Cacheable, Shoutable):
for e in data.findall('event') for e in data.findall('event')
] ]
def get_past_events(self, @depaginate
limit = None): def get_past_events(self, limit = None, page = None):
params = self._default_params({'method': 'user.getPastEvents'}) params = self._default_params({'method': 'user.getPastEvents'})
if limit is not None: if limit is not None:
params.update({'limit': limit}) params.update({'limit': limit})
if page is not None:
params.update({'page': page})
@lazylist
def gen(lst):
data = self._api._fetch_data(params).find('events') data = self._api._fetch_data(params).find('events')
total_pages = int(data.attrib['totalPages']) total_pages = int(data.attrib['totalPages'])
yield total_pages
@lazylist
def gen2(lst, data):
for e in data.findall('event'): for e in data.findall('event'):
yield Event.create_from_data(self._api, e) yield Event.create_from_data(self._api, e)
for e in gen2(data):
yield e
for page in xrange(2, total_pages+1):
params.update({'page': page})
data = self._api._fetch_data(params).find('events')
for e in gen2(data):
yield e
return gen()
@cached_property @cached_property
def past_events(self): def past_events(self):
return self.get_past_events() return self.get_past_events()
@authenticate @authenticate
def get_recommended_events(self, limit = None): @depaginate
def get_recommended_events(self, limit = None, page = None):
params = {'method': 'user.getRecommendedEvents'} params = {'method': 'user.getRecommendedEvents'}
if limit is not None: if limit is not None:
params.update({'limit': limit}) params.update({'limit': limit})
if page is not None:
@lazylist
def gen(lst):
data = self._api._fetch_data(params, sign = True, session = True).find('events')
total_pages = int(data.attrib['totalPages'])
@lazylist
def gen2(lst, data):
for e in data.findall('event'):
yield Event.create_from_data(self._api, e)
for e in gen2(data):
yield e
for page in xrange(2, total_pages+1):
params.update({'page': page}) params.update({'page': page})
data = self._api._fetch_data(params, sign = True, session = True).find('events') data = self._api._fetch_data(params, sign = True, session = True).find('events')
for e in gen2(data): total_pages = int(data.attrib['totalPages'])
yield e yield total_pages
return gen() for e in data.findall('event'):
yield Event.create_from_data(self._api, e)
@cached_property @cached_property
def recommended_events(self): def recommended_events(self):
@ -411,16 +387,16 @@ class User(LastfmBase, Cacheable, Shoutable):
@cached_property @cached_property
@authenticate @authenticate
def recommended_artists(self): @depaginate
def recommended_artists(self, page = None):
params = {'method': 'user.getRecommendedArtists'} params = {'method': 'user.getRecommendedArtists'}
if page is not None:
params.update({'page': page})
@lazylist
def gen(lst):
data = self._api._fetch_data(params, sign = True, session = True).find('recommendations') data = self._api._fetch_data(params, sign = True, session = True).find('recommendations')
total_pages = int(data.attrib['totalPages']) total_pages = int(data.attrib['totalPages'])
yield total_pages
@lazylist
def gen2(lst, data):
for a in data.findall('artist'): for a in data.findall('artist'):
yield Artist( yield Artist(
self._api, self._api,
@ -431,16 +407,6 @@ class User(LastfmBase, Cacheable, Shoutable):
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 gen2(data):
yield a
for page in xrange(2, total_pages+1):
params.update({'page': page})
data = self._api._fetch_data(params, sign = True, session = True).find('recommendations')
for a in gen2(data):
yield a
return gen()
def get_top_tracks(self, period = None): def get_top_tracks(self, period = None):
params = self._default_params({'method': 'user.getTopTracks'}) params = self._default_params({'method': 'user.getTopTracks'})
if period is not None: if period is not None:
@ -755,19 +721,19 @@ class User(LastfmBase, Cacheable, Shoutable):
def user(self): def user(self):
return self._user return self._user
def get_albums(self, @depaginate
limit = None): def get_albums(self, limit = None, page = None):
params = self._default_params({'method': 'library.getAlbums'}) params = self._default_params({'method': 'library.getAlbums'})
if limit is not None: if limit is not None:
params.update({'limit': limit}) params.update({'limit': limit})
if page is not None:
params.update({'page': page})
@lazylist try:
def gen(lst):
data = self._api._fetch_data(params).find('albums') data = self._api._fetch_data(params).find('albums')
total_pages = int(data.attrib['totalPages']) total_pages = int(data.attrib['totalPages'])
yield total_pages
@lazylist
def gen2(lst, data):
for a in data.findall('album'): for a in data.findall('album'):
yield Album( yield Album(
self._api, self._api,
@ -788,20 +754,8 @@ class User(LastfmBase, Cacheable, Shoutable):
playcount = int(a.findtext('playcount')), playcount = int(a.findtext('playcount')),
) )
) )
for a in gen2(data):
yield a
for page in xrange(2, total_pages+1):
params.update({'page': page})
try:
data = self._api._fetch_data(params).find('albums')
except LastfmError: except LastfmError:
continue return
for a in gen2(data):
yield a
return gen()
@cached_property @cached_property
def albums(self): def albums(self):
@ -825,19 +779,19 @@ class User(LastfmBase, Cacheable, Shoutable):
self._api._post_data(params) self._api._post_data(params)
self._albums = None self._albums = None
def get_artists(self, @depaginate
limit = None): def get_artists(self, limit = None, page = None):
params = self._default_params({'method': 'library.getArtists'}) params = self._default_params({'method': 'library.getArtists'})
if limit is not None: if limit is not None:
params.update({'limit': limit}) params.update({'limit': limit})
if page is not None:
params.update({'page': page})
@lazylist try:
def gen(lst):
data = self._api._fetch_data(params).find('artists') data = self._api._fetch_data(params).find('artists')
total_pages = int(data.attrib['totalPages']) total_pages = int(data.attrib['totalPages'])
yield total_pages
@lazylist
def gen2(lst, data):
for a in data.findall('artist'): for a in data.findall('artist'):
yield Artist( yield Artist(
self._api, self._api,
@ -853,19 +807,8 @@ class User(LastfmBase, Cacheable, Shoutable):
streamable = (a.findtext('streamable') == "1"), streamable = (a.findtext('streamable') == "1"),
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 gen2(data):
yield a
for page in xrange(2, total_pages+1):
params.update({'page': page})
try:
data = self._api._fetch_data(params).find('artists')
except LastfmError: except LastfmError:
continue return
for a in gen2(data):
yield a
return gen()
@cached_property @cached_property
def artists(self): def artists(self):
@ -881,19 +824,19 @@ class User(LastfmBase, Cacheable, Shoutable):
self._api._post_data(params) self._api._post_data(params)
self._artists = None self._artists = None
def get_tracks(self, @depaginate
limit = None): def get_tracks(self, limit = None, page = None):
params = self._default_params({'method': 'library.getTracks'}) params = self._default_params({'method': 'library.getTracks'})
if limit is not None: if limit is not None:
params.update({'limit': limit}) params.update({'limit': limit})
if page is not None:
params.update({'page': page})
@lazylist try:
def gen(lst):
data = self._api._fetch_data(params).find('tracks') data = self._api._fetch_data(params).find('tracks')
total_pages = int(data.attrib['totalPages']) total_pages = int(data.attrib['totalPages'])
yield total_pages
@lazylist
def gen2(lst, data):
for t in data.findall('track'): for t in data.findall('track'):
yield Track( yield Track(
self._api, self._api,
@ -916,20 +859,8 @@ class User(LastfmBase, Cacheable, Shoutable):
full_track = (t.find('streamable').attrib['fulltrack'] == '1'), full_track = (t.find('streamable').attrib['fulltrack'] == '1'),
image = dict([(i.get('size'), i.text) for i in t.findall('image')]), image = dict([(i.get('size'), i.text) for i in t.findall('image')]),
) )
for t in gen2(data):
yield t
for page in xrange(2, total_pages+1):
params.update({'page': page})
data = None
try:
data = self._api._fetch_data(params).find('tracks')
except LastfmError: except LastfmError:
continue return
for t in gen2(data):
yield t
return gen()
@cached_property @cached_property
def tracks(self): def tracks(self):

View File

@ -7,8 +7,7 @@ __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.lazylist import lazylist from lastfm.decorators import cached_property, depaginate
from lastfm.decorators import cached_property
class Venue(LastfmBase, Cacheable, Searchable): class Venue(LastfmBase, Cacheable, Searchable):
"""A class representing a venue of an event""" """A class representing a venue of an event"""
@ -57,32 +56,21 @@ class Venue(LastfmBase, Cacheable, Searchable):
for e in data.findall('event') for e in data.findall('event')
] ]
def get_past_events(self, @depaginate
limit = None): def get_past_events(self, limit = None, page = None):
params = self._default_params({'method': 'venue.getPastEvents'}) params = self._default_params({'method': 'venue.getPastEvents'})
if limit is not None: if limit is not None:
params.update({'limit': limit}) params.update({'limit': limit})
if page is not None:
params.update({'page': page})
@lazylist
def gen(lst):
data = self._api._fetch_data(params).find('events') data = self._api._fetch_data(params).find('events')
total_pages = int(data.attrib['totalPages']) total_pages = int(data.attrib['totalPages'])
yield total_pages
@lazylist
def gen2(lst, data):
for e in data.findall('event'): for e in data.findall('event'):
yield Event.create_from_data(self._api, e) yield Event.create_from_data(self._api, e)
for e in gen2(data):
yield e
for page in xrange(2, total_pages+1):
params.update({'page': page})
data = self._api._fetch_data(params).find('events')
for e in gen2(data):
yield e
return gen()
@cached_property @cached_property
def past_events(self): def past_events(self):
return self.get_past_events() return self.get_past_events()
@ -96,6 +84,9 @@ class Venue(LastfmBase, Cacheable, Searchable):
@staticmethod @staticmethod
def _search_yield_func(api, venue): def _search_yield_func(api, venue):
latitude = venue.findtext('location/{%s}point/{%s}lat' % ((Location.XMLNS,)*2))
longitude = venue.findtext('location/{%s}point/{%s}long' % ((Location.XMLNS,)*2))
return Venue( return Venue(
api, api,
id = int(venue.findtext('id')), id = int(venue.findtext('id')),
@ -109,12 +100,8 @@ class Venue(LastfmBase, Cacheable, Searchable):
), ),
street = venue.findtext('location/street'), street = venue.findtext('location/street'),
postal_code = venue.findtext('location/postalcode'), postal_code = venue.findtext('location/postalcode'),
latitude = float(venue.findtext( latitude = (latitude.strip()!= '') and float(latitude) or None,
'location/{%s}point/{%s}lat' % ((Location.XMLNS,)*2) longitude = (longitude.strip()!= '') and float(longitude) or None,
)),
longitude = float(venue.findtext(
'location/{%s}point/{%s}long' % ((Location.XMLNS,)*2)
)),
), ),
url = venue.findtext('url') url = venue.findtext('url')
) )