mirror of
https://github.com/beetbox/beets.git
synced 2025-12-25 10:05:13 +01:00
use the new python enum instead of hand-rolled version
This commit is contained in:
parent
8d196a8c01
commit
c4e6f38963
4 changed files with 50 additions and 174 deletions
|
|
@ -25,11 +25,12 @@ from munkres import Munkres
|
|||
from beets import plugins
|
||||
from beets import config
|
||||
from beets.util import plurality
|
||||
from beets.util.enumeration import enum
|
||||
from beets.util.enumeration import OrderedEnum
|
||||
from beets.autotag import hooks
|
||||
|
||||
# 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
|
||||
# album level to determine whether a given release is likely a VA
|
||||
|
|
|
|||
|
|
@ -31,13 +31,12 @@ from beets import util
|
|||
from beets import config
|
||||
from beets.util import pipeline
|
||||
from beets.util import syspath, normpath, displayable_path
|
||||
from beets.util.enumeration import enum
|
||||
from enum import Enum
|
||||
from beets import mediafile
|
||||
|
||||
action = enum(
|
||||
'SKIP', 'ASIS', 'TRACKS', 'MANUAL', 'APPLY', 'MANUAL_ID',
|
||||
'ALBUMS', name='action'
|
||||
)
|
||||
action = Enum('action',
|
||||
['SKIP', 'ASIS', 'TRACKS', 'MANUAL', 'APPLY', 'MANUAL_ID',
|
||||
'ALBUMS'])
|
||||
|
||||
QUEUE_SIZE = 128
|
||||
SINGLE_ARTIST_THRESH = 0.25
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ import imghdr
|
|||
import os
|
||||
import logging
|
||||
import traceback
|
||||
from beets.util.enumeration import enum
|
||||
from beets.util.enumeration import IndexableEnum
|
||||
|
||||
__all__ = ['UnreadableFileError', 'FileTypeError', 'MediaFile']
|
||||
|
||||
|
|
@ -295,29 +295,31 @@ class Image(object):
|
|||
* ``mime_type`` Read-only property that contains the mime type of
|
||||
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',
|
||||
'icon',
|
||||
'other icon',
|
||||
'other_icon',
|
||||
'front',
|
||||
'back',
|
||||
'leaflet',
|
||||
'media',
|
||||
'lead artist',
|
||||
'lead_artist',
|
||||
'artist',
|
||||
'conductor',
|
||||
'group',
|
||||
'composer',
|
||||
'lyricist',
|
||||
'recording location',
|
||||
'recording session',
|
||||
'recording_location',
|
||||
'recording_session',
|
||||
'performance',
|
||||
'screen capture',
|
||||
'screen_capture',
|
||||
'fish',
|
||||
'illustration',
|
||||
'artist logo',
|
||||
'publisher logo',
|
||||
], name='TageImage.TYPES')
|
||||
'artist_logo',
|
||||
'publisher_logo',
|
||||
])])
|
||||
|
||||
def __init__(self, data, desc=None, type=None):
|
||||
self.data = data
|
||||
|
|
|
|||
|
|
@ -12,167 +12,41 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# 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
|
||||
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.
|
||||
class OrderedEnum(Enum):
|
||||
"""
|
||||
|
||||
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
|
||||
An Enum subclass that allows comparison of members.
|
||||
"""
|
||||
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
|
||||
|
||||
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'])
|
||||
class IndexableEnumMeta(EnumMeta):
|
||||
"""
|
||||
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:
|
||||
# Create a probably-unique name. It doesn't really have to be
|
||||
# unique, but getting distinct names each time helps with
|
||||
# identification in debugging.
|
||||
name = 'Enumeration' + hex(random.randint(0,0xfffffff))[2:].upper()
|
||||
else:
|
||||
name = kwargs['name']
|
||||
class IndexableEnum(Enum):
|
||||
"""
|
||||
An Enum subclass that suports indexing by value.
|
||||
"""
|
||||
__metaclass__ = IndexableEnumMeta
|
||||
|
||||
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})
|
||||
|
|
|
|||
Loading…
Reference in a new issue