mirror of
https://github.com/beetbox/beets.git
synced 2026-01-30 20:13:37 +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 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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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})
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue