added async_callback decorator for supporting asynchronous calls. applied it to get_* methods in Api class.

master
Abhinav Sarkar 2009-03-21 06:53:24 +00:00
parent d1656c1a84
commit 238d88f7e8
3 changed files with 212 additions and 107 deletions

View File

@ -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.

View File

@ -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

View File

@ -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"""