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,26 +53,15 @@ 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:
@lazylist params.update({'page': page})
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,38 +194,26 @@ 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
for u in data.findall('user'):
@lazylist yield User(
def gen2(lst, data): self._api,
for u in data.findall('user'): name = u.findtext('name'),
yield User( real_name = u.findtext('realname'),
self._api, image = dict([(i.get('size'), i.text) for i in u.findall('image')]),
name = u.findtext('name'), url = u.findtext('url')
real_name = u.findtext('realname'), )
image = dict([(i.get('size'), i.text) for i in u.findall('image')]),
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:

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,27 +28,15 @@ class Searchable(object):
if limit: if limit:
params.update({'limit': limit}) params.update({'limit': limit})
if page is not None:
@lazylist params.update({'page': page})
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 for a in data.findall('%smatches/%s'%(cls_name, cls_name)):
def gen2(lst, data): yield cls._search_yield_func(api, a)
for a in data.findall('%smatches/%s'%(cls_name, cls_name)):
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):

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."""
@ -110,62 +110,38 @@ class User(LastfmBase, Cacheable, Shoutable):
Event.create_from_data(self._api, e) Event.create_from_data(self._api, e)
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 data = self._api._fetch_data(params).find('events')
def gen(lst): total_pages = int(data.attrib['totalPages'])
data = self._api._fetch_data(params).find('events') yield total_pages
total_pages = int(data.attrib['totalPages']) for e in data.findall('event'):
yield Event.create_from_data(self._api, e)
@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})
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 params.update({'page': page})
def gen(lst): data = self._api._fetch_data(params, sign = True, session = True).find('events')
data = self._api._fetch_data(params, sign = True, session = True).find('events') total_pages = int(data.attrib['totalPages'])
total_pages = int(data.attrib['totalPages']) yield total_pages
for e in data.findall('event'):
@lazylist yield Event.create_from_data(self._api, e)
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})
data = self._api._fetch_data(params, sign = True, session = True).find('events')
for e in gen2(data):
yield e
return gen()
@cached_property @cached_property
def recommended_events(self): def recommended_events(self):
@ -411,35 +387,25 @@ 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:
@lazylist params.update({'page': page})
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 for a in data.findall('artist'):
def gen2(lst, data): yield Artist(
for a in data.findall('artist'): self._api,
yield Artist( name = a.findtext('name'),
self._api, mbid = a.findtext('mbid'),
name = a.findtext('name'), url = a.findtext('url'),
mbid = a.findtext('mbid'), streamable = (a.findtext('streamable') == "1"),
url = a.findtext('url'), image = dict([(i.get('size'), i.text) for i in a.findall('image')]),
streamable = (a.findtext('streamable') == "1"), )
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'})
@ -755,53 +721,41 @@ 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, subject = self,
subject = self, name = a.findtext('name'),
name = a.findtext('name'), artist = Artist(
artist = Artist( self._api,
self._api, subject = self,
subject = self, name = a.findtext('artist/name'),
name = a.findtext('artist/name'), mbid = a.findtext('artist/mbid'),
mbid = a.findtext('artist/mbid'), url = a.findtext('artist/url'),
url = a.findtext('artist/url'), ),
), mbid = a.findtext('mbid'),
mbid = a.findtext('mbid'), 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')]), stats = Stats(
stats = Stats( subject = a.findtext('name'),
subject = a.findtext('name'), playcount = int(a.findtext('playcount')),
playcount = int(a.findtext('playcount')), )
) )
) except LastfmError:
return
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:
continue
for a in gen2(data):
yield a
return gen()
@cached_property @cached_property
def albums(self): def albums(self):
@ -825,47 +779,36 @@ 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, subject = self,
subject = self, name = a.findtext('name'),
name = a.findtext('name'), mbid = a.findtext('mbid'),
mbid = a.findtext('mbid'), stats = Stats(
stats = Stats( subject = a.findtext('name'),
subject = a.findtext('name'), playcount = a.findtext('playcount') and int(a.findtext('playcount')) or None,
playcount = a.findtext('playcount') and int(a.findtext('playcount')) or None, tagcount = a.findtext('tagcount') and int(a.findtext('tagcount')) or None
tagcount = a.findtext('tagcount') and int(a.findtext('tagcount')) or None ),
), url = a.findtext('url'),
url = a.findtext('url'), 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')]), )
) except LastfmError:
return
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:
continue
for a in gen2(data):
yield a
return gen()
@cached_property @cached_property
def artists(self): def artists(self):
@ -881,55 +824,43 @@ 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:
@lazylist params.update({'page': page})
def gen(lst):
try:
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, subject = self,
subject = self, name = t.findtext('name'),
name = t.findtext('name'), artist = Artist(
artist = Artist( self._api,
self._api, subject = self,
subject = self, name = t.findtext('artist/name'),
name = t.findtext('artist/name'), mbid = t.findtext('artist/mbid'),
mbid = t.findtext('artist/mbid'), url = t.findtext('artist/url'),
url = t.findtext('artist/url'), ),
), mbid = t.findtext('mbid'),
mbid = t.findtext('mbid'), stats = Stats(
stats = Stats( subject = t.findtext('name'),
subject = t.findtext('name'), playcount = t.findtext('playcount') and int(t.findtext('playcount')) or None,
playcount = t.findtext('playcount') and int(t.findtext('playcount')) or None, tagcount = t.findtext('tagcount') and int(t.findtext('tagcount')) or None
tagcount = t.findtext('tagcount') and int(t.findtext('tagcount')) or None ),
), streamable = (t.findtext('streamable') == '1'),
streamable = (t.findtext('streamable') == '1'), 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')]), )
) except LastfmError:
return
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:
continue
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"""
@ -56,32 +55,21 @@ class Venue(LastfmBase, Cacheable, Searchable):
Event.create_from_data(self._api, e) Event.create_from_data(self._api, e)
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 data = self._api._fetch_data(params).find('events')
def gen(lst): total_pages = int(data.attrib['totalPages'])
data = self._api._fetch_data(params).find('events') yield total_pages
total_pages = int(data.attrib['totalPages'])
@lazylist for e in data.findall('event'):
def gen2(lst, data): yield Event.create_from_data(self._api, e)
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})
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):
@ -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,13 +100,9 @@ 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')
) )