use the new python enum instead of hand-rolled version

This commit is contained in:
Kyle Konrad 2014-04-02 10:23:48 -07:00
parent 8d196a8c01
commit c4e6f38963
4 changed files with 50 additions and 174 deletions

View file

@ -25,11 +25,12 @@ from munkres import Munkres
from beets import plugins from beets import plugins
from beets import config from beets import config
from beets.util import plurality from beets.util import plurality
from beets.util.enumeration import enum from beets.util.enumeration import OrderedEnum
from beets.autotag import hooks from beets.autotag import hooks
# Recommendation enumeration. # Recommendation enumeration.
recommendation = enum('none', 'low', 'medium', 'strong', name='recommendation') recommendation = OrderedEnum('recommendation', ['none', 'low', 'medium',
'strong'])
# Artist signals that indicate "various artists". These are used at the # Artist signals that indicate "various artists". These are used at the
# album level to determine whether a given release is likely a VA # album level to determine whether a given release is likely a VA

View file

@ -31,13 +31,12 @@ from beets import util
from beets import config from beets import config
from beets.util import pipeline from beets.util import pipeline
from beets.util import syspath, normpath, displayable_path from beets.util import syspath, normpath, displayable_path
from beets.util.enumeration import enum from enum import Enum
from beets import mediafile from beets import mediafile
action = enum( action = Enum('action',
'SKIP', 'ASIS', 'TRACKS', 'MANUAL', 'APPLY', 'MANUAL_ID', ['SKIP', 'ASIS', 'TRACKS', 'MANUAL', 'APPLY', 'MANUAL_ID',
'ALBUMS', name='action' 'ALBUMS'])
)
QUEUE_SIZE = 128 QUEUE_SIZE = 128
SINGLE_ARTIST_THRESH = 0.25 SINGLE_ARTIST_THRESH = 0.25

View file

@ -49,7 +49,7 @@ import imghdr
import os import os
import logging import logging
import traceback import traceback
from beets.util.enumeration import enum from beets.util.enumeration import IndexableEnum
__all__ = ['UnreadableFileError', 'FileTypeError', 'MediaFile'] __all__ = ['UnreadableFileError', 'FileTypeError', 'MediaFile']
@ -295,29 +295,31 @@ class Image(object):
* ``mime_type`` Read-only property that contains the mime type of * ``mime_type`` Read-only property that contains the mime type of
the binary data the binary data
""" """
TYPES = enum([ # By default, enums are 1-indexed. We make this enum zero-indexed
# by explicitly specifying the indicies of field
TYPES = IndexableEnum('TageImage.TYPES', [(k, i) for (i, k) in enumerate([
'other', 'other',
'icon', 'icon',
'other icon', 'other_icon',
'front', 'front',
'back', 'back',
'leaflet', 'leaflet',
'media', 'media',
'lead artist', 'lead_artist',
'artist', 'artist',
'conductor', 'conductor',
'group', 'group',
'composer', 'composer',
'lyricist', 'lyricist',
'recording location', 'recording_location',
'recording session', 'recording_session',
'performance', 'performance',
'screen capture', 'screen_capture',
'fish', 'fish',
'illustration', 'illustration',
'artist logo', 'artist_logo',
'publisher logo', 'publisher_logo',
], name='TageImage.TYPES') ])])
def __init__(self, data, desc=None, type=None): def __init__(self, data, desc=None, type=None):
self.data = data self.data = data

View file

@ -12,167 +12,41 @@
# The above copyright notice and this permission notice shall be # The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software. # included in all copies or substantial portions of the Software.
"""A metaclass for enumerated types that really are types. from enum import Enum, EnumMeta
You can create enumerations with `enum(values, [name])` and they work class OrderedEnum(Enum):
how you would expect them to.
>>> from enumeration import enum
>>> Direction = enum('north east south west', name='Direction')
>>> Direction.west
Direction.west
>>> Direction.west == Direction.west
True
>>> Direction.west == Direction.east
False
>>> isinstance(Direction.west, Direction)
True
>>> Direction[3]
Direction.west
>>> Direction['west']
Direction.west
>>> Direction.west.name
'west'
>>> Direction.north < Direction.west
True
Enumerations are classes; their instances represent the possible values
of the enumeration. Because Python classes must have names, you may
provide a `name` parameter to `enum`; if you don't, a meaningless one
will be chosen for you.
"""
import random
class Enumeration(type):
"""A metaclass whose classes are enumerations.
The `values` attribute of the class is used to populate the
enumeration. Values may either be a list of enumerated names or a
string containing a space-separated list of names. When the class
is created, it is instantiated for each name value in `values`.
Each such instance is the name of the enumerated item as the sole
argument.
The `Enumerated` class is a good choice for a superclass.
""" """
An Enum subclass that allows comparison of members.
def __init__(cls, name, bases, dic):
super(Enumeration, cls).__init__(name, bases, dic)
if 'values' not in dic:
# Do nothing if no values are provided (i.e., with
# Enumerated itself).
return
# May be called with a single string, in which case we split on
# whitespace for convenience.
values = dic['values']
if isinstance(values, basestring):
values = values.split()
# Create the Enumerated instances for each value. We have to use
# super's __setattr__ here because we disallow setattr below.
super(Enumeration, cls).__setattr__('_items_dict', {})
super(Enumeration, cls).__setattr__('_items_list', [])
for value in values:
item = cls(value, len(cls._items_list))
cls._items_dict[value] = item
cls._items_list.append(item)
def __getattr__(cls, key):
try:
return cls._items_dict[key]
except KeyError:
raise AttributeError("enumeration '" + cls.__name__ +
"' has no item '" + key + "'")
def __setattr__(cls, key, val):
raise TypeError("enumerations do not support attribute assignment")
def __getitem__(cls, key):
if isinstance(key, int):
return cls._items_list[key]
else:
return getattr(cls, key)
def __len__(cls):
return len(cls._items_list)
def __iter__(cls):
return iter(cls._items_list)
def __nonzero__(cls):
# Ensures that __len__ doesn't get called before __init__ by
# pydoc.
return True
class Enumerated(object):
"""An item in an enumeration.
Contains instance methods inherited by enumerated objects. The
metaclass is preset to `Enumeration` for your convenience.
Instance attributes:
name -- The name of the item.
index -- The index of the item in its enumeration.
>>> from enumeration import Enumerated
>>> class Garment(Enumerated):
... values = 'hat glove belt poncho lederhosen suspenders'
... def wear(self):
... print('now wearing a ' + self.name)
...
>>> Garment.poncho.wear()
now wearing a poncho
""" """
def __ge__(self, other):
if self.__class__ is other.__class__:
return self._value_ >= other._value_
return NotImplemented
def __gt__(self, other):
if self.__class__ is other.__class__:
return self._value_ > other._value_
return NotImplemented
def __le__(self, other):
if self.__class__ is other.__class__:
return self._value_ <= other._value_
return NotImplemented
def __lt__(self, other):
if self.__class__ is other.__class__:
return self._value_ < other._value_
return NotImplemented
__metaclass__ = Enumeration class IndexableEnumMeta(EnumMeta):
def __init__(self, name, index):
self.name = name
self.index = index
def __str__(self):
return type(self).__name__ + '.' + self.name
def __repr__(self):
return str(self)
def __cmp__(self, other):
if type(self) is type(other):
# Note that we're assuming that the items are direct
# instances of the same Enumeration (i.e., no fancy
# subclassing), which is probably okay.
return cmp(self.index, other.index)
else:
return NotImplemented
def enum(*values, **kwargs):
"""Shorthand for creating a new Enumeration class.
Call with enumeration values as a list, a space-delimited string, or
just an argument list. To give the class a name, pass it as the
`name` keyword argument. Otherwise, a name will be chosen for you.
The following are all equivalent:
enum('pinkie ring middle index thumb')
enum('pinkie', 'ring', 'middle', 'index', 'thumb')
enum(['pinkie', 'ring', 'middle', 'index', 'thumb'])
""" """
Metaclass for Enums that support indexing by value
"""
def __getitem__(obj, x):
if isinstance(x, int):
return obj._value2member_map_[x]
return super(IndexableEnumMeta, EnumMeta).__getitem__(obj, x)
if ('name' not in kwargs) or kwargs['name'] is None: class IndexableEnum(Enum):
# Create a probably-unique name. It doesn't really have to be """
# unique, but getting distinct names each time helps with An Enum subclass that suports indexing by value.
# identification in debugging. """
name = 'Enumeration' + hex(random.randint(0,0xfffffff))[2:].upper() __metaclass__ = IndexableEnumMeta
else:
name = kwargs['name']
if len(values) == 1:
# If there's only one value, we have a couple of alternate calling
# styles.
if isinstance(values[0], basestring) or hasattr(values[0], '__iter__'):
values = values[0]
return type(name, (Enumerated,), {'values': values})