implemented weeklyXXXChartList properties (as lazy list) in user and group modules. added lazylist module.
This commit is contained in:
parent
7b3af16612
commit
e619c69822
53
src/group.py
53
src/group.py
@ -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,25 +29,12 @@ 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)
|
||||
|
||||
@ -54,11 +42,21 @@ class Group(LastfmBase):
|
||||
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)
|
||||
|
||||
@ -66,11 +64,21 @@ class Group(LastfmBase):
|
||||
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)
|
||||
|
||||
@ -78,6 +86,16 @@ class Group(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()
|
||||
|
||||
@staticmethod
|
||||
def hashFunc(*args, **kwds):
|
||||
try:
|
||||
@ -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
139
src/lazylist.py
Normal 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])
|
50
src/user.py
50
src/user.py
@ -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,25 +350,12 @@ 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)
|
||||
|
||||
@ -375,11 +363,21 @@ class User(LastfmBase):
|
||||
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)
|
||||
|
||||
@ -387,11 +385,21 @@ class User(LastfmBase):
|
||||
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
|
||||
|
@ -34,6 +34,21 @@ class WeeklyChart(LastfmBase):
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user