added async_callback decorator for supporting asynchronous calls. applied it to get_* methods in Api class.
parent
d1656c1a84
commit
238d88f7e8
|
@ -6,7 +6,7 @@ __version__ = "0.2"
|
|||
__license__ = "GNU Lesser General Public License"
|
||||
__package__ = "lastfm"
|
||||
|
||||
from lastfm.decorators import cached_property
|
||||
from lastfm.decorators import cached_property, async_callback
|
||||
|
||||
class Api(object):
|
||||
"""The class representing the last.fm web services API."""
|
||||
|
@ -169,6 +169,7 @@ class Api(object):
|
|||
"""
|
||||
self._request_headers['User-Agent'] = user_agent
|
||||
|
||||
@async_callback
|
||||
def get_album(self,
|
||||
album = None,
|
||||
artist = None,
|
||||
|
@ -196,6 +197,7 @@ class Api(object):
|
|||
artist = artist.name
|
||||
return Album.get_info(self, artist, album, mbid)
|
||||
|
||||
@async_callback
|
||||
def search_album(self, album, limit = None):
|
||||
"""
|
||||
Search for an album by name.
|
||||
|
@ -212,6 +214,7 @@ class Api(object):
|
|||
"""
|
||||
return Album.search(self, search_item = album, limit = limit)
|
||||
|
||||
@async_callback
|
||||
def get_artist(self,
|
||||
artist = None,
|
||||
mbid = None):
|
||||
|
@ -232,7 +235,8 @@ class Api(object):
|
|||
@see: L{Artist.get_info}
|
||||
"""
|
||||
return Artist.get_info(self, artist, mbid)
|
||||
|
||||
|
||||
@async_callback
|
||||
def search_artist(self,
|
||||
artist,
|
||||
limit = None):
|
||||
|
@ -251,6 +255,7 @@ class Api(object):
|
|||
"""
|
||||
return Artist.search(self, search_item = artist, limit = limit)
|
||||
|
||||
@async_callback
|
||||
def get_event(self, event):
|
||||
"""
|
||||
Get an event object.
|
||||
|
@ -266,7 +271,8 @@ class Api(object):
|
|||
@see: L{Event.get_info}
|
||||
"""
|
||||
return Event.get_info(self, event)
|
||||
|
||||
|
||||
@async_callback
|
||||
def get_location(self, city):
|
||||
"""
|
||||
Get a location object.
|
||||
|
@ -279,6 +285,7 @@ class Api(object):
|
|||
"""
|
||||
return Location(self, city = city)
|
||||
|
||||
@async_callback
|
||||
def get_country(self, name):
|
||||
"""
|
||||
Get a country object.
|
||||
|
@ -290,7 +297,8 @@ class Api(object):
|
|||
@rtype: L{Country}
|
||||
"""
|
||||
return Country(self, name = name)
|
||||
|
||||
|
||||
@async_callback
|
||||
def get_group(self, name):
|
||||
"""
|
||||
Get a group object.
|
||||
|
@ -303,6 +311,7 @@ class Api(object):
|
|||
"""
|
||||
return Group(self, name = name)
|
||||
|
||||
@async_callback
|
||||
def get_playlist(self, url):
|
||||
"""
|
||||
Get a playlist object.
|
||||
|
@ -316,7 +325,8 @@ class Api(object):
|
|||
@see: L{Playlist.fetch}
|
||||
"""
|
||||
return Playlist.fetch(self, url)
|
||||
|
||||
|
||||
@async_callback
|
||||
def get_tag(self, name):
|
||||
"""
|
||||
Get a tag object.
|
||||
|
@ -329,6 +339,7 @@ class Api(object):
|
|||
"""
|
||||
return Tag(self, name = name)
|
||||
|
||||
@async_callback
|
||||
def get_global_top_tags(self):
|
||||
"""
|
||||
Get the top global tags on Last.fm, sorted by popularity (number of times used).
|
||||
|
@ -338,6 +349,7 @@ class Api(object):
|
|||
"""
|
||||
return Tag.get_top_tags(self)
|
||||
|
||||
@async_callback
|
||||
def search_tag(self,
|
||||
tag,
|
||||
limit = None):
|
||||
|
@ -356,6 +368,7 @@ class Api(object):
|
|||
"""
|
||||
return Tag.search(self, search_item = tag, limit = limit)
|
||||
|
||||
@async_callback
|
||||
def compare_taste(self,
|
||||
type1, type2,
|
||||
value1, value2,
|
||||
|
@ -383,6 +396,7 @@ class Api(object):
|
|||
"""
|
||||
return Tasteometer.compare(self, type1, type2, value1, value2, limit)
|
||||
|
||||
@async_callback
|
||||
def get_track(self, track, artist = None, mbid = None):
|
||||
"""
|
||||
Get a track object.
|
||||
|
@ -405,11 +419,9 @@ class Api(object):
|
|||
if isinstance(artist, Artist):
|
||||
artist = artist.name
|
||||
return Track.get_info(self, artist, track, mbid)
|
||||
|
||||
def search_track(self,
|
||||
track,
|
||||
artist = None,
|
||||
limit = None):
|
||||
|
||||
@async_callback
|
||||
def search_track(self, track, artist = None, limit = None):
|
||||
"""
|
||||
Search for a track by name.
|
||||
|
||||
|
@ -429,6 +441,7 @@ class Api(object):
|
|||
artist = artist.name
|
||||
return Track.search(self, search_item = track, limit = limit, artist = artist)
|
||||
|
||||
@async_callback
|
||||
def get_user(self, name):
|
||||
"""
|
||||
Get an user object.
|
||||
|
@ -445,6 +458,7 @@ class Api(object):
|
|||
"""
|
||||
return User.get_info(self, name = name)
|
||||
|
||||
@async_callback
|
||||
def get_authenticated_user(self):
|
||||
"""
|
||||
Get the currently authenticated user.
|
||||
|
@ -459,6 +473,7 @@ class Api(object):
|
|||
else:
|
||||
raise AuthenticationFailedError("session key must be present to call this method")
|
||||
|
||||
@async_callback
|
||||
def get_venue(self, venue):
|
||||
"""
|
||||
Get a venue object.
|
||||
|
@ -478,6 +493,7 @@ class Api(object):
|
|||
except IndexError:
|
||||
raise InvalidParametersError("No such venue exists")
|
||||
|
||||
@async_callback
|
||||
def search_venue(self, venue, limit = None, country = None):
|
||||
"""
|
||||
Search for a venue by name.
|
||||
|
|
|
@ -100,6 +100,16 @@ def authenticate(func):
|
|||
return wrapper
|
||||
|
||||
def depaginate(func):
|
||||
"""
|
||||
A decorator to depaginate the search results.
|
||||
|
||||
@param func: a function that returns the first page of search results
|
||||
@type func: C{function}
|
||||
|
||||
@return: a function that wraps the original function and returns
|
||||
a L{lazylist} of all search results (all pages)
|
||||
@rtype: C{function}
|
||||
"""
|
||||
from lastfm.lazylist import lazylist
|
||||
def wrapper(*args, **kwargs):
|
||||
@lazylist
|
||||
|
@ -118,5 +128,82 @@ def depaginate(func):
|
|||
wrapper.__doc__ = func.__doc__
|
||||
return wrapper
|
||||
|
||||
def async_callback(func):
|
||||
"""
|
||||
A decorator to convert a synchronous (blocking) function into
|
||||
an asynchronous (non-blocking) function.
|
||||
|
||||
Pass a callback function as a keyword argument
|
||||
(C{func(other argument... , callback = callback)}) or positional argument
|
||||
(C{func(other argument... , callback)}) to the function to activate the
|
||||
asynchronous behaviour. The callback function is called with the return value
|
||||
of the original function when it returns. If an exception is raised in the
|
||||
original function, then the callback function is called with that exception.
|
||||
If the callback function is not given then the original function is called
|
||||
synchronously (it blocks the caller function) and its return value is returned.
|
||||
|
||||
All the functions on which this decorator is applied get the signature:
|
||||
C{func(self, *args, **kwargs)}. Refer to the documentation or source code of
|
||||
the original function for the correct function signature.
|
||||
|
||||
@param func: a synchronous (blocking) function
|
||||
@type func: C{function}
|
||||
|
||||
@return: an asynchronous (non-blocking) function that wraps the
|
||||
original synchronous (blocking) function
|
||||
@rtype: C{function}
|
||||
"""
|
||||
from threading import Thread
|
||||
def wrapper(self, *args, **kwargs):
|
||||
callback = None
|
||||
for a in args:
|
||||
if callable(a):
|
||||
callback = a
|
||||
args = list(args)
|
||||
args.remove(a)
|
||||
args = tuple(args)
|
||||
break
|
||||
if 'callback' in kwargs:
|
||||
callback = kwargs['callback']
|
||||
del kwargs['callback']
|
||||
|
||||
if (callback is not None and callable(callback)):
|
||||
def async_call():
|
||||
result = None
|
||||
try:
|
||||
result = func(self, *args, **kwargs)
|
||||
except Exception, e:
|
||||
result = e
|
||||
callback(result)
|
||||
thread = Thread(target = async_call)
|
||||
thread.start()
|
||||
return
|
||||
return func(self, *args, **kwargs)
|
||||
|
||||
wrapper.__doc__ = "%s\n @see: L{async_callback}" % func.__doc__
|
||||
return wrapper
|
||||
|
||||
def _get_arg_string(argspecs):
|
||||
arg_str = ""
|
||||
args = argspecs.args
|
||||
args.remove('self')
|
||||
defaults = argspecs.defaults
|
||||
|
||||
if defaults is not None:
|
||||
defargs = args[-len(defaults):]
|
||||
nondefargs = args[:-len(defaults)]
|
||||
nondefargs_str = ", ".join(nondefargs)
|
||||
defargs_str = ", ".join(["%s = %s" % (defargs[i], defaults[i]) for i in xrange(len(defargs))])
|
||||
if nondefargs_str != '' and defargs_str != '':
|
||||
arg_str = "%s, %s" % (nondefargs_str, defargs_str)
|
||||
elif nondefargs_str != '':
|
||||
arg_str = nondefargs_str
|
||||
elif defargs_str != '':
|
||||
arg_str = defargs_str
|
||||
else:
|
||||
arg_str = ", ".join(args)
|
||||
print arg_str
|
||||
return arg_str
|
||||
|
||||
import copy
|
||||
from lastfm.error import LastfmError, AuthenticationFailedError
|
|
@ -119,41 +119,41 @@ class WeeklyAlbumChart(WeeklyChart):
|
|||
end = datetime.utcfromtimestamp(int(data.attrib['to'])),
|
||||
)
|
||||
return WeeklyAlbumChart(
|
||||
subject = subject,
|
||||
start = datetime.utcfromtimestamp(int(data.attrib['from'])),
|
||||
end = datetime.utcfromtimestamp(int(data.attrib['to'])),
|
||||
stats = Stats(
|
||||
subject = subject,
|
||||
playcount = reduce(
|
||||
lambda x,y:(
|
||||
x + int(y.findtext('playcount'))
|
||||
),
|
||||
data.findall('album'),
|
||||
0
|
||||
)
|
||||
),
|
||||
albums = [
|
||||
Album(
|
||||
api,
|
||||
subject = w,
|
||||
name = a.findtext('name'),
|
||||
mbid = a.findtext('mbid'),
|
||||
artist = Artist(
|
||||
api,
|
||||
subject = w,
|
||||
name = a.findtext('artist'),
|
||||
mbid = a.find('artist').attrib['mbid'],
|
||||
),
|
||||
stats = Stats(
|
||||
subject = a.findtext('name'),
|
||||
rank = int(a.attrib['rank']),
|
||||
playcount = int(a.findtext('playcount')),
|
||||
),
|
||||
url = a.findtext('url'),
|
||||
)
|
||||
for a in data.findall('album')
|
||||
]
|
||||
)
|
||||
subject = subject,
|
||||
start = datetime.utcfromtimestamp(int(data.attrib['from'])),
|
||||
end = datetime.utcfromtimestamp(int(data.attrib['to'])),
|
||||
stats = Stats(
|
||||
subject = subject,
|
||||
playcount = reduce(
|
||||
lambda x,y:(
|
||||
x + int(y.findtext('playcount'))
|
||||
),
|
||||
data.findall('album'),
|
||||
0
|
||||
)
|
||||
),
|
||||
albums = [
|
||||
Album(
|
||||
api,
|
||||
subject = w,
|
||||
name = a.findtext('name'),
|
||||
mbid = a.findtext('mbid'),
|
||||
artist = Artist(
|
||||
api,
|
||||
subject = w,
|
||||
name = a.findtext('artist'),
|
||||
mbid = a.find('artist').attrib['mbid'],
|
||||
),
|
||||
stats = Stats(
|
||||
subject = a.findtext('name'),
|
||||
rank = int(a.attrib['rank']),
|
||||
playcount = int(a.findtext('playcount')),
|
||||
),
|
||||
url = a.findtext('url'),
|
||||
)
|
||||
for a in data.findall('album')
|
||||
]
|
||||
)
|
||||
|
||||
class WeeklyArtistChart(WeeklyChart):
|
||||
"""A class for representing the weekly artist charts"""
|
||||
|
@ -176,32 +176,34 @@ class WeeklyArtistChart(WeeklyChart):
|
|||
def get_count_attribute(artist):
|
||||
return {count_attribute: int(eval(artist.findtext(count_attribute)))}
|
||||
def get_count_attribute_sum(artists):
|
||||
return {count_attribute: reduce(lambda x,y:(x + int(eval(y.findtext(count_attribute)))), artists, 0)}
|
||||
return {count_attribute: reduce(
|
||||
lambda x, y:(x + int(eval(y.findtext(count_attribute)))), artists, 0
|
||||
)}
|
||||
|
||||
return WeeklyArtistChart(
|
||||
subject = subject,
|
||||
start = datetime.utcfromtimestamp(int(data.attrib['from'])),
|
||||
end = datetime.utcfromtimestamp(int(data.attrib['to'])),
|
||||
stats = Stats(
|
||||
subject = subject,
|
||||
**get_count_attribute_sum(data.findall('artist'))
|
||||
),
|
||||
artists = [
|
||||
Artist(
|
||||
api,
|
||||
subject = w,
|
||||
name = a.findtext('name'),
|
||||
mbid = a.findtext('mbid'),
|
||||
stats = Stats(
|
||||
subject = a.findtext('name'),
|
||||
rank = int(a.attrib['rank']),
|
||||
**get_count_attribute(a)
|
||||
),
|
||||
url = a.findtext('url'),
|
||||
)
|
||||
for a in data.findall('artist')
|
||||
]
|
||||
)
|
||||
subject = subject,
|
||||
start = datetime.utcfromtimestamp(int(data.attrib['from'])),
|
||||
end = datetime.utcfromtimestamp(int(data.attrib['to'])),
|
||||
stats = Stats(
|
||||
subject = subject,
|
||||
**get_count_attribute_sum(data.findall('artist'))
|
||||
),
|
||||
artists = [
|
||||
Artist(
|
||||
api,
|
||||
subject = w,
|
||||
name = a.findtext('name'),
|
||||
mbid = a.findtext('mbid'),
|
||||
stats = Stats(
|
||||
subject = a.findtext('name'),
|
||||
rank = int(a.attrib['rank']),
|
||||
**get_count_attribute(a)
|
||||
),
|
||||
url = a.findtext('url'),
|
||||
)
|
||||
for a in data.findall('artist')
|
||||
]
|
||||
)
|
||||
|
||||
class WeeklyTrackChart(WeeklyChart):
|
||||
"""A class for representing the weekly track charts"""
|
||||
|
@ -216,45 +218,45 @@ class WeeklyTrackChart(WeeklyChart):
|
|||
@staticmethod
|
||||
def create_from_data(api, subject, data):
|
||||
w = WeeklyChart(
|
||||
subject = subject,
|
||||
start = datetime.utcfromtimestamp(int(data.attrib['from'])),
|
||||
end = datetime.utcfromtimestamp(int(data.attrib['to'])),
|
||||
)
|
||||
subject = subject,
|
||||
start = datetime.utcfromtimestamp(int(data.attrib['from'])),
|
||||
end = datetime.utcfromtimestamp(int(data.attrib['to'])),
|
||||
)
|
||||
return WeeklyTrackChart(
|
||||
subject = subject,
|
||||
start = datetime.utcfromtimestamp(int(data.attrib['from'])),
|
||||
end = datetime.utcfromtimestamp(int(data.attrib['to'])),
|
||||
stats = Stats(
|
||||
subject = subject,
|
||||
playcount = reduce(
|
||||
lambda x,y:(
|
||||
x + int(y.findtext('playcount'))
|
||||
),
|
||||
data.findall('track'),
|
||||
0
|
||||
)
|
||||
),
|
||||
tracks = [
|
||||
Track(
|
||||
api,
|
||||
subject = w,
|
||||
name = t.findtext('name'),
|
||||
mbid = t.findtext('mbid'),
|
||||
artist = Artist(
|
||||
api,
|
||||
name = t.findtext('artist'),
|
||||
mbid = t.find('artist').attrib['mbid'],
|
||||
),
|
||||
stats = Stats(
|
||||
subject = t.findtext('name'),
|
||||
rank = int(t.attrib['rank']),
|
||||
playcount = int(t.findtext('playcount')),
|
||||
),
|
||||
url = t.findtext('url'),
|
||||
)
|
||||
for t in data.findall('track')
|
||||
]
|
||||
)
|
||||
subject = subject,
|
||||
start = datetime.utcfromtimestamp(int(data.attrib['from'])),
|
||||
end = datetime.utcfromtimestamp(int(data.attrib['to'])),
|
||||
stats = Stats(
|
||||
subject = subject,
|
||||
playcount = reduce(
|
||||
lambda x,y:(
|
||||
x + int(y.findtext('playcount'))
|
||||
),
|
||||
data.findall('track'),
|
||||
0
|
||||
)
|
||||
),
|
||||
tracks = [
|
||||
Track(
|
||||
api,
|
||||
subject = w,
|
||||
name = t.findtext('name'),
|
||||
mbid = t.findtext('mbid'),
|
||||
artist = Artist(
|
||||
api,
|
||||
name = t.findtext('artist'),
|
||||
mbid = t.find('artist').attrib['mbid'],
|
||||
),
|
||||
stats = Stats(
|
||||
subject = t.findtext('name'),
|
||||
rank = int(t.attrib['rank']),
|
||||
playcount = int(t.findtext('playcount')),
|
||||
),
|
||||
url = t.findtext('url'),
|
||||
)
|
||||
for t in data.findall('track')
|
||||
]
|
||||
)
|
||||
|
||||
class WeeklyTagChart(WeeklyChart):
|
||||
"""A class for representing the weekly tag charts"""
|
||||
|
|
Loading…
Reference in New Issue