implemented weeklyXXXChartList properties (as lazy list) in user and group modules. added lazylist module.

master
Abhinav Sarkar 2008-08-27 17:03:40 +00:00
parent 7b3af16612
commit e619c69822
4 changed files with 224 additions and 36 deletions

View File

@ -5,6 +5,7 @@ __version__ = "0.1"
__license__ = "GNU Lesser General Public License"
from base import LastfmBase
from lazylist import lazylist
class Group(LastfmBase):
"""A class representing a group on last.fm."""
@ -28,55 +29,72 @@ class Group(LastfmBase):
WeeklyChart.createFromData(self.__api, self, c)
for c in data.findall('chart')
]
def _checkWeeklyChartParams(self, params, start = None, end = None):
if (start is not None and end is None) or (start is None and end is not None):
raise LastfmError("both start and end have to be provided.")
if start is not None and end is not None:
if isinstance(start, datetime) and isinstance(end, datetime):
params.update({
'start': int(time.mktime(start.timetuple())),
'end': int(time.mktime(end.timetuple()))
})
else:
raise LastfmError("start and end must be datetime.datetime instances")
return params
def getWeeklyAlbumChart(self,
start = None,
end = None):
params = {'method': 'group.getweeklyalbumchart', 'group': self.name}
params = self._checkWeeklyChartParams(params, start, end)
params = WeeklyChart._checkWeeklyChartParams(params, start, end)
data = self.__api._fetchData(params).find('weeklyalbumchart')
return WeeklyAlbumChart.createFromData(self.__api, self, data)
@LastfmBase.cachedProperty
def recentWeeklyAlbumChart(self):
return self.getWeeklyAlbumChart()
@LastfmBase.cachedProperty
def weeklyAlbumChartList(self):
wcl = list(self.weeklyChartList)
wcl.reverse()
@lazylist
def gen(lst):
for wc in wcl:
yield self.getWeeklyAlbumChart(wc.start, wc.end)
return gen()
def getWeeklyArtistChart(self,
start = None,
end = None):
params = {'method': 'group.getweeklyartistchart', 'group': self.name}
params = self._checkWeeklyChartParams(params, start, end)
params = WeeklyChart._checkWeeklyChartParams(params, start, end)
data = self.__api._fetchData(params).find('weeklyartistchart')
return WeeklyArtistChart.createFromData(self.__api, self, data)
@LastfmBase.cachedProperty
def recentWeeklyArtistChart(self):
return self.getWeeklyArtistChart()
@LastfmBase.cachedProperty
def weeklyArtistChartList(self):
wcl = list(self.weeklyChartList)
wcl.reverse()
@lazylist
def gen(lst):
for wc in wcl:
yield self.getWeeklyArtistChart(wc.start, wc.end)
return gen()
def getWeeklyTrackChart(self,
start = None,
end = None):
params = {'method': 'group.getweeklytrackchart', 'group': self.name}
params = self._checkWeeklyChartParams(params, start, end)
params = WeeklyChart._checkWeeklyChartParams(params, start, end)
data = self.__api._fetchData(params).find('weeklytrackchart')
return WeeklyTrackChart.createFromData(self.__api, self, data)
@LastfmBase.cachedProperty
def recentWeeklyTrackChart(self):
return self.getWeeklyTrackChart()
@LastfmBase.cachedProperty
def weeklyTrackChartList(self):
wcl = list(self.weeklyChartList)
wcl.reverse()
@lazylist
def gen(lst):
for wc in wcl:
yield self.getWeeklyTrackChart(wc.start, wc.end)
return gen()
@staticmethod
def hashFunc(*args, **kwds):
@ -97,9 +115,6 @@ class Group(LastfmBase):
def __repr__(self):
return "<lastfm.Group: %s>" % self.name
import time
from datetime import datetime
from api import Api
from error import LastfmError
from weeklychart import WeeklyChart, WeeklyAlbumChart, WeeklyArtistChart, WeeklyTrackChart

139
src/lazylist.py Normal file
View File

@ -0,0 +1,139 @@
"""Module for the creation and use of iterator-based lazy lists.
this module defines a class LazyList which can be used to represent sequences
of values generated lazily. One can also create recursively defined lazy lists
that generate their values based on ones previously generated.
Backport to python 2.5 by Michael Pust
"""
__author__ = 'Dan Spitz'
__all__ = ('LazyList', 'RecursiveLazyList', 'lazylist')
import itertools
class LazyList(object):
"""A Sequence whose values are computed lazily by an iterator.
"""
def __init__(self, iterable):
self._exhausted = False
self._iterator = iter(iterable)
self._data = []
def __len__(self):
"""Get the length of a LazyList's computed data."""
return len(self._data)
def __getitem__(self, i):
"""Get an item from a LazyList.
i should be a positive integer or a slice object."""
if isinstance(i, int):
#index has not yet been yielded by iterator (or iterator exhausted
#before reaching that index)
if i >= len(self):
self.exhaust(i)
elif i < 0:
raise ValueError('cannot index LazyList with negative number')
return self._data[i]
#LazyList slices are iterators over a portion of the list.
elif isinstance(i, slice):
start, stop, step = i.start, i.stop, i.step
if any(x is not None and x < 0 for x in (start, stop, step)):
raise ValueError('cannot index or step through a LazyList with'
'a negative number')
#set start and step to their integer defaults if they are None.
if start is None:
start = 0
if step is None:
step = 1
def LazyListIterator():
count = start
predicate = ((lambda: True) if stop is None
else (lambda: count < stop))
while predicate():
try:
yield self[count]
#slices can go out of actual index range without raising an
#error
except IndexError:
break
count += step
return LazyListIterator()
raise TypeError('i must be an integer or slice')
def __iter__(self):
"""return an iterator over each value in the sequence,
whether it has been computed yet or not."""
return self[:]
def computed(self):
"""Return an iterator over the values in a LazyList that have
already been computed."""
return self[:len(self)]
def exhaust(self, index = None):
"""Exhaust the iterator generating this LazyList's values.
if index is None, this will exhaust the iterator completely.
Otherwise, it will iterate over the iterator until either the list
has a value for index or the iterator is exhausted.
"""
if self._exhausted:
return
if index is None:
ind_range = itertools.count(len(self))
else:
ind_range = range(len(self), index + 1)
for ind in ind_range:
try:
self._data.append(self._iterator.next())
except StopIteration: #iterator is fully exhausted
self._exhausted = True
break
class RecursiveLazyList(LazyList):
def __init__(self, prod, *args, **kwds):
super(RecursiveLazyList,self).__init__(prod(self,*args, **kwds))
class RecursiveLazyListFactory:
def __init__(self, producer):
self._gen = producer
def __call__(self,*a,**kw):
return RecursiveLazyList(self._gen,*a,**kw)
def lazylist(gen):
"""Decorator for creating a RecursiveLazyList subclass.
This should decorate a generator function taking the LazyList object as its
first argument which yields the contents of the list in order.
"""
return RecursiveLazyListFactory(gen)
#two examples
if __name__ == '__main__':
#fibonnacci sequence in a lazy list.
@lazylist
def fibgen(lst):
yield 0
yield 1
for a, b in itertools.izip(lst, lst[1:]):
yield a + b
fibs = fibgen() #now fibs can be indexed or iterated over as if it were
#an infinitely long list containing the fibonnaci sequence
#prime numbers in a lazy list.
@lazylist
def primegen(lst):
yield 2
for candidate in itertools.count(3): #start at next number after 2
#if candidate is not divisible by any smaller prime numbers,
#it is a prime.
if all(candidate % p for p in lst.computed()):
yield candidate
primes = primegen() #same for primes- treat it like an infinitely long list
#containing all prime numbers.
print fibs[0], fibs[1], fibs[2], primes[0], primes[1], primes[2]
print list(fibs[:10]), list(primes[:10])

View File

@ -5,6 +5,7 @@ __version__ = "0.1"
__license__ = "GNU Lesser General Public License"
from base import LastfmBase
from lazylist import lazylist
class User(LastfmBase):
"""A class representing an user."""
@ -349,49 +350,56 @@ class User(LastfmBase):
WeeklyChart.createFromData(self.__api, self, c)
for c in data.findall('chart')
]
def _checkWeeklyChartParams(self, params, start = None, end = None):
if (start is not None and end is None) or (start is None and end is not None):
raise LastfmError("both start and end have to be provided.")
if start is not None and end is not None:
if isinstance(start, datetime) and isinstance(end, datetime):
params.update({
'start': int(time.mktime(start.timetuple())),
'end': int(time.mktime(end.timetuple()))
})
else:
raise LastfmError("start and end must be datetime.datetime instances")
return params
def getWeeklyAlbumChart(self,
start = None,
end = None):
params = {'method': 'user.getweeklyalbumchart', 'user': self.name}
params = self._checkWeeklyChartParams(params, start, end)
params = WeeklyChart._checkWeeklyChartParams(params, start, end)
data = self.__api._fetchData(params).find('weeklyalbumchart')
return WeeklyAlbumChart.createFromData(self.__api, self, data)
@LastfmBase.cachedProperty
def recentWeeklyAlbumChart(self):
return self.getWeeklyAlbumChart()
@LastfmBase.cachedProperty
def weeklyAlbumChartList(self):
wcl = list(self.weeklyChartList)
wcl.reverse()
@lazylist
def gen(lst):
for wc in wcl:
yield self.getWeeklyAlbumChart(wc.start, wc.end)
return gen()
def getWeeklyArtistChart(self,
start = None,
end = None):
params = {'method': 'user.getweeklyartistchart', 'user': self.name}
params = self._checkWeeklyChartParams(params, start, end)
params = WeeklyChart._checkWeeklyChartParams(params, start, end)
data = self.__api._fetchData(params).find('weeklyartistchart')
return WeeklyArtistChart.createFromData(self.__api, self, data)
@LastfmBase.cachedProperty
def recentWeeklyArtistChart(self):
return self.getWeeklyArtistChart()
@LastfmBase.cachedProperty
def weeklyArtistChartList(self):
wcl = list(self.weeklyChartList)
wcl.reverse()
@lazylist
def gen(lst):
for wc in wcl:
yield self.getWeeklyArtistChart(wc.start, wc.end)
return gen()
def getWeeklyTrackChart(self,
start = None,
end = None):
params = {'method': 'user.getweeklytrackchart', 'user': self.name}
params = self._checkWeeklyChartParams(params, start, end)
params = WeeklyChart._checkWeeklyChartParams(params, start, end)
data = self.__api._fetchData(params).find('weeklytrackchart')
return WeeklyTrackChart.createFromData(self.__api, self, data)
@ -399,6 +407,16 @@ class User(LastfmBase):
def recentWeeklyTrackChart(self):
return self.getWeeklyTrackChart()
@LastfmBase.cachedProperty
def weeklyTrackChartList(self):
wcl = list(self.weeklyChartList)
wcl.reverse()
@lazylist
def gen(lst):
for wc in wcl:
yield self.getWeeklyTrackChart(wc.start, wc.end)
return gen()
@property
def library(self):
return self.__lirary

View File

@ -33,7 +33,22 @@ class WeeklyChart(LastfmBase):
start = datetime.utcfromtimestamp(int(data.attrib['from'])),
end = datetime.utcfromtimestamp(int(data.attrib['to']))
)
@staticmethod
def _checkWeeklyChartParams(params, start = None, end = None):
if (start is not None and end is None) or (start is None and end is not None):
raise LastfmError("both start and end have to be provided.")
if start is not None and end is not None:
if isinstance(start, datetime) and isinstance(end, datetime):
params.update({
'from': int(calendar.timegm(start.timetuple())),
'to': int(calendar.timegm(end.timetuple()))
})
else:
raise LastfmError("start and end must be datetime.datetime instances")
return params
@staticmethod
def hashFunc(*args, **kwds):
try:
@ -190,6 +205,7 @@ class WeeklyTrackChart(WeeklyChart):
)
from datetime import datetime
import calendar
from album import Album
from artist import Artist