From b30839a26ff671d3fb379df530277b7ad2111260 Mon Sep 17 00:00:00 2001 From: Abhinav Sarkar Date: Sat, 18 Apr 2009 05:11:22 +0000 Subject: [PATCH] added property_adder mixin. moved all "property" attributes of all classes to a Meta class. moved attribute setting code to LastfmBase. --- lastfm/album.py | 138 +++---------------- lastfm/api.py | 2 +- lastfm/artist.py | 136 ++++-------------- lastfm/base.py | 7 + lastfm/chart.py | 56 +++----- lastfm/event.py | 138 +++---------------- lastfm/geo.py | 113 +++------------ lastfm/group.py | 22 ++- lastfm/mixin/__init__.py | 11 +- lastfm/mixin/_chartable.py | 2 +- lastfm/mixin/_propertyadder.py | 43 ++++++ lastfm/playlist.py | 13 +- lastfm/shout.py | 31 ++--- lastfm/stats.py | 79 ++--------- lastfm/tag.py | 50 ++----- lastfm/track.py | 161 ++++------------------ lastfm/user.py | 95 ++++--------- lastfm/util/__init__.py | 2 +- lastfm/util/{lazylist.py => _lazylist.py} | 0 lastfm/venue.py | 43 ++---- lastfm/wiki.py | 27 +--- 21 files changed, 280 insertions(+), 889 deletions(-) create mode 100644 lastfm/mixin/_propertyadder.py rename lastfm/util/{lazylist.py => _lazylist.py} (100%) diff --git a/lastfm/album.py b/lastfm/album.py index a1f0a54..aba80e5 100644 --- a/lastfm/album.py +++ b/lastfm/album.py @@ -7,28 +7,20 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" 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 -@crawlable -@taggable -@searchable -@cacheable +@mixin("crawlable", "taggable", "searchable", + "cacheable", "property_adder") class Album(LastfmBase): """A class representing an album.""" - def init(self, - api, - name = None, - artist = None, - id = None, - mbid = None, - url = None, - release_date = None, - image = None, - stats = None, - top_tags = None, - streamable = None, - subject = None): + class Meta(object): + properties = ["name", "artist", "top_tags", + "streamable"] + fillable_properties = ["id", "mbid", "url", + "release_date", "image", "stats", ] + + def init(self, api, subject = None, **kwargs): """ Create an Album object by providing all the data related to it. @@ -63,108 +55,16 @@ class Album(LastfmBase): if not isinstance(api, Api): raise InvalidParametersError("api reference must be supplied as an argument") self._api = api - self._name = name - self._artist = artist - self._id = id - self._mbid = mbid - self._url = url - self._release_date = release_date - self._image = image - self._stats = stats and Stats( - subject = self, - listeners = stats.listeners, - playcount = stats.playcount, - match = stats.match, - rank = stats.rank - ) - self._top_tags = top_tags - self._streamable = streamable + super(Album, self).init(**kwargs) + self._stats = hasattr(self, "_stats") and Stats( + subject = self, + listeners = self._stats.listeners, + playcount = self._stats.playcount, + match = self._stats.match, + rank = self._stats.rank + ) or None 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 def top_tags(self): """ diff --git a/lastfm/api.py b/lastfm/api.py index ddff48d..1e8158e 100644 --- a/lastfm/api.py +++ b/lastfm/api.py @@ -84,7 +84,7 @@ class Api(object): self._debug = None if self._debug is not None: Wormhole.enable() - logging.set_api(self) + logging.set_api(self) @property def api_key(self): diff --git a/lastfm/artist.py b/lastfm/artist.py index a2ec4fd..de65329 100644 --- a/lastfm/artist.py +++ b/lastfm/artist.py @@ -7,29 +7,19 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" 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 -@crawlable -@shoutable -@sharable -@taggable -@searchable -@cacheable +@mixin("crawlable", "shoutable", "sharable", + "taggable", "searchable", "cacheable", "property_adder") class Artist(LastfmBase): """A class representing an artist.""" - def init(self, - api, - name = None, - mbid = None, - url = None, - image = None, - streamable = None, - stats = None, - similar = None, - top_tags = None, - bio = None, - subject = None): + class Meta(object): + properties = ["name", "similar", "top_tags"] + fillable_properties = ["mbid", "url", "image", + "streamable", "stats", "bio"] + + def init(self, api, subject = None, **kwargs): """ 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") self._api = api - self._name = name - self._mbid = mbid - self._url = url - self._image = image - self._streamable = streamable - self._stats = stats and Stats( - subject = self, - listeners = stats.listeners, - playcount = stats.playcount, - weight = stats.weight, - match = stats.match, - rank = stats.rank - ) - self._similar = similar - self._top_tags = top_tags - self._bio = bio and Wiki( - subject = self, - published = bio.published, - summary = bio.summary, - content = bio.content - ) + super(Artist, self).init(**kwargs) + self._stats = hasattr(self, "_stats") and Stats( + subject = self, + listeners = self._stats.listeners, + playcount = self._stats.playcount, + weight = self._stats.weight, + match = self._stats.match, + rank = self._stats.rank + ) or None + self._bio = hasattr(self, "_bio") and Wiki( + subject = self, + published = self._bio.published, + summary = self._bio.summary, + content = self._bio.content + ) or None 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): """ Get the artists similar to this artist. @@ -181,7 +107,7 @@ class Artist(LastfmBase): artists similar to this 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._similar[:] @@ -199,7 +125,7 @@ class Artist(LastfmBase): top tags for the artist @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'}) data = self._api._fetch_data(params).find('toptags') self._top_tags = [ @@ -221,16 +147,6 @@ class Artist(LastfmBase): """ 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 def events(self): """ diff --git a/lastfm/base.py b/lastfm/base.py index 3bbe7b9..a09b3a4 100644 --- a/lastfm/base.py +++ b/lastfm/base.py @@ -9,6 +9,13 @@ __package__ = "lastfm" class LastfmBase(object): """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): raise NotImplementedError("The subclass must override this method") diff --git a/lastfm/chart.py b/lastfm/chart.py index d6247d0..0fc3969 100644 --- a/lastfm/chart.py +++ b/lastfm/chart.py @@ -7,35 +7,21 @@ __package__ = "lastfm" from functools import reduce from lastfm.base import LastfmBase -from lastfm.mixin import cacheable +from lastfm.mixin import mixin from lastfm.util import logging from operator import xor -@cacheable +@mixin("cacheable", "property_adder") class Chart(LastfmBase): """The base class for all the chart classes""" + class Meta(object): + properties = ["subject", "start", "end", "stats"] def init(self, subject, start, end, stats = None): self._subject = subject self._start = start self._end = end 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 def _check_chart_params(params, subject, start = None, end = None): @@ -93,42 +79,42 @@ class Chart(LastfmBase): self.end.strftime("%x"), ) +@mixin("property_adder") class AlbumChart(Chart): + class Meta(object): + properties = ["albums"] + def init(self, subject, start, end, stats, albums): super(AlbumChart, self).init(subject, start, end, stats) self._albums = albums - - @property - def albums(self): - return self._albums +@mixin("property_adder") class ArtistChart(Chart): + class Meta(object): + properties = ["artists"] + def init(self, subject, start, end, stats, artists): super(ArtistChart, self).init(subject, start, end, stats) self._artists = artists - - @property - def artists(self): - return self._artists +@mixin("property_adder") class TrackChart(Chart): + class Meta(object): + properties = ["tracks"] + def init(self, subject, start, end, tracks, stats): super(TrackChart, self).init(subject, start, end, stats) self._tracks = tracks - - @property - def tracks(self): - return self._tracks +@mixin("property_adder") class TagChart(Chart): + class Meta(object): + properties = ["tags"] + def init(self, subject, start, end, tags, stats): super(TagChart, self).init(subject, start, end, stats) self._tags = tags - - @property - def tags(self): - return self._tags - + class WeeklyChart(Chart): """A class for representing the weekly charts""" @staticmethod diff --git a/lastfm/event.py b/lastfm/event.py index 7489162..07be508 100644 --- a/lastfm/event.py +++ b/lastfm/event.py @@ -7,32 +7,23 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" from lastfm.base import LastfmBase -from lastfm.mixin import cacheable, sharable, shoutable, crawlable +from lastfm.mixin import mixin -@crawlable -@shoutable -@sharable -@cacheable +@mixin("crawlable", "shoutable", "sharable", + "cacheable", "property_adder") class Event(LastfmBase): """A class representing an event.""" STATUS_ATTENDING = 0 STATUS_MAYBE = 1 STATUS_NOT = 2 + + class Meta(object): + properties = ["id", "title", "artists", + "headliner", "venue", "start_date", + "description", "image", "url", + "stats", "tag"] - def init(self, - api, - id = None, - title = None, - artists = None, - headliner = None, - venue = None, - start_date = None, - description = None, - image = None, - url = None, - stats = None, - tag = None, - **kwargs): + def init(self, api, **kwargs): """ 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") self._api = api - self._id = id - self._title = title - self._artists = artists - self._headliner = headliner - self._venue = venue - self._start_date = start_date - 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 + super(Event, self).init(**kwargs) + self._stats = hasattr(self, "_stats") and Stats( + subject = self, + attendance = self._stats.attendance, + reviews = self._stats.reviews + ) or None def attend(self, status = STATUS_ATTENDING): """ diff --git a/lastfm/geo.py b/lastfm/geo.py index 6fff487..a8e543a 100644 --- a/lastfm/geo.py +++ b/lastfm/geo.py @@ -8,7 +8,7 @@ __package__ = "lastfm" from functools import reduce 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 class Geo(object): @@ -146,22 +146,17 @@ class Geo(object): for t in data.findall('track') ] -@crawlable -@cacheable +@mixin("crawlable", "cacheable", "property_adder") class Location(LastfmBase): """A class representing a location of an event""" XMLNS = "http://www.w3.org/2003/01/geo/wgs84_pos#" - - def init(self, - api, - city = None, - country = None, - street = None, - postal_code = None, - latitude = None, - longitude = None, - timezone = None, - **kwargs): + + class Meta(object): + properties = ["city", "country", "street", + "postal_code", "latitude", "longitude", + "timezone"] + + def init(self, api, **kwargs): """ Create a Location object by providing all the data related to it. @@ -188,69 +183,7 @@ class Location(LastfmBase): if not isinstance(api, Api): raise InvalidParametersError("api reference must be supplied as an argument") self._api = api - self._city = city - 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 + super(Location, self).init(**kwargs) @cached_property def top_tracks(self): @@ -280,11 +213,8 @@ class Location(LastfmBase): @return: events taking place at the location @rtype: L{lazylist} of L{Event} """ - return Geo.get_events(self._api, - self.city, - self.latitude, - self.longitude, - distance) + return Geo.get_events(self._api, self.city, + self.latitude, self.longitude, distance) @cached_property def events(self): @@ -335,8 +265,7 @@ class Location(LastfmBase): else: return "" % self.city -@crawlable -@cacheable +@mixin("crawlable", "cacheable", "property_adder") class Country(LastfmBase): """A class representing a country.""" ISO_CODES = { @@ -587,7 +516,11 @@ class Country(LastfmBase): 'ZM': 'Zambia', 'ZW': 'Zimbabwe'} """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. @param api: an instance of L{Api} @@ -601,15 +534,7 @@ class Country(LastfmBase): if not isinstance(api, Api): raise InvalidParametersError("api reference must be supplied as an argument") self._api = api - self._name = name - - @property - def name(self): - """ - name of the country - @rtype: L{str} - """ - return self._name + super(Country, self).init(**kwargs) @cached_property def top_artists(self): diff --git a/lastfm/group.py b/lastfm/group.py index 876dae3..f382d35 100644 --- a/lastfm/group.py +++ b/lastfm/group.py @@ -7,14 +7,18 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" from lastfm.base import LastfmBase -from lastfm.mixin import cacheable, chartable +from lastfm.mixin import mixin, chartable from lastfm.decorators import cached_property, depaginate -@chartable(['album', 'artist', 'track', 'tag']) -@cacheable +@chartable('album', 'artist', 'track', 'tag') +@mixin("cacheable", "property_adder") class Group(LastfmBase): """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. @@ -30,16 +34,8 @@ class Group(LastfmBase): raise InvalidParametersError("api reference must be supplied as an argument") 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 @depaginate def members(self, page = None): diff --git a/lastfm/mixin/__init__.py b/lastfm/mixin/__init__.py index ff2c949..65cebf1 100644 --- a/lastfm/mixin/__init__.py +++ b/lastfm/mixin/__init__.py @@ -12,6 +12,15 @@ from lastfm.mixin._shoutable import shoutable from lastfm.mixin._taggable import taggable from lastfm.mixin._chartable import chartable 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' - 'chartable','crawlable'] \ No newline at end of file + 'chartable','crawlable', 'property_adder'] \ No newline at end of file diff --git a/lastfm/mixin/_chartable.py b/lastfm/mixin/_chartable.py index 4813682..c8b9177 100644 --- a/lastfm/mixin/_chartable.py +++ b/lastfm/mixin/_chartable.py @@ -8,7 +8,7 @@ __package__ = "lastfm.mixin" from lastfm.util import lazylist, logging from lastfm.decorators import cached_property -def chartable(chart_types): +def chartable(*chart_types): def wrapper(cls): @cached_property def weekly_chart_list(self): diff --git a/lastfm/mixin/_propertyadder.py b/lastfm/mixin/_propertyadder.py new file mode 100644 index 0000000..f28191b --- /dev/null +++ b/lastfm/mixin/_propertyadder.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python + +__author__ = "Abhinav Sarkar " +__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 \ No newline at end of file diff --git a/lastfm/playlist.py b/lastfm/playlist.py index 6b0fe37..0f598a6 100644 --- a/lastfm/playlist.py +++ b/lastfm/playlist.py @@ -6,12 +6,16 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" from lastfm.base import LastfmBase -from lastfm.mixin import cacheable +from lastfm.mixin import mixin from lastfm.decorators import cached_property -@cacheable +@mixin("cacheable", "property_adder") class Playlist(LastfmBase): """A class representing an XPSF playlist.""" + + class Meta(object): + properties = ["url"] + def init(self, api, url, **kwargs): self._api = api self._data = None @@ -25,11 +29,6 @@ class Playlist(LastfmBase): ElementTree.ElementTree(self._api._fetch_data(params)[0]).write(tmp) return tmp.getvalue() - @property - def url(self): - """url of the playlist""" - return self._url - @staticmethod def fetch(api, url): return Playlist(api, url = url) diff --git a/lastfm/shout.py b/lastfm/shout.py index 6af5717..a1a5f55 100644 --- a/lastfm/shout.py +++ b/lastfm/shout.py @@ -6,34 +6,19 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" from lastfm.base import LastfmBase -from lastfm.mixin import cacheable +from lastfm.mixin import mixin from lastfm.decorators import cached_property -@cacheable +@mixin("cacheable", "property_adder") class Shout(LastfmBase): """A class representing a shout.""" - - def init(self, - body = None, - author = None, - date = None, - **kwargs): - self._body = body - self._author = author - self._date = date - - @cached_property - def body(self): - return self._body - - @cached_property - def author(self): - return self._author - - @cached_property - def date(self): - return self._date + class Meta(object): + properties = ["body", "author", "date"] + + def init(self, **kwargs): + super(Shout, self).init(**kwargs) + @staticmethod def _hash_func(*args, **kwds): try: diff --git a/lastfm/stats.py b/lastfm/stats.py index c476801..716bfb3 100644 --- a/lastfm/stats.py +++ b/lastfm/stats.py @@ -5,80 +5,27 @@ __version__ = "0.2" __license__ = "GNU Lesser General Public License" __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.""" - def __init__(self, - subject, - listeners = None, - playcount = None, - tagcount = None, - count = None, - match = None, - rank = None, - weight = None, - attendance = None, - reviews = None,): + + class Meta(object): + properties = ["listeners", "playcount", + "tagcount", "count", "match", "rank", + "weight", "attendance", "reviews"] + + def __init__(self, subject, **kwargs): self._subject = subject - self._listeners = listeners - self._playcount = playcount - self._tagcount = tagcount - self._count = count - self._match = match - self._rank = rank - self._weight = weight - self._attendance = attendance - self._reviews = reviews + super(Stats, self).init(**kwargs) @property def subject(self): """subject of the stats""" 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): if hasattr(self._subject, 'name'): return "" % self._subject.name diff --git a/lastfm/tag.py b/lastfm/tag.py index 674b92d..ffb4360 100644 --- a/lastfm/tag.py +++ b/lastfm/tag.py @@ -6,53 +6,27 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" 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 -@crawlable -@chartable(['artist']) -@searchable -@cacheable +@chartable("artist") +@mixin("crawlable", "searchable", "cacheable", "property_adder") class Tag(LastfmBase): """A class representing a tag.""" - def init(self, - api, - name = None, - url = None, - streamable = None, - stats = None, - **kwargs): + class Meta(object): + properties = ["name", "url", "streamable", "stats"] + + def init(self, api, **kwargs): if not isinstance(api, Api): raise InvalidParametersError("api reference must be supplied as an argument") self._api = api - self._name = name - self._url = url - self._streamable = streamable - self._stats = stats and Stats( + super(Tag, self).init(**kwargs) + self._stats = hasattr(self, '_stats') and Stats( subject = self, - count = stats.count, - rank = stats.rank - ) - - @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 + count = self._stats.count, + rank = self._stats.rank + ) or None @cached_property def similar(self): diff --git a/lastfm/track.py b/lastfm/track.py index ac13a4a..9e3de2f 100644 --- a/lastfm/track.py +++ b/lastfm/track.py @@ -6,142 +6,39 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" 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 -@crawlable -@sharable -@taggable -@searchable -@cacheable +@mixin("crawlable", "sharable", "taggable", + "searchable", "cacheable", "property_adder") class Track(LastfmBase): """A class representing a track.""" - def init(self, - api, - name = None, - mbid = None, - url = None, - duration = None, - streamable = None, - full_track = None, - artist = None, - album = None, - position = None, - image = None, - stats = None, - played_on = None, - loved_on = None, - wiki = None, - subject = None): + class Meta(object): + properties = ["id", "name", "mbid", "url", "duration", + "artist", "image", "stats", "played_on", "loved_on", + "subject"] + fillable_properties = ["streamable", "full_track", + "album", "position", "wiki"] + + def init(self, api, **kwargs): if not isinstance(api, Api): raise InvalidParametersError("api reference must be supplied as an argument") self._api = api - self._id = id - self._name = name - 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( + super(Track, self).init(**kwargs) + self._stats = hasattr(self, "_stats") and Stats( subject = self, - match = stats.match, - playcount = stats.playcount, - rank = stats.rank, - listeners = stats.listeners, - ) - self._played_on = played_on - self._loved_on = loved_on - self._wiki = wiki and Wiki( + match = self._stats.match, + playcount = self._stats.playcount, + rank = self._stats.rank, + listeners = self._stats.listeners, + ) or None + self._wiki = hasattr(self, "_wiki") and Wiki( subject = self, - published = wiki.published, - summary = wiki.summary, - content = wiki.content - ) - 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 - + published = self._wiki.published, + summary = self._wiki.summary, + content = self._wiki.content + ) or None + @property def wiki(self): """wiki of the track""" @@ -155,11 +52,11 @@ class Track(LastfmBase): def similar(self): """tracks similar to this track""" params = Track._check_params( - {'method': 'track.getSimilar'}, - self.artist.name, - self.name, - self.mbid - ) + {'method': 'track.getSimilar'}, + self.artist.name, + self.name, + self.mbid + ) data = self._api._fetch_data(params).find('similartracks') return [ Track( diff --git a/lastfm/user.py b/lastfm/user.py index 4621125..02eea0b 100644 --- a/lastfm/user.py +++ b/lastfm/user.py @@ -6,66 +6,34 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" from lastfm.base import LastfmBase -from lastfm.mixin import cacheable, shoutable, chartable, crawlable +from lastfm.mixin import chartable, mixin import lastfm.playlist from lastfm.decorators import ( cached_property, top_property, authentication_required, depaginate) -@crawlable -@chartable(['album', 'artist', 'track', 'tag']) -@shoutable -@cacheable +@chartable('album', 'artist', 'track', 'tag') +@mixin("crawlable", "shoutable", "cacheable", "property_adder") class User(LastfmBase): """A class representing an user.""" - def init(self, - api, - name = None, - real_name = None, - url = None, - image = None, - stats = None, - **kwargs): + + class Meta(object): + properties = ["name", "real_name", + "url", "image", "stats"] + + def init(self, api, **kwargs): if not isinstance(api, Api): raise InvalidParametersError("api reference must be supplied as an argument") self._api = api - self._name = name - self._real_name = real_name - self._url = url - self._image = image - self._stats = stats and Stats( - subject = self, - match = stats.match, - weight = stats.weight, - playcount = stats.playcount - ) + super(User, self).init(**kwargs) + self._stats = hasattr(self, "_stats") and Stats( + subject = self, + match = self._stats.match, + weight = self._stats.weight, + playcount = self._stats.playcount + ) or None 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 @authentication_required def language(self): @@ -340,8 +308,8 @@ class User(LastfmBase): image = dict([(i.get('size'), i.text) for i in a.findall('image')]), stats = Stats( subject = a.findtext('name'), - playcount = int(a.findtext('playcount')), - rank = int(a.attrib['rank']) + playcount = a.findtext('playcount').strip() and int(a.findtext('playcount')), + rank = a.attrib['rank'].strip() and int(a.attrib['rank']) ) ) for a in data.findall('album') @@ -372,7 +340,7 @@ class User(LastfmBase): stats = Stats( subject = a.findtext('name'), 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'), streamable = (a.findtext('streamable') == "1"), @@ -555,8 +523,13 @@ class User(LastfmBase): def __repr__(self): return "" % self.name + @mixin("property_adder") class Playlist(lastfm.playlist.Playlist): """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): super(User.Playlist, self).init(api, "lastfm://playlist/%s" % id) self._id = id @@ -564,26 +537,6 @@ class User(LastfmBase): self._date = date self._size = size 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 def user(self): diff --git a/lastfm/util/__init__.py b/lastfm/util/__init__.py index 2544d71..3677dda 100644 --- a/lastfm/util/__init__.py +++ b/lastfm/util/__init__.py @@ -4,7 +4,7 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm.util" 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.filecache import FileCache from lastfm.util.objectcache import ObjectCache diff --git a/lastfm/util/lazylist.py b/lastfm/util/_lazylist.py similarity index 100% rename from lastfm/util/lazylist.py rename to lastfm/util/_lazylist.py diff --git a/lastfm/venue.py b/lastfm/venue.py index f676007..c863478 100644 --- a/lastfm/venue.py +++ b/lastfm/venue.py @@ -6,48 +6,21 @@ __license__ = "GNU Lesser General Public License" __package__ = "lastfm" from lastfm.base import LastfmBase -from lastfm.mixin import cacheable, searchable, crawlable +from lastfm.mixin import mixin from lastfm.decorators import cached_property, depaginate -@crawlable -@searchable -@cacheable +@mixin("crawlable", "searchable", "cacheable", "property_adder") class Venue(LastfmBase): """A class representing a venue of an event""" - def init(self, - api, - id = None, - name = None, - location = None, - url = None, - **kwargs): + + class Meta(object): + properties = ["id", "name", "location", "url"] + + def init(self, api, **kwargs): if not isinstance(api, Api): raise InvalidParametersError("api reference must be supplied as an argument") self._api = api - self._id = id - 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 + super(Venue, self).init(**kwargs) @cached_property def events(self): diff --git a/lastfm/wiki.py b/lastfm/wiki.py index 2ec249c..9f123f6 100644 --- a/lastfm/wiki.py +++ b/lastfm/wiki.py @@ -5,8 +5,15 @@ __version__ = "0.2" __license__ = "GNU Lesser General Public License" __package__ = "lastfm" +from lastfm.mixin import mixin + +@mixin("property_adder") class Wiki(object): """A class representing the information from the wiki of the subject.""" + + class Meta(object): + properties = ["subject", "published", "summary", "content"] + def __init__(self, subject, published = None, @@ -17,25 +24,5 @@ class Wiki(object): self._summary = summary 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): return "" % (self.subject.__class__.__name__, self.subject.name) \ No newline at end of file