models: Convert all models to using fields.
This commit is contained in:
parent
07912e1091
commit
4faf4de7aa
125
mopidy/models.py
125
mopidy/models.py
@ -128,12 +128,15 @@ class ImmutableObject(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
Superclass for immutable objects whose fields can only be modified via the
|
Superclass for immutable objects whose fields can only be modified via the
|
||||||
constructor.
|
constructor. Fields should be :class:`Field` instances to ensure type
|
||||||
|
safety in our models.
|
||||||
|
|
||||||
:param kwargs: kwargs to set as fields on the object
|
:param kwargs: kwargs to set as fields on the object
|
||||||
:type kwargs: any
|
:type kwargs: any
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
__metaclass__ = FieldOwner
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
if not hasattr(self, key) or callable(getattr(self, key)):
|
if not hasattr(self, key) or callable(getattr(self, key)):
|
||||||
@ -142,7 +145,7 @@ class ImmutableObject(object):
|
|||||||
key)
|
key)
|
||||||
if value == getattr(self, key):
|
if value == getattr(self, key):
|
||||||
continue # Don't explicitly set default values
|
continue # Don't explicitly set default values
|
||||||
self.__dict__[key] = value
|
super(ImmutableObject, self).__setattr__(key, value)
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
def __setattr__(self, name, value):
|
||||||
if name.startswith('_'):
|
if name.startswith('_'):
|
||||||
@ -192,7 +195,7 @@ class ImmutableObject(object):
|
|||||||
:type values: dict
|
:type values: dict
|
||||||
:rtype: new instance of the model being copied
|
:rtype: new instance of the model being copied
|
||||||
"""
|
"""
|
||||||
data = {}
|
data = {} # TODO: do we need public key handling now?
|
||||||
for key in self.__dict__.keys():
|
for key in self.__dict__.keys():
|
||||||
public_key = key.lstrip('_')
|
public_key = key.lstrip('_')
|
||||||
value = values.pop(public_key, self.__dict__[key])
|
value = values.pop(public_key, self.__dict__[key])
|
||||||
@ -207,7 +210,7 @@ class ImmutableObject(object):
|
|||||||
return self.__class__(**data)
|
return self.__class__(**data)
|
||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
data = {}
|
data = {} # TODO: do we need public key handling now?
|
||||||
data['__model__'] = self.__class__.__name__
|
data['__model__'] = self.__class__.__name__
|
||||||
for key in self.__dict__.keys():
|
for key in self.__dict__.keys():
|
||||||
public_key = key.lstrip('_')
|
public_key = key.lstrip('_')
|
||||||
@ -282,14 +285,10 @@ class Ref(ImmutableObject):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
#: The object URI. Read-only.
|
#: The object URI. Read-only.
|
||||||
uri = None
|
uri = String()
|
||||||
|
|
||||||
#: The object name. Read-only.
|
#: The object name. Read-only.
|
||||||
name = None
|
name = String()
|
||||||
|
|
||||||
#: The object type, e.g. "artist", "album", "track", "playlist",
|
|
||||||
#: "directory". Read-only.
|
|
||||||
type = None
|
|
||||||
|
|
||||||
#: Constant used for comparison with the :attr:`type` field.
|
#: Constant used for comparison with the :attr:`type` field.
|
||||||
ALBUM = 'album'
|
ALBUM = 'album'
|
||||||
@ -306,6 +305,10 @@ class Ref(ImmutableObject):
|
|||||||
#: Constant used for comparison with the :attr:`type` field.
|
#: Constant used for comparison with the :attr:`type` field.
|
||||||
TRACK = 'track'
|
TRACK = 'track'
|
||||||
|
|
||||||
|
#: The object type, e.g. "artist", "album", "track", "playlist",
|
||||||
|
#: "directory". Read-only.
|
||||||
|
type = Field(choices=(ALBUM, ARTIST, DIRECTORY, PLAYLIST, TRACK))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def album(cls, **kwargs):
|
def album(cls, **kwargs):
|
||||||
"""Create a :class:`Ref` with ``type`` :attr:`ALBUM`."""
|
"""Create a :class:`Ref` with ``type`` :attr:`ALBUM`."""
|
||||||
@ -346,13 +349,13 @@ class Image(ImmutableObject):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
#: The image URI. Read-only.
|
#: The image URI. Read-only.
|
||||||
uri = None
|
uri = String()
|
||||||
|
|
||||||
#: Optional width of the image or :class:`None`. Read-only.
|
#: Optional width of the image or :class:`None`. Read-only.
|
||||||
width = None
|
width = Integer(min=0)
|
||||||
|
|
||||||
#: Optional height of the image or :class:`None`. Read-only.
|
#: Optional height of the image or :class:`None`. Read-only.
|
||||||
height = None
|
height = Integer(min=0)
|
||||||
|
|
||||||
|
|
||||||
class Artist(ImmutableObject):
|
class Artist(ImmutableObject):
|
||||||
@ -367,13 +370,13 @@ class Artist(ImmutableObject):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
#: The artist URI. Read-only.
|
#: The artist URI. Read-only.
|
||||||
uri = None
|
uri = String()
|
||||||
|
|
||||||
#: The artist name. Read-only.
|
#: The artist name. Read-only.
|
||||||
name = None
|
name = String()
|
||||||
|
|
||||||
#: The MusicBrainz ID of the artist. Read-only.
|
#: The MusicBrainz ID of the artist. Read-only.
|
||||||
musicbrainz_id = None
|
musicbrainz_id = String()
|
||||||
|
|
||||||
|
|
||||||
class Album(ImmutableObject):
|
class Album(ImmutableObject):
|
||||||
@ -398,37 +401,32 @@ class Album(ImmutableObject):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
#: The album URI. Read-only.
|
#: The album URI. Read-only.
|
||||||
uri = None
|
uri = String()
|
||||||
|
|
||||||
#: The album name. Read-only.
|
#: The album name. Read-only.
|
||||||
name = None
|
name = String()
|
||||||
|
|
||||||
#: A set of album artists. Read-only.
|
#: A set of album artists. Read-only.
|
||||||
artists = frozenset()
|
artists = Collection(type=Artist, container=frozenset)
|
||||||
|
|
||||||
#: The number of tracks in the album. Read-only.
|
#: The number of tracks in the album. Read-only.
|
||||||
num_tracks = None
|
num_tracks = Integer(min=0)
|
||||||
|
|
||||||
#: The number of discs in the album. Read-only.
|
#: The number of discs in the album. Read-only.
|
||||||
num_discs = None
|
num_discs = Integer(min=0)
|
||||||
|
|
||||||
#: The album release date. Read-only.
|
#: The album release date. Read-only.
|
||||||
date = None
|
date = String() # TODO: add date type
|
||||||
|
|
||||||
#: The MusicBrainz ID of the album. Read-only.
|
#: The MusicBrainz ID of the album. Read-only.
|
||||||
musicbrainz_id = None
|
musicbrainz_id = String()
|
||||||
|
|
||||||
#: The album image URIs. Read-only.
|
#: The album image URIs. Read-only.
|
||||||
images = frozenset()
|
images = Collection(type=basestring, container=frozenset)
|
||||||
# XXX If we want to keep the order of images we shouldn't use frozenset()
|
# XXX If we want to keep the order of images we shouldn't use frozenset()
|
||||||
# as it doesn't preserve order. I'm deferring this issue until we got
|
# as it doesn't preserve order. I'm deferring this issue until we got
|
||||||
# actual usage of this field with more than one image.
|
# actual usage of this field with more than one image.
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.__dict__['artists'] = frozenset(kwargs.pop('artists', None) or [])
|
|
||||||
self.__dict__['images'] = frozenset(kwargs.pop('images', None) or [])
|
|
||||||
super(Album, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class Track(ImmutableObject):
|
class Track(ImmutableObject):
|
||||||
|
|
||||||
@ -466,61 +464,52 @@ class Track(ImmutableObject):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
#: The track URI. Read-only.
|
#: The track URI. Read-only.
|
||||||
uri = None
|
uri = String()
|
||||||
|
|
||||||
#: The track name. Read-only.
|
#: The track name. Read-only.
|
||||||
name = None
|
name = String()
|
||||||
|
|
||||||
#: A set of track artists. Read-only.
|
#: A set of track artists. Read-only.
|
||||||
artists = frozenset()
|
artists = Collection(type=Artist, container=frozenset)
|
||||||
|
|
||||||
#: The track :class:`Album`. Read-only.
|
#: The track :class:`Album`. Read-only.
|
||||||
album = None
|
album = Field(type=Album)
|
||||||
|
|
||||||
#: A set of track composers. Read-only.
|
#: A set of track composers. Read-only.
|
||||||
composers = frozenset()
|
composers = Collection(type=Artist, container=frozenset)
|
||||||
|
|
||||||
#: A set of track performers`. Read-only.
|
#: A set of track performers`. Read-only.
|
||||||
performers = frozenset()
|
performers = Collection(type=Artist, container=frozenset)
|
||||||
|
|
||||||
#: The track genre. Read-only.
|
#: The track genre. Read-only.
|
||||||
genre = None
|
genre = String()
|
||||||
|
|
||||||
#: The track number in the album. Read-only.
|
#: The track number in the album. Read-only.
|
||||||
track_no = None
|
track_no = Integer(min=0)
|
||||||
|
|
||||||
#: The disc number in the album. Read-only.
|
#: The disc number in the album. Read-only.
|
||||||
disc_no = None
|
disc_no = Integer(min=0)
|
||||||
|
|
||||||
#: The track release date. Read-only.
|
#: The track release date. Read-only.
|
||||||
date = None
|
date = String() # TODO: add date type
|
||||||
|
|
||||||
#: The track length in milliseconds. Read-only.
|
#: The track length in milliseconds. Read-only.
|
||||||
length = None
|
length = Integer(min=0)
|
||||||
|
|
||||||
#: The track's bitrate in kbit/s. Read-only.
|
#: The track's bitrate in kbit/s. Read-only.
|
||||||
bitrate = None
|
bitrate = Integer(min=0)
|
||||||
|
|
||||||
#: The track comment. Read-only.
|
#: The track comment. Read-only.
|
||||||
comment = None
|
comment = String()
|
||||||
|
|
||||||
#: The MusicBrainz ID of the track. Read-only.
|
#: The MusicBrainz ID of the track. Read-only.
|
||||||
musicbrainz_id = None
|
musicbrainz_id = String()
|
||||||
|
|
||||||
#: Integer representing when the track was last modified. Exact meaning
|
#: Integer representing when the track was last modified. Exact meaning
|
||||||
#: depends on source of track. For local files this is the modification
|
#: depends on source of track. For local files this is the modification
|
||||||
#: time in milliseconds since Unix epoch. For other backends it could be an
|
#: time in milliseconds since Unix epoch. For other backends it could be an
|
||||||
#: equivalent timestamp or simply a version counter.
|
#: equivalent timestamp or simply a version counter.
|
||||||
last_modified = None
|
last_modified = Integer(min=0)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
def get(key):
|
|
||||||
return frozenset(kwargs.pop(key, None) or [])
|
|
||||||
|
|
||||||
self.__dict__['artists'] = get('artists')
|
|
||||||
self.__dict__['composers'] = get('composers')
|
|
||||||
self.__dict__['performers'] = get('performers')
|
|
||||||
super(Track, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class TlTrack(ImmutableObject):
|
class TlTrack(ImmutableObject):
|
||||||
@ -546,10 +535,10 @@ class TlTrack(ImmutableObject):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
#: The tracklist ID. Read-only.
|
#: The tracklist ID. Read-only.
|
||||||
tlid = None
|
tlid = Integer(min=0)
|
||||||
|
|
||||||
#: The track. Read-only.
|
#: The track. Read-only.
|
||||||
track = None
|
track = Field(type=Track)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
if len(args) == 2 and len(kwargs) == 0:
|
if len(args) == 2 and len(kwargs) == 0:
|
||||||
@ -577,23 +566,19 @@ class Playlist(ImmutableObject):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
#: The playlist URI. Read-only.
|
#: The playlist URI. Read-only.
|
||||||
uri = None
|
uri = String()
|
||||||
|
|
||||||
#: The playlist name. Read-only.
|
#: The playlist name. Read-only.
|
||||||
name = None
|
name = String()
|
||||||
|
|
||||||
#: The playlist's tracks. Read-only.
|
#: The playlist's tracks. Read-only.
|
||||||
tracks = tuple()
|
tracks = Collection(type=Track, container=tuple)
|
||||||
|
|
||||||
#: The playlist modification time in milliseconds since Unix epoch.
|
#: The playlist modification time in milliseconds since Unix epoch.
|
||||||
#: Read-only.
|
#: Read-only.
|
||||||
#:
|
#:
|
||||||
#: Integer, or :class:`None` if unknown.
|
#: Integer, or :class:`None` if unknown.
|
||||||
last_modified = None
|
last_modified = Integer(min=0)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.__dict__['tracks'] = tuple(kwargs.pop('tracks', None) or [])
|
|
||||||
super(Playlist, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
# TODO: def insert(self, pos, track): ... ?
|
# TODO: def insert(self, pos, track): ... ?
|
||||||
|
|
||||||
@ -617,19 +602,13 @@ class SearchResult(ImmutableObject):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# The search result URI. Read-only.
|
# The search result URI. Read-only.
|
||||||
uri = None
|
uri = String()
|
||||||
|
|
||||||
# The tracks matching the search query. Read-only.
|
# The tracks matching the search query. Read-only.
|
||||||
tracks = tuple()
|
tracks = Collection(type=Track, container=tuple)
|
||||||
|
|
||||||
# The artists matching the search query. Read-only.
|
# The artists matching the search query. Read-only.
|
||||||
artists = tuple()
|
artists = Collection(type=Artist, container=tuple)
|
||||||
|
|
||||||
# The albums matching the search query. Read-only.
|
# The albums matching the search query. Read-only.
|
||||||
albums = tuple()
|
albums = Collection(type=Album, container=tuple)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.__dict__['tracks'] = tuple(kwargs.pop('tracks', None) or [])
|
|
||||||
self.__dict__['artists'] = tuple(kwargs.pop('artists', None) or [])
|
|
||||||
self.__dict__['albums'] = tuple(kwargs.pop('albums', None) or [])
|
|
||||||
super(SearchResult, self).__init__(*args, **kwargs)
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user