added property_adder mixin. moved all "property" attributes of all classes to a Meta class. moved attribute setting code to LastfmBase.

This commit is contained in:
Abhinav Sarkar 2009-04-18 05:11:22 +00:00
parent 03c5cb0046
commit b30839a26f
21 changed files with 280 additions and 889 deletions

View File

@ -7,28 +7,20 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixin import cacheable, searchable, taggable, crawlable from lastfm.mixin import mixin
from lastfm.decorators import cached_property, top_property from lastfm.decorators import cached_property, top_property
@crawlable @mixin("crawlable", "taggable", "searchable",
@taggable "cacheable", "property_adder")
@searchable
@cacheable
class Album(LastfmBase): class Album(LastfmBase):
"""A class representing an album.""" """A class representing an album."""
def init(self, class Meta(object):
api, properties = ["name", "artist", "top_tags",
name = None, "streamable"]
artist = None, fillable_properties = ["id", "mbid", "url",
id = None, "release_date", "image", "stats", ]
mbid = None,
url = None, def init(self, api, subject = None, **kwargs):
release_date = None,
image = None,
stats = None,
top_tags = None,
streamable = None,
subject = None):
""" """
Create an Album object by providing all the data related to it. Create an Album object by providing all the data related to it.
@ -63,108 +55,16 @@ class Album(LastfmBase):
if not isinstance(api, Api): if not isinstance(api, Api):
raise InvalidParametersError("api reference must be supplied as an argument") raise InvalidParametersError("api reference must be supplied as an argument")
self._api = api self._api = api
self._name = name super(Album, self).init(**kwargs)
self._artist = artist self._stats = hasattr(self, "_stats") and Stats(
self._id = id subject = self,
self._mbid = mbid listeners = self._stats.listeners,
self._url = url playcount = self._stats.playcount,
self._release_date = release_date match = self._stats.match,
self._image = image rank = self._stats.rank
self._stats = stats and Stats( ) or None
subject = self,
listeners = stats.listeners,
playcount = stats.playcount,
match = stats.match,
rank = stats.rank
)
self._top_tags = top_tags
self._streamable = streamable
self._subject = subject self._subject = subject
@property
def name(self):
"""
name of the album
@rtype: L{str}
"""
return self._name
@property
def artist(self):
"""
artist of the album
@rtype: L{Artist}
"""
return self._artist
@property
def id(self):
"""
id of the album
@rtype: L{int}
"""
if self._id is None:
self._fill_info()
return self._id
@property
def mbid(self):
"""
MBID of the album
@rtype: L{str}
"""
if self._mbid is None:
self._fill_info()
return self._mbid
@property
def url(self):
"""
url of the album's page
@rtype: L{str}
"""
if self._url is None:
self._fill_info()
return self._url
@property
def release_date(self):
"""
release date of the album
@rtype: C{datetime.datetime}
"""
if self._release_date is None:
self._fill_info()
return self._release_date
@property
def image(self):
"""
cover images of the album
@rtype: L{dict}
"""
if self._image is None:
self._fill_info()
return self._image
@property
def stats(self):
"""
stats related to the album
@rtype: L{Stats}
"""
if self._stats is None:
self._fill_info()
return self._stats
@property
def streamable(self):
"""
is the album streamable on last.fm
@rtype: L{bool}
"""
return self._streamable
@cached_property @cached_property
def top_tags(self): def top_tags(self):
""" """

View File

@ -84,7 +84,7 @@ class Api(object):
self._debug = None self._debug = None
if self._debug is not None: if self._debug is not None:
Wormhole.enable() Wormhole.enable()
logging.set_api(self) logging.set_api(self)
@property @property
def api_key(self): def api_key(self):

View File

@ -7,29 +7,19 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixin import cacheable, searchable, sharable, shoutable, taggable, crawlable from lastfm.mixin import mixin
from lastfm.decorators import cached_property, top_property from lastfm.decorators import cached_property, top_property
@crawlable @mixin("crawlable", "shoutable", "sharable",
@shoutable "taggable", "searchable", "cacheable", "property_adder")
@sharable
@taggable
@searchable
@cacheable
class Artist(LastfmBase): class Artist(LastfmBase):
"""A class representing an artist.""" """A class representing an artist."""
def init(self, class Meta(object):
api, properties = ["name", "similar", "top_tags"]
name = None, fillable_properties = ["mbid", "url", "image",
mbid = None, "streamable", "stats", "bio"]
url = None,
image = None, def init(self, api, subject = None, **kwargs):
streamable = None,
stats = None,
similar = None,
top_tags = None,
bio = None,
subject = None):
""" """
Create an Artist object by providing all the data related to it. Create an Artist object by providing all the data related to it.
@ -63,87 +53,23 @@ class Artist(LastfmBase):
raise InvalidParametersError("api reference must be supplied as an argument") raise InvalidParametersError("api reference must be supplied as an argument")
self._api = api self._api = api
self._name = name super(Artist, self).init(**kwargs)
self._mbid = mbid self._stats = hasattr(self, "_stats") and Stats(
self._url = url subject = self,
self._image = image listeners = self._stats.listeners,
self._streamable = streamable playcount = self._stats.playcount,
self._stats = stats and Stats( weight = self._stats.weight,
subject = self, match = self._stats.match,
listeners = stats.listeners, rank = self._stats.rank
playcount = stats.playcount, ) or None
weight = stats.weight, self._bio = hasattr(self, "_bio") and Wiki(
match = stats.match, subject = self,
rank = stats.rank published = self._bio.published,
) summary = self._bio.summary,
self._similar = similar content = self._bio.content
self._top_tags = top_tags ) or None
self._bio = bio and Wiki(
subject = self,
published = bio.published,
summary = bio.summary,
content = bio.content
)
self._subject = subject self._subject = subject
@property
def name(self):
"""
name of the artist
@rtype: L{str}
"""
return self._name
@property
def mbid(self):
"""
MBID of the artist
@rtype: L{str}
"""
if self._mbid is None:
self._fill_info()
return self._mbid
@property
def url(self):
"""
url of the artist's page
@rtype: L{str}
"""
if self._url is None:
self._fill_info()
return self._url
@property
def image(self):
"""
images of the artist
@rtype: L{dict}
"""
if self._image is None:
self._fill_info()
return self._image
@property
def streamable(self):
"""
is the artist streamable on last.fm
@rtype: L{bool}
"""
if self._streamable is None:
self._fill_info()
return self._streamable
@property
def stats(self):
"""
stats for the artist
@rtype: L{Stats}
"""
if self._stats is None:
self._fill_info()
return self._stats
def get_similar(self, limit = None): def get_similar(self, limit = None):
""" """
Get the artists similar to this artist. Get the artists similar to this artist.
@ -181,7 +107,7 @@ class Artist(LastfmBase):
artists similar to this artist artists similar to this artist
@rtype: L{list} of L{Artist} @rtype: L{list} of L{Artist}
""" """
if self._similar is None or len(self._similar) < 6: if not hasattr(self, "_similar") or self._similar is None or len(self._similar) < 6:
return self.get_similar() return self.get_similar()
return self._similar[:] return self._similar[:]
@ -199,7 +125,7 @@ class Artist(LastfmBase):
top tags for the artist top tags for the artist
@rtype: L{list} of L{Tag} @rtype: L{list} of L{Tag}
""" """
if self._top_tags is None or len(self._top_tags) < 6: if not hasattr(self, "_top_tags") or self._top_tags is None or len(self._top_tags) < 6:
params = self._default_params({'method': 'artist.getTopTags'}) params = self._default_params({'method': 'artist.getTopTags'})
data = self._api._fetch_data(params).find('toptags') data = self._api._fetch_data(params).find('toptags')
self._top_tags = [ self._top_tags = [
@ -221,16 +147,6 @@ class Artist(LastfmBase):
""" """
pass pass
@property
def bio(self):
"""
biography of the artist
@rtype: L{Wiki}
"""
if self._bio is None:
self._fill_info()
return self._bio
@cached_property @cached_property
def events(self): def events(self):
""" """

View File

@ -9,6 +9,13 @@ __package__ = "lastfm"
class LastfmBase(object): class LastfmBase(object):
"""Base class for all the classes in this package""" """Base class for all the classes in this package"""
def init(self, **kwargs):
for k in kwargs:
if (k in self.Meta.properties or
(hasattr(self.Meta, 'fillable_properties') and
k in self.Meta.fillable_properties)):
setattr(self, "_{0}".format(k), kwargs[k])
def __eq__(self, other): def __eq__(self, other):
raise NotImplementedError("The subclass must override this method") raise NotImplementedError("The subclass must override this method")

View File

@ -7,13 +7,15 @@ __package__ = "lastfm"
from functools import reduce from functools import reduce
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixin import cacheable from lastfm.mixin import mixin
from lastfm.util import logging from lastfm.util import logging
from operator import xor from operator import xor
@cacheable @mixin("cacheable", "property_adder")
class Chart(LastfmBase): class Chart(LastfmBase):
"""The base class for all the chart classes""" """The base class for all the chart classes"""
class Meta(object):
properties = ["subject", "start", "end", "stats"]
def init(self, subject, start, end, stats = None): def init(self, subject, start, end, stats = None):
self._subject = subject self._subject = subject
@ -21,22 +23,6 @@ class Chart(LastfmBase):
self._end = end self._end = end
self._stats = stats self._stats = stats
@property
def subject(self):
return self._subject
@property
def start(self):
return self._start
@property
def end(self):
return self._end
@property
def stats(self):
return self._stats
@staticmethod @staticmethod
def _check_chart_params(params, subject, start = None, end = None): def _check_chart_params(params, subject, start = None, end = None):
if xor(start is None, end is None): if xor(start is None, end is None):
@ -93,42 +79,42 @@ class Chart(LastfmBase):
self.end.strftime("%x"), self.end.strftime("%x"),
) )
@mixin("property_adder")
class AlbumChart(Chart): class AlbumChart(Chart):
class Meta(object):
properties = ["albums"]
def init(self, subject, start, end, stats, albums): def init(self, subject, start, end, stats, albums):
super(AlbumChart, self).init(subject, start, end, stats) super(AlbumChart, self).init(subject, start, end, stats)
self._albums = albums self._albums = albums
@property @mixin("property_adder")
def albums(self):
return self._albums
class ArtistChart(Chart): class ArtistChart(Chart):
class Meta(object):
properties = ["artists"]
def init(self, subject, start, end, stats, artists): def init(self, subject, start, end, stats, artists):
super(ArtistChart, self).init(subject, start, end, stats) super(ArtistChart, self).init(subject, start, end, stats)
self._artists = artists self._artists = artists
@property @mixin("property_adder")
def artists(self):
return self._artists
class TrackChart(Chart): class TrackChart(Chart):
class Meta(object):
properties = ["tracks"]
def init(self, subject, start, end, tracks, stats): def init(self, subject, start, end, tracks, stats):
super(TrackChart, self).init(subject, start, end, stats) super(TrackChart, self).init(subject, start, end, stats)
self._tracks = tracks self._tracks = tracks
@property @mixin("property_adder")
def tracks(self):
return self._tracks
class TagChart(Chart): class TagChart(Chart):
class Meta(object):
properties = ["tags"]
def init(self, subject, start, end, tags, stats): def init(self, subject, start, end, tags, stats):
super(TagChart, self).init(subject, start, end, stats) super(TagChart, self).init(subject, start, end, stats)
self._tags = tags self._tags = tags
@property
def tags(self):
return self._tags
class WeeklyChart(Chart): class WeeklyChart(Chart):
"""A class for representing the weekly charts""" """A class for representing the weekly charts"""
@staticmethod @staticmethod

View File

@ -7,32 +7,23 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixin import cacheable, sharable, shoutable, crawlable from lastfm.mixin import mixin
@crawlable @mixin("crawlable", "shoutable", "sharable",
@shoutable "cacheable", "property_adder")
@sharable
@cacheable
class Event(LastfmBase): class Event(LastfmBase):
"""A class representing an event.""" """A class representing an event."""
STATUS_ATTENDING = 0 STATUS_ATTENDING = 0
STATUS_MAYBE = 1 STATUS_MAYBE = 1
STATUS_NOT = 2 STATUS_NOT = 2
def init(self, class Meta(object):
api, properties = ["id", "title", "artists",
id = None, "headliner", "venue", "start_date",
title = None, "description", "image", "url",
artists = None, "stats", "tag"]
headliner = None,
venue = None, def init(self, api, **kwargs):
start_date = None,
description = None,
image = None,
url = None,
stats = None,
tag = None,
**kwargs):
""" """
Create an Event object by providing all the data related to it. Create an Event object by providing all the data related to it.
@ -65,109 +56,12 @@ class Event(LastfmBase):
raise InvalidParametersError("api reference must be supplied as an argument") raise InvalidParametersError("api reference must be supplied as an argument")
self._api = api self._api = api
self._id = id super(Event, self).init(**kwargs)
self._title = title self._stats = hasattr(self, "_stats") and Stats(
self._artists = artists subject = self,
self._headliner = headliner attendance = self._stats.attendance,
self._venue = venue reviews = self._stats.reviews
self._start_date = start_date ) or None
self._description = description
self._image = image
self._url = url
self._stats = stats and Stats(
subject = self,
attendance = stats.attendance,
reviews = stats.reviews
)
self._tag = tag
@property
def id(self):
"""
id of the event
@rtype: L{int}
"""
return self._id
@property
def title(self):
"""
title of the event
@rtype: L{str}
"""
return self._title
@property
def artists(self):
"""
artists performing in the event
@rtype: L{list} of L{Artist}
"""
return self._artists
@property
def headliner(self):
"""
headliner artist of the event
@rtype: L{Artist}
"""
return self._headliner
@property
def venue(self):
"""
venue of the event
@rtype: L{Venue}
"""
return self._venue
@property
def start_date(self):
"""
start date of the event
@rtype: C{datetime.datetime}
"""
return self._start_date
@property
def description(self):
"""
description of the event
@rtype: L{str}
"""
return self._description
@property
def image(self):
"""
poster of the event
@rtype: L{dict}
"""
return self._image
@property
def url(self):
"""
url of the event's page
@rtype: L{str}
"""
return self._url
@property
def stats(self):
"""
statistics for the event
@rtype: L{Stats}
"""
return self._stats
@property
def tag(self):
"""
tag for the event
@rtype: L{str}
"""
return self._tag
def attend(self, status = STATUS_ATTENDING): def attend(self, status = STATUS_ATTENDING):
""" """

View File

@ -8,7 +8,7 @@ __package__ = "lastfm"
from functools import reduce from functools import reduce
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixin import cacheable, crawlable from lastfm.mixin import mixin
from lastfm.decorators import cached_property, top_property, depaginate from lastfm.decorators import cached_property, top_property, depaginate
class Geo(object): class Geo(object):
@ -146,22 +146,17 @@ class Geo(object):
for t in data.findall('track') for t in data.findall('track')
] ]
@crawlable @mixin("crawlable", "cacheable", "property_adder")
@cacheable
class Location(LastfmBase): class Location(LastfmBase):
"""A class representing a location of an event""" """A class representing a location of an event"""
XMLNS = "http://www.w3.org/2003/01/geo/wgs84_pos#" XMLNS = "http://www.w3.org/2003/01/geo/wgs84_pos#"
def init(self, class Meta(object):
api, properties = ["city", "country", "street",
city = None, "postal_code", "latitude", "longitude",
country = None, "timezone"]
street = None,
postal_code = None, def init(self, api, **kwargs):
latitude = None,
longitude = None,
timezone = None,
**kwargs):
""" """
Create a Location object by providing all the data related to it. Create a Location object by providing all the data related to it.
@ -188,69 +183,7 @@ class Location(LastfmBase):
if not isinstance(api, Api): if not isinstance(api, Api):
raise InvalidParametersError("api reference must be supplied as an argument") raise InvalidParametersError("api reference must be supplied as an argument")
self._api = api self._api = api
self._city = city super(Location, self).init(**kwargs)
self._country = country
self._street = street
self._postal_code = postal_code
self._latitude = latitude
self._longitude = longitude
self._timezone = timezone
@property
def city(self):
"""
city in which the location is situated
@rtype: L{str}
"""
return self._city
@property
def country(self):
"""
country in which the location is situated
@rtype: L{Country}
"""
return self._country
@property
def street(self):
"""
street in which the location is situated
@rtype: L{str}
"""
return self._street
@property
def postal_code(self):
"""
postal code of the location
@rtype: L{str}
"""
return self._postal_code
@property
def latitude(self):
"""
latitude of the location
@rtype: L{float}
"""
return self._latitude
@property
def longitude(self):
"""
longitude of the location
@rtype: L{float}
"""
return self._longitude
@property
def timezone(self):
"""
timezone in which the location is situated
@rtype: L{str}
"""
return self._timezone
@cached_property @cached_property
def top_tracks(self): def top_tracks(self):
@ -280,11 +213,8 @@ class Location(LastfmBase):
@return: events taking place at the location @return: events taking place at the location
@rtype: L{lazylist} of L{Event} @rtype: L{lazylist} of L{Event}
""" """
return Geo.get_events(self._api, return Geo.get_events(self._api, self.city,
self.city, self.latitude, self.longitude, distance)
self.latitude,
self.longitude,
distance)
@cached_property @cached_property
def events(self): def events(self):
@ -335,8 +265,7 @@ class Location(LastfmBase):
else: else:
return "<lastfm.geo.Location: %s>" % self.city return "<lastfm.geo.Location: %s>" % self.city
@crawlable @mixin("crawlable", "cacheable", "property_adder")
@cacheable
class Country(LastfmBase): class Country(LastfmBase):
"""A class representing a country.""" """A class representing a country."""
ISO_CODES = { ISO_CODES = {
@ -587,7 +516,11 @@ class Country(LastfmBase):
'ZM': 'Zambia', 'ZM': 'Zambia',
'ZW': 'Zimbabwe'} 'ZW': 'Zimbabwe'}
"""ISO Codes of the countries""" """ISO Codes of the countries"""
def init(self, api, name = None, **kwargs):
class Meta(object):
properties = ["name"]
def init(self, api, **kwargs):
""" """
Create a Country object by providing all the data related to it. Create a Country object by providing all the data related to it.
@param api: an instance of L{Api} @param api: an instance of L{Api}
@ -601,15 +534,7 @@ class Country(LastfmBase):
if not isinstance(api, Api): if not isinstance(api, Api):
raise InvalidParametersError("api reference must be supplied as an argument") raise InvalidParametersError("api reference must be supplied as an argument")
self._api = api self._api = api
self._name = name super(Country, self).init(**kwargs)
@property
def name(self):
"""
name of the country
@rtype: L{str}
"""
return self._name
@cached_property @cached_property
def top_artists(self): def top_artists(self):

View File

@ -7,14 +7,18 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixin import cacheable, chartable from lastfm.mixin import mixin, chartable
from lastfm.decorators import cached_property, depaginate from lastfm.decorators import cached_property, depaginate
@chartable(['album', 'artist', 'track', 'tag']) @chartable('album', 'artist', 'track', 'tag')
@cacheable @mixin("cacheable", "property_adder")
class Group(LastfmBase): class Group(LastfmBase):
"""A class representing a group on last.fm.""" """A class representing a group on last.fm."""
def init(self, api, name = None, **kwargs):
class Meta(object):
properties = ["name"]
def init(self, api, **kwargs):
""" """
Create a Group object by providing all the data related to it. Create a Group object by providing all the data related to it.
@ -30,15 +34,7 @@ class Group(LastfmBase):
raise InvalidParametersError("api reference must be supplied as an argument") raise InvalidParametersError("api reference must be supplied as an argument")
self._api = api self._api = api
self._name = name super(Group, self).init(**kwargs)
@property
def name(self):
"""
name of the group
@rtype: L{str}
"""
return self._name
@cached_property @cached_property
@depaginate @depaginate

View File

@ -12,6 +12,15 @@ from lastfm.mixin._shoutable import shoutable
from lastfm.mixin._taggable import taggable from lastfm.mixin._taggable import taggable
from lastfm.mixin._chartable import chartable from lastfm.mixin._chartable import chartable
from lastfm.mixin._crawlable import crawlable from lastfm.mixin._crawlable import crawlable
from lastfm.mixin._propertyadder import property_adder
def mixin(*mixins):
def wrapper(cls):
for m in reversed(mixins):
if m in __all__:
cls = eval(m)(cls)
return cls
return wrapper
__all__ = ['cacheable', 'searchable', 'sharable', 'shoutable', 'taggable' __all__ = ['cacheable', 'searchable', 'sharable', 'shoutable', 'taggable'
'chartable','crawlable'] 'chartable','crawlable', 'property_adder']

View File

@ -8,7 +8,7 @@ __package__ = "lastfm.mixin"
from lastfm.util import lazylist, logging from lastfm.util import lazylist, logging
from lastfm.decorators import cached_property from lastfm.decorators import cached_property
def chartable(chart_types): def chartable(*chart_types):
def wrapper(cls): def wrapper(cls):
@cached_property @cached_property
def weekly_chart_list(self): def weekly_chart_list(self):

View File

@ -0,0 +1,43 @@
#!/usr/bin/env python
__author__ = "Abhinav Sarkar <abhinav@abhinavsarkar.net>"
__version__ = "0.2"
__license__ = "GNU Lesser General Public License"
__package__ = "lastfm.mixin"
def property_adder(cls):
for p in cls.Meta.properties:
if not hasattr(cls, p):
def wrapper():
q = p
@property
def get(self):
try:
return getattr(self, "_{0}".format(q))
except AttributeError:
return None
return get
setattr(cls, p, wrapper())
if hasattr(cls.Meta, 'fillable_properties'):
for p in cls.Meta.fillable_properties:
if not hasattr(cls, p):
def wrapper():
q = p
@property
def get(self):
fill = False
try:
attrval = getattr(self, "_{0}".format(q))
if attrval is None:
fill = True
else:
return attrval
except AttributeError:
fill = True
if fill:
self._fill_info()
return getattr(self, "_{0}".format(q))
return get
setattr(cls, p, wrapper())
return cls

View File

@ -6,12 +6,16 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixin import cacheable from lastfm.mixin import mixin
from lastfm.decorators import cached_property from lastfm.decorators import cached_property
@cacheable @mixin("cacheable", "property_adder")
class Playlist(LastfmBase): class Playlist(LastfmBase):
"""A class representing an XPSF playlist.""" """A class representing an XPSF playlist."""
class Meta(object):
properties = ["url"]
def init(self, api, url, **kwargs): def init(self, api, url, **kwargs):
self._api = api self._api = api
self._data = None self._data = None
@ -25,11 +29,6 @@ class Playlist(LastfmBase):
ElementTree.ElementTree(self._api._fetch_data(params)[0]).write(tmp) ElementTree.ElementTree(self._api._fetch_data(params)[0]).write(tmp)
return tmp.getvalue() return tmp.getvalue()
@property
def url(self):
"""url of the playlist"""
return self._url
@staticmethod @staticmethod
def fetch(api, url): def fetch(api, url):
return Playlist(api, url = url) return Playlist(api, url = url)

View File

@ -6,33 +6,18 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixin import cacheable from lastfm.mixin import mixin
from lastfm.decorators import cached_property from lastfm.decorators import cached_property
@cacheable @mixin("cacheable", "property_adder")
class Shout(LastfmBase): class Shout(LastfmBase):
"""A class representing a shout.""" """A class representing a shout."""
def init(self, class Meta(object):
body = None, properties = ["body", "author", "date"]
author = None,
date = None,
**kwargs):
self._body = body
self._author = author
self._date = date
@cached_property def init(self, **kwargs):
def body(self): super(Shout, self).init(**kwargs)
return self._body
@cached_property
def author(self):
return self._author
@cached_property
def date(self):
return self._date
@staticmethod @staticmethod
def _hash_func(*args, **kwds): def _hash_func(*args, **kwds):

View File

@ -5,80 +5,27 @@ __version__ = "0.2"
__license__ = "GNU Lesser General Public License" __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
class Stats(object): from lastfm.base import LastfmBase
from lastfm.mixin import mixin
@mixin("property_adder")
class Stats(LastfmBase):
"""A class representing the stats of an artist.""" """A class representing the stats of an artist."""
def __init__(self,
subject, class Meta(object):
listeners = None, properties = ["listeners", "playcount",
playcount = None, "tagcount", "count", "match", "rank",
tagcount = None, "weight", "attendance", "reviews"]
count = None,
match = None, def __init__(self, subject, **kwargs):
rank = None,
weight = None,
attendance = None,
reviews = None,):
self._subject = subject self._subject = subject
self._listeners = listeners super(Stats, self).init(**kwargs)
self._playcount = playcount
self._tagcount = tagcount
self._count = count
self._match = match
self._rank = rank
self._weight = weight
self._attendance = attendance
self._reviews = reviews
@property @property
def subject(self): def subject(self):
"""subject of the stats""" """subject of the stats"""
return self._subject return self._subject
@property
def rank(self):
"""rank of the subject"""
return self._rank
@property
def listeners(self):
"""number of listeners of the subject"""
return self._listeners
@property
def playcount(self):
"""playcount of the subject"""
return self._playcount
@property
def tagcount(self):
"""tagcount of the subject"""
return self._tagcount
@property
def count(self):
"""count of the subject"""
return self._count
@property
def match(self):
"""match of the subject"""
return self._match
@property
def weight(self):
"""weight of the subject"""
return self._weight
@property
def attendance(self):
"""attendance of the subject"""
return self._attendance
@property
def reviews(self):
"""reviews of the subject"""
return self._reviews
def __repr__(self): def __repr__(self):
if hasattr(self._subject, 'name'): if hasattr(self._subject, 'name'):
return "<lastfm.Stats: for '%s'>" % self._subject.name return "<lastfm.Stats: for '%s'>" % self._subject.name

View File

@ -6,53 +6,27 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixin import cacheable, searchable, chartable, crawlable from lastfm.mixin import mixin, chartable
from lastfm.decorators import cached_property, top_property from lastfm.decorators import cached_property, top_property
@crawlable @chartable("artist")
@chartable(['artist']) @mixin("crawlable", "searchable", "cacheable", "property_adder")
@searchable
@cacheable
class Tag(LastfmBase): class Tag(LastfmBase):
"""A class representing a tag.""" """A class representing a tag."""
def init(self, class Meta(object):
api, properties = ["name", "url", "streamable", "stats"]
name = None,
url = None, def init(self, api, **kwargs):
streamable = None,
stats = None,
**kwargs):
if not isinstance(api, Api): if not isinstance(api, Api):
raise InvalidParametersError("api reference must be supplied as an argument") raise InvalidParametersError("api reference must be supplied as an argument")
self._api = api self._api = api
self._name = name super(Tag, self).init(**kwargs)
self._url = url self._stats = hasattr(self, '_stats') and Stats(
self._streamable = streamable
self._stats = stats and Stats(
subject = self, subject = self,
count = stats.count, count = self._stats.count,
rank = stats.rank rank = self._stats.rank
) ) or None
@property
def name(self):
"""name of the tag"""
return self._name
@property
def url(self):
"""url of the tag's page"""
return self._url
@property
def streamable(self):
"""is the tag streamable"""
return self._streamable
@property
def stats(self):
return self._stats
@cached_property @cached_property
def similar(self): def similar(self):

View File

@ -6,141 +6,38 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixin import cacheable, searchable, sharable, taggable, crawlable from lastfm.mixin import mixin
from lastfm.decorators import cached_property, top_property from lastfm.decorators import cached_property, top_property
@crawlable @mixin("crawlable", "sharable", "taggable",
@sharable "searchable", "cacheable", "property_adder")
@taggable
@searchable
@cacheable
class Track(LastfmBase): class Track(LastfmBase):
"""A class representing a track.""" """A class representing a track."""
def init(self, class Meta(object):
api, properties = ["id", "name", "mbid", "url", "duration",
name = None, "artist", "image", "stats", "played_on", "loved_on",
mbid = None, "subject"]
url = None, fillable_properties = ["streamable", "full_track",
duration = None, "album", "position", "wiki"]
streamable = None,
full_track = None, def init(self, api, **kwargs):
artist = None,
album = None,
position = None,
image = None,
stats = None,
played_on = None,
loved_on = None,
wiki = None,
subject = None):
if not isinstance(api, Api): if not isinstance(api, Api):
raise InvalidParametersError("api reference must be supplied as an argument") raise InvalidParametersError("api reference must be supplied as an argument")
self._api = api self._api = api
self._id = id super(Track, self).init(**kwargs)
self._name = name self._stats = hasattr(self, "_stats") and Stats(
self._mbid = mbid
self._url = url
self._duration = duration
self._streamable = streamable
self._full_track = full_track
self._artist = artist
self._album = album
self._position = position
self._image = image
self._stats = stats and Stats(
subject = self, subject = self,
match = stats.match, match = self._stats.match,
playcount = stats.playcount, playcount = self._stats.playcount,
rank = stats.rank, rank = self._stats.rank,
listeners = stats.listeners, listeners = self._stats.listeners,
) ) or None
self._played_on = played_on self._wiki = hasattr(self, "_wiki") and Wiki(
self._loved_on = loved_on
self._wiki = wiki and Wiki(
subject = self, subject = self,
published = wiki.published, published = self._wiki.published,
summary = wiki.summary, summary = self._wiki.summary,
content = wiki.content content = self._wiki.content
) ) or None
self._subject = subject
@property
def id(self):
"""id of the track"""
return self._id
@property
def name(self):
"""name of the track"""
return self._name
@property
def mbid(self):
"""mbid of the track"""
return self._mbid
@property
def url(self):
"""url of the tracks's page"""
return self._url
@property
def duration(self):
"""duration of the tracks's page"""
return self._duration
@property
def streamable(self):
"""is the track streamable"""
if self._streamable is None:
self._fill_info()
return self._streamable
@property
def full_track(self):
"""is the full track streamable"""
if self._full_track is None:
self._fill_info()
return self._full_track
@property
def artist(self):
"""artist of the track"""
return self._artist
@property
def album(self):
"""artist of the track"""
if self._album is None:
self._fill_info()
return self._album
@property
def position(self):
"""position of the track"""
if self._position is None:
self._fill_info()
return self._position
@property
def image(self):
"""image of the track's album cover"""
return self._image
@property
def stats(self):
"""stats of the track"""
return self._stats
@property
def played_on(self):
"""datetime the track was last played"""
return self._played_on
@property
def loved_on(self):
"""datetime the track was marked 'loved'"""
return self._loved_on
@property @property
def wiki(self): def wiki(self):
@ -155,11 +52,11 @@ class Track(LastfmBase):
def similar(self): def similar(self):
"""tracks similar to this track""" """tracks similar to this track"""
params = Track._check_params( params = Track._check_params(
{'method': 'track.getSimilar'}, {'method': 'track.getSimilar'},
self.artist.name, self.artist.name,
self.name, self.name,
self.mbid self.mbid
) )
data = self._api._fetch_data(params).find('similartracks') data = self._api._fetch_data(params).find('similartracks')
return [ return [
Track( Track(

View File

@ -6,66 +6,34 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixin import cacheable, shoutable, chartable, crawlable from lastfm.mixin import chartable, mixin
import lastfm.playlist import lastfm.playlist
from lastfm.decorators import ( from lastfm.decorators import (
cached_property, top_property, authentication_required, depaginate) cached_property, top_property, authentication_required, depaginate)
@crawlable @chartable('album', 'artist', 'track', 'tag')
@chartable(['album', 'artist', 'track', 'tag']) @mixin("crawlable", "shoutable", "cacheable", "property_adder")
@shoutable
@cacheable
class User(LastfmBase): class User(LastfmBase):
"""A class representing an user.""" """A class representing an user."""
def init(self,
api, class Meta(object):
name = None, properties = ["name", "real_name",
real_name = None, "url", "image", "stats"]
url = None,
image = None, def init(self, api, **kwargs):
stats = None,
**kwargs):
if not isinstance(api, Api): if not isinstance(api, Api):
raise InvalidParametersError("api reference must be supplied as an argument") raise InvalidParametersError("api reference must be supplied as an argument")
self._api = api self._api = api
self._name = name super(User, self).init(**kwargs)
self._real_name = real_name self._stats = hasattr(self, "_stats") and Stats(
self._url = url subject = self,
self._image = image match = self._stats.match,
self._stats = stats and Stats( weight = self._stats.weight,
subject = self, playcount = self._stats.playcount
match = stats.match, ) or None
weight = stats.weight,
playcount = stats.playcount
)
self._library = User.Library(api, self) self._library = User.Library(api, self)
@property
def name(self):
"""name of the user"""
return self._name
@property
def real_name(self):
"""real name of the user"""
return self._real_name
@property
def url(self):
"""url of the user's page"""
return self._url
@property
def image(self):
"""image of the user"""
return self._image
@property
def stats(self):
"""stats for the user"""
return self._stats
@property @property
@authentication_required @authentication_required
def language(self): def language(self):
@ -340,8 +308,8 @@ class User(LastfmBase):
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 = a.findtext('playcount').strip() and int(a.findtext('playcount')),
rank = int(a.attrib['rank']) rank = a.attrib['rank'].strip() and int(a.attrib['rank'])
) )
) )
for a in data.findall('album') for a in data.findall('album')
@ -372,7 +340,7 @@ class User(LastfmBase):
stats = Stats( stats = Stats(
subject = a.findtext('name'), subject = a.findtext('name'),
rank = a.attrib['rank'].strip() and int(a.attrib['rank']) or None, rank = a.attrib['rank'].strip() and int(a.attrib['rank']) or None,
playcount = a.findtext('playcount') and int(a.findtext('playcount')) or None playcount = a.findtext('playcount').strip() and int(a.findtext('playcount')) or None
), ),
url = a.findtext('url'), url = a.findtext('url'),
streamable = (a.findtext('streamable') == "1"), streamable = (a.findtext('streamable') == "1"),
@ -555,8 +523,13 @@ class User(LastfmBase):
def __repr__(self): def __repr__(self):
return "<lastfm.User: %s>" % self.name return "<lastfm.User: %s>" % self.name
@mixin("property_adder")
class Playlist(lastfm.playlist.Playlist): class Playlist(lastfm.playlist.Playlist):
"""A class representing a playlist belonging to the user.""" """A class representing a playlist belonging to the user."""
class Meta(object):
properties = ["id", "title", "date", "size", "creator"]
def init(self, api, id, title, date, size, creator): def init(self, api, id, title, date, size, creator):
super(User.Playlist, self).init(api, "lastfm://playlist/%s" % id) super(User.Playlist, self).init(api, "lastfm://playlist/%s" % id)
self._id = id self._id = id
@ -565,26 +538,6 @@ class User(LastfmBase):
self._size = size self._size = size
self._creator = creator self._creator = creator
@property
def id(self):
return self._id
@property
def title(self):
return self._title
@property
def date(self):
return self._date
@property
def size(self):
return self._size
@property
def creator(self):
return self._creator
@property @property
def user(self): def user(self):
return self._creator return self._creator

View File

@ -4,7 +4,7 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm.util" __package__ = "lastfm.util"
from lastfm.util.wormhole import Wormhole from lastfm.util.wormhole import Wormhole
from lastfm.util.lazylist import lazylist from lastfm.util._lazylist import lazylist
from lastfm.util.safelist import SafeList from lastfm.util.safelist import SafeList
from lastfm.util.filecache import FileCache from lastfm.util.filecache import FileCache
from lastfm.util.objectcache import ObjectCache from lastfm.util.objectcache import ObjectCache

View File

@ -6,48 +6,21 @@ __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.base import LastfmBase from lastfm.base import LastfmBase
from lastfm.mixin import cacheable, searchable, crawlable from lastfm.mixin import mixin
from lastfm.decorators import cached_property, depaginate from lastfm.decorators import cached_property, depaginate
@crawlable @mixin("crawlable", "searchable", "cacheable", "property_adder")
@searchable
@cacheable
class Venue(LastfmBase): class Venue(LastfmBase):
"""A class representing a venue of an event""" """A class representing a venue of an event"""
def init(self,
api, class Meta(object):
id = None, properties = ["id", "name", "location", "url"]
name = None,
location = None, def init(self, api, **kwargs):
url = None,
**kwargs):
if not isinstance(api, Api): if not isinstance(api, Api):
raise InvalidParametersError("api reference must be supplied as an argument") raise InvalidParametersError("api reference must be supplied as an argument")
self._api = api self._api = api
self._id = id super(Venue, self).init(**kwargs)
self._name = name
self._location = location
self._url = url
@property
def id(self):
"""id of the venue"""
return self._id
@property
def name(self):
"""name of the venue"""
return self._name
@property
def location(self):
"""location of the event"""
return self._location
@property
def url(self):
"""url of the event's page"""
return self._url
@cached_property @cached_property
def events(self): def events(self):

View File

@ -5,8 +5,15 @@ __version__ = "0.2"
__license__ = "GNU Lesser General Public License" __license__ = "GNU Lesser General Public License"
__package__ = "lastfm" __package__ = "lastfm"
from lastfm.mixin import mixin
@mixin("property_adder")
class Wiki(object): class Wiki(object):
"""A class representing the information from the wiki of the subject.""" """A class representing the information from the wiki of the subject."""
class Meta(object):
properties = ["subject", "published", "summary", "content"]
def __init__(self, def __init__(self,
subject, subject,
published = None, published = None,
@ -17,25 +24,5 @@ class Wiki(object):
self._summary = summary self._summary = summary
self._content = content self._content = content
@property
def subject(self):
"""artist for which the biography is"""
return self._subject
@property
def published(self):
"""publication time of the biography"""
return self._published
@property
def summary(self):
"""summary of the biography"""
return self._summary
@property
def content(self):
"""content of the biography"""
return self._content
def __repr__(self): def __repr__(self):
return "<lastfm.Wiki: for %s '%s'>" % (self.subject.__class__.__name__, self.subject.name) return "<lastfm.Wiki: for %s '%s'>" % (self.subject.__class__.__name__, self.subject.name)