replace unicode with six.text_type

This commit is contained in:
Johnny Robeson 2016-06-20 04:34:24 -04:00
parent 7b66dfec4b
commit e8afcbe7ec
56 changed files with 269 additions and 203 deletions

View file

@ -27,6 +27,7 @@ from beets.util import as_string
from beets.autotag import mb from beets.autotag import mb
from jellyfish import levenshtein_distance from jellyfish import levenshtein_distance
from unidecode import unidecode from unidecode import unidecode
import six
log = logging.getLogger('beets') log = logging.getLogger('beets')
@ -205,8 +206,8 @@ def _string_dist_basic(str1, str2):
transliteration/lowering to ASCII characters. Normalized by string transliteration/lowering to ASCII characters. Normalized by string
length. length.
""" """
assert isinstance(str1, unicode) assert isinstance(str1, six.text_type)
assert isinstance(str2, unicode) assert isinstance(str2, six.text_type)
str1 = as_string(unidecode(str1)) str1 = as_string(unidecode(str1))
str2 = as_string(unidecode(str2)) str2 = as_string(unidecode(str2))
str1 = re.sub(r'[^a-z0-9]', '', str1.lower()) str1 = re.sub(r'[^a-z0-9]', '', str1.lower())

View file

@ -27,6 +27,7 @@ import beets.autotag.hooks
import beets import beets
from beets import util from beets import util
from beets import config from beets import config
import six
VARIOUS_ARTISTS_ID = '89ad4ac3-39f7-470e-963a-56509c546377' VARIOUS_ARTISTS_ID = '89ad4ac3-39f7-470e-963a-56509c546377'
BASE_URL = 'http://musicbrainz.org/' BASE_URL = 'http://musicbrainz.org/'
@ -69,7 +70,8 @@ def configure():
"""Set up the python-musicbrainz-ngs module according to settings """Set up the python-musicbrainz-ngs module according to settings
from the beets configuration. This should be called at startup. from the beets configuration. This should be called at startup.
""" """
musicbrainzngs.set_hostname(config['musicbrainz']['host'].get(unicode)) hostname = config['musicbrainz']['host'].get(six.text_type)
musicbrainzngs.set_hostname(hostname)
musicbrainzngs.set_rate_limit( musicbrainzngs.set_rate_limit(
config['musicbrainz']['ratelimit_interval'].as_number(), config['musicbrainz']['ratelimit_interval'].as_number(),
config['musicbrainz']['ratelimit'].get(int), config['musicbrainz']['ratelimit'].get(int),
@ -260,7 +262,7 @@ def album_info(release):
) )
info.va = info.artist_id == VARIOUS_ARTISTS_ID info.va = info.artist_id == VARIOUS_ARTISTS_ID
if info.va: if info.va:
info.artist = config['va_name'].get(unicode) info.artist = config['va_name'].get(six.text_type)
info.asin = release.get('asin') info.asin = release.get('asin')
info.releasegroup_id = release['release-group']['id'] info.releasegroup_id = release['release-group']['id']
info.country = release.get('country') info.country = release.get('country')
@ -329,7 +331,7 @@ def match_album(artist, album, tracks=None):
# Various Artists search. # Various Artists search.
criteria['arid'] = VARIOUS_ARTISTS_ID criteria['arid'] = VARIOUS_ARTISTS_ID
if tracks is not None: if tracks is not None:
criteria['tracks'] = unicode(tracks) criteria['tracks'] = six.text_type(tracks)
# Abort if we have no search terms. # Abort if we have no search terms.
if not any(criteria.itervalues()): if not any(criteria.itervalues()):

View file

@ -29,6 +29,7 @@ import beets
from beets.util.functemplate import Template from beets.util.functemplate import Template
from beets.dbcore import types from beets.dbcore import types
from .query import MatchQuery, NullSort, TrueQuery from .query import MatchQuery, NullSort, TrueQuery
import six
class FormattedMapping(collections.Mapping): class FormattedMapping(collections.Mapping):
@ -69,7 +70,7 @@ class FormattedMapping(collections.Mapping):
value = value.decode('utf8', 'ignore') value = value.decode('utf8', 'ignore')
if self.for_path: if self.for_path:
sep_repl = beets.config['path_sep_replace'].get(unicode) sep_repl = beets.config['path_sep_replace'].get(six.text_type)
for sep in (os.path.sep, os.path.altsep): for sep in (os.path.sep, os.path.altsep):
if sep: if sep:
value = value.replace(sep, sep_repl) value = value.replace(sep, sep_repl)

View file

@ -23,6 +23,7 @@ from beets import util
from datetime import datetime, timedelta from datetime import datetime, timedelta
import unicodedata import unicodedata
from functools import reduce from functools import reduce
import six
class ParsingError(ValueError): class ParsingError(ValueError):
@ -246,8 +247,8 @@ class BytesQuery(MatchQuery):
# Use a buffer representation of the pattern for SQLite # Use a buffer representation of the pattern for SQLite
# matching. This instructs SQLite to treat the blob as binary # matching. This instructs SQLite to treat the blob as binary
# rather than encoded Unicode. # rather than encoded Unicode.
if isinstance(self.pattern, (unicode, bytes)): if isinstance(self.pattern, (six.text_type, bytes)):
if isinstance(self.pattern, unicode): if isinstance(self.pattern, six.text_type):
self.pattern = self.pattern.encode('utf8') self.pattern = self.pattern.encode('utf8')
self.buf_pattern = buffer(self.pattern) self.buf_pattern = buffer(self.pattern)
elif isinstance(self.pattern, buffer): elif isinstance(self.pattern, buffer):
@ -793,7 +794,7 @@ class FieldSort(Sort):
def key(item): def key(item):
field_val = item.get(self.field, '') field_val = item.get(self.field, '')
if self.case_insensitive and isinstance(field_val, unicode): if self.case_insensitive and isinstance(field_val, six.text_type):
field_val = field_val.lower() field_val = field_val.lower()
return field_val return field_val

View file

@ -19,6 +19,7 @@ from __future__ import division, absolute_import, print_function
from . import query from . import query
from beets.util import str2bool from beets.util import str2bool
import six
# Abstract base. # Abstract base.
@ -37,7 +38,7 @@ class Type(object):
"""The `Query` subclass to be used when querying the field. """The `Query` subclass to be used when querying the field.
""" """
model_type = unicode model_type = six.text_type
"""The Python type that is used to represent the value in the model. """The Python type that is used to represent the value in the model.
The model is guaranteed to return a value of this type if the field The model is guaranteed to return a value of this type if the field
@ -63,7 +64,7 @@ class Type(object):
if isinstance(value, bytes): if isinstance(value, bytes):
value = value.decode('utf8', 'ignore') value = value.decode('utf8', 'ignore')
return unicode(value) return six.text_type(value)
def parse(self, string): def parse(self, string):
"""Parse a (possibly human-written) string and return the """Parse a (possibly human-written) string and return the
@ -102,7 +103,7 @@ class Type(object):
""" """
if isinstance(sql_value, buffer): if isinstance(sql_value, buffer):
sql_value = bytes(sql_value).decode('utf8', 'ignore') sql_value = bytes(sql_value).decode('utf8', 'ignore')
if isinstance(sql_value, unicode): if isinstance(sql_value, six.text_type):
return self.parse(sql_value) return self.parse(sql_value)
else: else:
return self.normalize(sql_value) return self.normalize(sql_value)
@ -194,7 +195,7 @@ class Boolean(Type):
model_type = bool model_type = bool
def format(self, value): def format(self, value):
return unicode(bool(value)) return six.text_type(bool(value))
def parse(self, string): def parse(self, string):
return str2bool(string) return str2bool(string)

View file

@ -14,6 +14,7 @@
# included in all copies or substantial portions of the Software. # included in all copies or substantial portions of the Software.
from __future__ import division, absolute_import, print_function from __future__ import division, absolute_import, print_function
import six
"""Provides the basic, interface-agnostic workflow for importing and """Provides the basic, interface-agnostic workflow for importing and
autotagging music files. autotagging music files.
@ -640,7 +641,7 @@ class ImportTask(BaseImportTask):
changes['comp'] = False changes['comp'] = False
else: else:
# VA. # VA.
changes['albumartist'] = config['va_name'].get(unicode) changes['albumartist'] = config['va_name'].get(six.text_type)
changes['comp'] = True changes['comp'] = True
elif self.choice_flag in (action.APPLY, action.RETAG): elif self.choice_flag in (action.APPLY, action.RETAG):

View file

@ -124,14 +124,15 @@ class DateType(types.Float):
query = dbcore.query.DateQuery query = dbcore.query.DateQuery
def format(self, value): def format(self, value):
return time.strftime(beets.config['time_format'].get(unicode), return time.strftime(beets.config['time_format'].get(six.text_type),
time.localtime(value or 0)) time.localtime(value or 0))
def parse(self, string): def parse(self, string):
try: try:
# Try a formatted date string. # Try a formatted date string.
return time.mktime( return time.mktime(
time.strptime(string, beets.config['time_format'].get(unicode)) time.strptime(string,
beets.config['time_format'].get(six.text_type))
) )
except ValueError: except ValueError:
# Fall back to a plain timestamp number. # Fall back to a plain timestamp number.
@ -153,7 +154,7 @@ class PathType(types.Type):
return normpath(bytestring_path(string)) return normpath(bytestring_path(string))
def normalize(self, value): def normalize(self, value):
if isinstance(value, unicode): if isinstance(value, six.text_type):
# Paths stored internally as encoded bytes. # Paths stored internally as encoded bytes.
return bytestring_path(value) return bytestring_path(value)
@ -281,11 +282,11 @@ class FileOperationError(Exception):
""" """
return u'{0}: {1}'.format( return u'{0}: {1}'.format(
util.displayable_path(self.path), util.displayable_path(self.path),
unicode(self.reason) six.text_type(self.reason)
) )
def __str__(self): def __str__(self):
return unicode(self).encode('utf8') return six.text_type(self).encode('utf8')
class ReadError(FileOperationError): class ReadError(FileOperationError):
@ -331,7 +332,7 @@ class LibModel(dbcore.Model):
def __format__(self, spec): def __format__(self, spec):
if not spec: if not spec:
spec = beets.config[self._format_config_key].get(unicode) spec = beets.config[self._format_config_key].get(six.text_type)
result = self.evaluate_template(spec) result = self.evaluate_template(spec)
if isinstance(spec, bytes): if isinstance(spec, bytes):
# if spec is a byte string then we must return a one as well # if spec is a byte string then we must return a one as well
@ -517,7 +518,7 @@ class Item(LibModel):
""" """
# Encode unicode paths and read buffers. # Encode unicode paths and read buffers.
if key == 'path': if key == 'path':
if isinstance(value, unicode): if isinstance(value, six.text_type):
value = bytestring_path(value) value = bytestring_path(value)
elif isinstance(value, buffer): elif isinstance(value, buffer):
value = bytes(value) value = bytes(value)
@ -1061,7 +1062,8 @@ class Album(LibModel):
image = bytestring_path(image) image = bytestring_path(image)
item_dir = item_dir or self.item_dir() item_dir = item_dir or self.item_dir()
filename_tmpl = Template(beets.config['art_filename'].get(unicode)) filename_tmpl = Template(
beets.config['art_filename'].get(six.text_type))
subpath = self.evaluate_template(filename_tmpl, True) subpath = self.evaluate_template(filename_tmpl, True)
if beets.config['asciify_paths']: if beets.config['asciify_paths']:
subpath = unidecode(subpath) subpath = unidecode(subpath)
@ -1179,7 +1181,8 @@ def parse_query_string(s, model_cls):
The string is split into components using shell-like syntax. The string is split into components using shell-like syntax.
""" """
assert isinstance(s, unicode), u"Query is not unicode: {0!r}".format(s) message = u"Query is not unicode: {0!r}".format(s)
assert isinstance(s, six.text_type), message
try: try:
parts = util.shlex_split(s) parts = util.shlex_split(s)
except ValueError as exc: except ValueError as exc:
@ -1405,7 +1408,7 @@ class DefaultTemplateFunctions(object):
def tmpl_time(s, fmt): def tmpl_time(s, fmt):
"""Format a time value using `strftime`. """Format a time value using `strftime`.
""" """
cur_fmt = beets.config['time_format'].get(unicode) cur_fmt = beets.config['time_format'].get(six.text_type)
return time.strftime(fmt, time.strptime(s, cur_fmt)) return time.strftime(fmt, time.strptime(s, cur_fmt))
def tmpl_aunique(self, keys=None, disam=None): def tmpl_aunique(self, keys=None, disam=None):

View file

@ -27,6 +27,7 @@ from copy import copy
from logging import * # noqa from logging import * # noqa
import subprocess import subprocess
import threading import threading
import six
def logsafe(val): def logsafe(val):
@ -42,7 +43,7 @@ def logsafe(val):
example. example.
""" """
# Already Unicode. # Already Unicode.
if isinstance(val, unicode): if isinstance(val, six.text_type):
return val return val
# Bytestring: needs decoding. # Bytestring: needs decoding.
@ -56,7 +57,7 @@ def logsafe(val):
# A "problem" object: needs a workaround. # A "problem" object: needs a workaround.
elif isinstance(val, subprocess.CalledProcessError): elif isinstance(val, subprocess.CalledProcessError):
try: try:
return unicode(val) return six.text_type(val)
except UnicodeDecodeError: except UnicodeDecodeError:
# An object with a broken __unicode__ formatter. Use __str__ # An object with a broken __unicode__ formatter. Use __str__
# instead. # instead.

View file

@ -59,6 +59,7 @@ import enum
from beets import logging from beets import logging
from beets.util import displayable_path, syspath, as_string from beets.util import displayable_path, syspath, as_string
import six
__all__ = ['UnreadableFileError', 'FileTypeError', 'MediaFile'] __all__ = ['UnreadableFileError', 'FileTypeError', 'MediaFile']
@ -131,7 +132,7 @@ def _safe_cast(out_type, val):
else: else:
# Process any other type as a string. # Process any other type as a string.
if not isinstance(val, basestring): if not isinstance(val, basestring):
val = unicode(val) val = six.text_type(val)
# Get a number from the front of the string. # Get a number from the front of the string.
val = re.match(r'[0-9]*', val.strip()).group(0) val = re.match(r'[0-9]*', val.strip()).group(0)
if not val: if not val:
@ -146,13 +147,13 @@ def _safe_cast(out_type, val):
except ValueError: except ValueError:
return False return False
elif out_type == unicode: elif out_type == six.text_type:
if isinstance(val, bytes): if isinstance(val, bytes):
return val.decode('utf8', 'ignore') return val.decode('utf8', 'ignore')
elif isinstance(val, unicode): elif isinstance(val, six.text_type):
return val return val
else: else:
return unicode(val) return six.text_type(val)
elif out_type == float: elif out_type == float:
if isinstance(val, int) or isinstance(val, float): if isinstance(val, int) or isinstance(val, float):
@ -161,7 +162,7 @@ def _safe_cast(out_type, val):
if isinstance(val, bytes): if isinstance(val, bytes):
val = val.decode('utf8', 'ignore') val = val.decode('utf8', 'ignore')
else: else:
val = unicode(val) val = six.text_type(val)
match = re.match(r'[\+-]?([0-9]+\.?[0-9]*|[0-9]*\.[0-9]+)', match = re.match(r'[\+-]?([0-9]+\.?[0-9]*|[0-9]*\.[0-9]+)',
val.strip()) val.strip())
if match: if match:
@ -220,7 +221,7 @@ def _sc_decode(soundcheck):
""" """
# We decode binary data. If one of the formats gives us a text # We decode binary data. If one of the formats gives us a text
# string, interpret it as UTF-8. # string, interpret it as UTF-8.
if isinstance(soundcheck, unicode): if isinstance(soundcheck, six.text_type):
soundcheck = soundcheck.encode('utf8') soundcheck = soundcheck.encode('utf8')
# SoundCheck tags consist of 10 numbers, each represented by 8 # SoundCheck tags consist of 10 numbers, each represented by 8
@ -407,7 +408,8 @@ class StorageStyle(object):
"""List of mutagen classes the StorageStyle can handle. """List of mutagen classes the StorageStyle can handle.
""" """
def __init__(self, key, as_type=unicode, suffix=None, float_places=2): def __init__(self, key, as_type=six.text_type, suffix=None,
float_places=2):
"""Create a basic storage strategy. Parameters: """Create a basic storage strategy. Parameters:
- `key`: The key on the Mutagen file object used to access the - `key`: The key on the Mutagen file object used to access the
@ -426,8 +428,8 @@ class StorageStyle(object):
self.float_places = float_places self.float_places = float_places
# Convert suffix to correct string type. # Convert suffix to correct string type.
if self.suffix and self.as_type is unicode \ if self.suffix and self.as_type is six.text_type \
and not isinstance(self.suffix, unicode): and not isinstance(self.suffix, six.text_type):
self.suffix = self.suffix.decode('utf8') self.suffix = self.suffix.decode('utf8')
# Getter. # Getter.
@ -450,7 +452,7 @@ class StorageStyle(object):
"""Given a raw value stored on a Mutagen object, decode and """Given a raw value stored on a Mutagen object, decode and
return the represented value. return the represented value.
""" """
if self.suffix and isinstance(mutagen_value, unicode) \ if self.suffix and isinstance(mutagen_value, six.text_type) \
and mutagen_value.endswith(self.suffix): and mutagen_value.endswith(self.suffix):
return mutagen_value[:-len(self.suffix)] return mutagen_value[:-len(self.suffix)]
else: else:
@ -472,17 +474,17 @@ class StorageStyle(object):
"""Convert the external Python value to a type that is suitable for """Convert the external Python value to a type that is suitable for
storing in a Mutagen file object. storing in a Mutagen file object.
""" """
if isinstance(value, float) and self.as_type is unicode: if isinstance(value, float) and self.as_type is six.text_type:
value = u'{0:.{1}f}'.format(value, self.float_places) value = u'{0:.{1}f}'.format(value, self.float_places)
value = self.as_type(value) value = self.as_type(value)
elif self.as_type is unicode: elif self.as_type is six.text_type:
if isinstance(value, bool): if isinstance(value, bool):
# Store bools as 1/0 instead of True/False. # Store bools as 1/0 instead of True/False.
value = unicode(int(bool(value))) value = six.text_type(int(bool(value)))
elif isinstance(value, bytes): elif isinstance(value, bytes):
value = value.decode('utf8', 'ignore') value = value.decode('utf8', 'ignore')
else: else:
value = unicode(value) value = six.text_type(value)
else: else:
value = self.as_type(value) value = self.as_type(value)
@ -592,7 +594,7 @@ class MP4StorageStyle(StorageStyle):
def serialize(self, value): def serialize(self, value):
value = super(MP4StorageStyle, self).serialize(value) value = super(MP4StorageStyle, self).serialize(value)
if self.key.startswith('----:') and isinstance(value, unicode): if self.key.startswith('----:') and isinstance(value, six.text_type):
value = value.encode('utf8') value = value.encode('utf8')
return value return value
@ -807,7 +809,7 @@ class MP3SlashPackStorageStyle(MP3StorageStyle):
def _fetch_unpacked(self, mutagen_file): def _fetch_unpacked(self, mutagen_file):
data = self.fetch(mutagen_file) data = self.fetch(mutagen_file)
if data: if data:
items = unicode(data).split('/') items = six.text_type(data).split('/')
else: else:
items = [] items = []
packing_length = 2 packing_length = 2
@ -823,7 +825,7 @@ class MP3SlashPackStorageStyle(MP3StorageStyle):
items[0] = '' items[0] = ''
if items[1] is None: if items[1] is None:
items.pop() # Do not store last value items.pop() # Do not store last value
self.store(mutagen_file, '/'.join(map(unicode, items))) self.store(mutagen_file, '/'.join(map(six.text_type, items)))
def delete(self, mutagen_file): def delete(self, mutagen_file):
if self.pack_pos == 0: if self.pack_pos == 0:
@ -1074,7 +1076,7 @@ class MediaField(object):
getting this property. getting this property.
""" """
self.out_type = kwargs.get('out_type', unicode) self.out_type = kwargs.get('out_type', six.text_type)
self._styles = styles self._styles = styles
def styles(self, mutagen_file): def styles(self, mutagen_file):
@ -1113,7 +1115,7 @@ class MediaField(object):
return 0.0 return 0.0
elif self.out_type == bool: elif self.out_type == bool:
return False return False
elif self.out_type == unicode: elif self.out_type == six.text_type:
return u'' return u''
@ -1195,8 +1197,8 @@ class DateField(MediaField):
# Get the underlying data and split on hyphens and slashes. # Get the underlying data and split on hyphens and slashes.
datestring = super(DateField, self).__get__(mediafile, None) datestring = super(DateField, self).__get__(mediafile, None)
if isinstance(datestring, basestring): if isinstance(datestring, basestring):
datestring = re.sub(r'[Tt ].*$', '', unicode(datestring)) datestring = re.sub(r'[Tt ].*$', '', six.text_type(datestring))
items = re.split('[-/]', unicode(datestring)) items = re.split('[-/]', six.text_type(datestring))
else: else:
items = [] items = []
@ -1233,7 +1235,7 @@ class DateField(MediaField):
date.append(u'{0:02d}'.format(int(month))) date.append(u'{0:02d}'.format(int(month)))
if month and day: if month and day:
date.append(u'{0:02d}'.format(int(day))) date.append(u'{0:02d}'.format(int(day)))
date = map(unicode, date) date = map(six.text_type, date)
super(DateField, self).__set__(mediafile, u'-'.join(date)) super(DateField, self).__set__(mediafile, u'-'.join(date))
if hasattr(self, '_year_field'): if hasattr(self, '_year_field'):
@ -1360,7 +1362,7 @@ class MediaFile(object):
try: try:
self.mgfile = mutagen.File(path) self.mgfile = mutagen.File(path)
except unreadable_exc as exc: except unreadable_exc as exc:
log.debug(u'header parsing failed: {0}', unicode(exc)) log.debug(u'header parsing failed: {0}', six.text_type(exc))
raise UnreadableFileError(path) raise UnreadableFileError(path)
except IOError as exc: except IOError as exc:
if type(exc) == IOError: if type(exc) == IOError:

View file

@ -42,6 +42,7 @@ from beets import config
from beets.util import confit, as_string from beets.util import confit, as_string
from beets.autotag import mb from beets.autotag import mb
from beets.dbcore import query as db_query from beets.dbcore import query as db_query
import six
# On Windows platforms, use colorama to support "ANSI" terminal colors. # On Windows platforms, use colorama to support "ANSI" terminal colors.
if sys.platform == 'win32': if sys.platform == 'win32':
@ -140,7 +141,7 @@ def print_(*strings, **kwargs):
""" """
end = kwargs.get('end') end = kwargs.get('end')
if not strings or isinstance(strings[0], unicode): if not strings or isinstance(strings[0], six.text_type):
txt = u' '.join(strings) txt = u' '.join(strings)
txt += u'\n' if end is None else end txt += u'\n' if end is None else end
else: else:
@ -148,7 +149,7 @@ def print_(*strings, **kwargs):
txt += b'\n' if end is None else end txt += b'\n' if end is None else end
# Always send bytes to the stdout stream. # Always send bytes to the stdout stream.
if isinstance(txt, unicode): if isinstance(txt, six.text_type):
txt = txt.encode(_out_encoding(), 'replace') txt = txt.encode(_out_encoding(), 'replace')
sys.stdout.write(txt) sys.stdout.write(txt)
@ -298,11 +299,11 @@ def input_options(options, require=False, prompt=None, fallback_prompt=None,
prompt_part_lengths = [] prompt_part_lengths = []
if numrange: if numrange:
if isinstance(default, int): if isinstance(default, int):
default_name = unicode(default) default_name = six.text_type(default)
default_name = colorize('action_default', default_name) default_name = colorize('action_default', default_name)
tmpl = '# selection (default %s)' tmpl = '# selection (default %s)'
prompt_parts.append(tmpl % default_name) prompt_parts.append(tmpl % default_name)
prompt_part_lengths.append(len(tmpl % unicode(default))) prompt_part_lengths.append(len(tmpl % six.text_type(default)))
else: else:
prompt_parts.append('# selection') prompt_parts.append('# selection')
prompt_part_lengths.append(len(prompt_parts[-1])) prompt_part_lengths.append(len(prompt_parts[-1]))
@ -522,7 +523,8 @@ def colorize(color_name, text):
if config['ui']['color']: if config['ui']['color']:
global COLORS global COLORS
if not COLORS: if not COLORS:
COLORS = dict((name, config['ui']['colors'][name].get(unicode)) COLORS = dict((name,
config['ui']['colors'][name].get(six.text_type))
for name in COLOR_NAMES) for name in COLOR_NAMES)
# In case a 3rd party plugin is still passing the actual color ('red') # In case a 3rd party plugin is still passing the actual color ('red')
# instead of the abstract color name ('text_error') # instead of the abstract color name ('text_error')
@ -544,8 +546,8 @@ def _colordiff(a, b, highlight='text_highlight',
""" """
if not isinstance(a, basestring) or not isinstance(b, basestring): if not isinstance(a, basestring) or not isinstance(b, basestring):
# Non-strings: use ordinary equality. # Non-strings: use ordinary equality.
a = unicode(a) a = six.text_type(a)
b = unicode(b) b = six.text_type(b)
if a == b: if a == b:
return a, b return a, b
else: else:
@ -593,7 +595,7 @@ def colordiff(a, b, highlight='text_highlight'):
if config['ui']['color']: if config['ui']['color']:
return _colordiff(a, b, highlight) return _colordiff(a, b, highlight)
else: else:
return unicode(a), unicode(b) return six.text_type(a), six.text_type(b)
def get_path_formats(subview=None): def get_path_formats(subview=None):
@ -604,7 +606,7 @@ def get_path_formats(subview=None):
subview = subview or config['paths'] subview = subview or config['paths']
for query, view in subview.items(): for query, view in subview.items():
query = PF_KEY_QUERIES.get(query, query) # Expand common queries. query = PF_KEY_QUERIES.get(query, query) # Expand common queries.
path_formats.append((query, Template(view.get(unicode)))) path_formats.append((query, Template(view.get(six.text_type))))
return path_formats return path_formats

View file

@ -38,6 +38,7 @@ from beets import library
from beets import config from beets import config
from beets import logging from beets import logging
from beets.util.confit import _package_path from beets.util.confit import _package_path
import six
VARIOUS_ARTISTS = u'Various Artists' VARIOUS_ARTISTS = u'Various Artists'
PromptChoice = namedtuple('ExtraChoice', ['short', 'long', 'callback']) PromptChoice = namedtuple('ExtraChoice', ['short', 'long', 'callback'])
@ -163,7 +164,7 @@ def disambig_string(info):
else: else:
disambig.append(info.media) disambig.append(info.media)
if info.year: if info.year:
disambig.append(unicode(info.year)) disambig.append(six.text_type(info.year))
if info.country: if info.country:
disambig.append(info.country) disambig.append(info.country)
if info.label: if info.label:
@ -236,9 +237,9 @@ def show_change(cur_artist, cur_album, match):
if mediums > 1: if mediums > 1:
return u'{0}-{1}'.format(medium, medium_index) return u'{0}-{1}'.format(medium, medium_index)
else: else:
return unicode(medium_index) return six.text_type(medium_index)
else: else:
return unicode(index) return six.text_type(index)
# Identify the album in question. # Identify the album in question.
if cur_artist != match.info.artist or \ if cur_artist != match.info.artist or \

View file

@ -27,6 +27,7 @@ import subprocess
import platform import platform
import shlex import shlex
from beets.util import hidden from beets.util import hidden
import six
MAX_FILENAME_LENGTH = 200 MAX_FILENAME_LENGTH = 200
@ -65,14 +66,14 @@ class HumanReadableException(Exception):
def _reasonstr(self): def _reasonstr(self):
"""Get the reason as a string.""" """Get the reason as a string."""
if isinstance(self.reason, unicode): if isinstance(self.reason, six.text_type):
return self.reason return self.reason
elif isinstance(self.reason, bytes): elif isinstance(self.reason, bytes):
return self.reason.decode('utf8', 'ignore') return self.reason.decode('utf8', 'ignore')
elif hasattr(self.reason, 'strerror'): # i.e., EnvironmentError elif hasattr(self.reason, 'strerror'): # i.e., EnvironmentError
return self.reason.strerror return self.reason.strerror
else: else:
return u'"{0}"'.format(unicode(self.reason)) return u'"{0}"'.format(six.text_type(self.reason))
def get_message(self): def get_message(self):
"""Create the human-readable description of the error, sans """Create the human-readable description of the error, sans
@ -346,11 +347,11 @@ def displayable_path(path, separator=u'; '):
""" """
if isinstance(path, (list, tuple)): if isinstance(path, (list, tuple)):
return separator.join(displayable_path(p) for p in path) return separator.join(displayable_path(p) for p in path)
elif isinstance(path, unicode): elif isinstance(path, six.text_type):
return path return path
elif not isinstance(path, bytes): elif not isinstance(path, bytes):
# A non-string object: just get its unicode representation. # A non-string object: just get its unicode representation.
return unicode(path) return six.text_type(path)
try: try:
return path.decode(_fsencoding(), 'ignore') return path.decode(_fsencoding(), 'ignore')
@ -369,7 +370,7 @@ def syspath(path, prefix=True):
if os.path.__name__ != 'ntpath': if os.path.__name__ != 'ntpath':
return path return path
if not isinstance(path, unicode): if not isinstance(path, six.text_type):
# Beets currently represents Windows paths internally with UTF-8 # Beets currently represents Windows paths internally with UTF-8
# arbitrarily. But earlier versions used MBCS because it is # arbitrarily. But earlier versions used MBCS because it is
# reported as the FS encoding by Windows. Try both. # reported as the FS encoding by Windows. Try both.
@ -639,7 +640,7 @@ def as_string(value):
elif isinstance(value, bytes): elif isinstance(value, bytes):
return value.decode('utf8', 'ignore') return value.decode('utf8', 'ignore')
else: else:
return unicode(value) return six.text_type(value)
def plurality(objs): def plurality(objs):
@ -765,7 +766,7 @@ def shlex_split(s):
# Shlex works fine. # Shlex works fine.
return shlex.split(s) return shlex.split(s)
elif isinstance(s, unicode): elif isinstance(s, six.text_type):
# Work around a Python bug. # Work around a Python bug.
# http://bugs.python.org/issue6988 # http://bugs.python.org/issue6988
bs = s.encode('utf8') bs = s.encode('utf8')
@ -801,7 +802,7 @@ def _windows_long_path_name(short_path):
"""Use Windows' `GetLongPathNameW` via ctypes to get the canonical, """Use Windows' `GetLongPathNameW` via ctypes to get the canonical,
long path given a short filename. long path given a short filename.
""" """
if not isinstance(short_path, unicode): if not isinstance(short_path, six.text_type):
short_path = short_path.decode(_fsencoding()) short_path = short_path.decode(_fsencoding())
import ctypes import ctypes

View file

@ -25,6 +25,7 @@ import yaml
import collections import collections
import re import re
from collections import OrderedDict from collections import OrderedDict
import six
UNIX_DIR_VAR = 'XDG_CONFIG_HOME' UNIX_DIR_VAR = 'XDG_CONFIG_HOME'
UNIX_DIR_FALLBACK = '~/.config' UNIX_DIR_FALLBACK = '~/.config'
@ -44,7 +45,7 @@ REDACTED_TOMBSTONE = 'REDACTED'
# Utilities. # Utilities.
PY3 = sys.version_info[0] == 3 PY3 = sys.version_info[0] == 3
STRING = str if PY3 else unicode # noqa STRING = str if PY3 else six.text_type # noqa
BASESTRING = str if PY3 else basestring # noqa BASESTRING = str if PY3 else basestring # noqa
NUMERIC_TYPES = (int, float) if PY3 else (int, float, long) # noqa NUMERIC_TYPES = (int, float) if PY3 else (int, float, long) # noqa

View file

@ -35,6 +35,7 @@ import dis
import types import types
from .confit import NUMERIC_TYPES from .confit import NUMERIC_TYPES
import six
SYMBOL_DELIM = u'$' SYMBOL_DELIM = u'$'
FUNC_DELIM = u'%' FUNC_DELIM = u'%'
@ -190,8 +191,8 @@ class Call(object):
except Exception as exc: except Exception as exc:
# Function raised exception! Maybe inlining the name of # Function raised exception! Maybe inlining the name of
# the exception will help debug. # the exception will help debug.
return u'<%s>' % unicode(exc) return u'<%s>' % six.text_type(exc)
return unicode(out) return six.text_type(out)
else: else:
return self.original return self.original
@ -246,7 +247,7 @@ class Expression(object):
out.append(part) out.append(part)
else: else:
out.append(part.evaluate(env)) out.append(part.evaluate(env))
return u''.join(map(unicode, out)) return u''.join(map(six.text_type, out))
def translate(self): def translate(self):
"""Compile the expression to a list of Python AST expressions, a """Compile the expression to a list of Python AST expressions, a
@ -563,7 +564,7 @@ if __name__ == '__main__':
import timeit import timeit
_tmpl = Template(u'foo $bar %baz{foozle $bar barzle} $bar') _tmpl = Template(u'foo $bar %baz{foozle $bar barzle} $bar')
_vars = {'bar': 'qux'} _vars = {'bar': 'qux'}
_funcs = {'baz': unicode.upper} _funcs = {'baz': six.text_type.upper}
interp_time = timeit.timeit('_tmpl.interpret(_vars, _funcs)', interp_time = timeit.timeit('_tmpl.interpret(_vars, _funcs)',
'from __main__ import _tmpl, _vars, _funcs', 'from __main__ import _tmpl, _vars, _funcs',
number=10000) number=10000)

View file

@ -20,6 +20,7 @@ import os
import stat import stat
import ctypes import ctypes
import sys import sys
import six
def _is_hidden_osx(path): def _is_hidden_osx(path):
@ -74,7 +75,7 @@ def is_hidden(path):
work out if a file is hidden. work out if a file is hidden.
""" """
# Convert the path to unicode if it is not already. # Convert the path to unicode if it is not already.
if not isinstance(path, unicode): if not isinstance(path, six.text_type):
path = path.decode('utf-8') path = path.decode('utf-8')
# Run platform specific functions depending on the platform # Run platform specific functions depending on the platform

View file

@ -27,6 +27,7 @@ import shlex
import os import os
import errno import errno
import sys import sys
import six
class BadFiles(BeetsPlugin): class BadFiles(BeetsPlugin):
@ -97,7 +98,7 @@ class BadFiles(BeetsPlugin):
if not checker: if not checker:
continue continue
path = item.path path = item.path
if not isinstance(path, unicode): if not isinstance(path, six.text_type):
path = item.path.decode(sys.getfilesystemencoding()) path = item.path.decode(sys.getfilesystemencoding())
status, errors, output = checker(path) status, errors, output = checker(path)
if status > 0: if status > 0:

View file

@ -18,6 +18,7 @@ from __future__ import division, absolute_import, print_function
import json import json
import re import re
import six
from datetime import datetime, timedelta from datetime import datetime, timedelta
from requests_oauthlib import OAuth1Session from requests_oauthlib import OAuth1Session
@ -42,15 +43,15 @@ class BeatportAPIError(Exception):
class BeatportObject(object): class BeatportObject(object):
def __init__(self, data): def __init__(self, data):
self.beatport_id = data['id'] self.beatport_id = data['id']
self.name = unicode(data['name']) self.name = six.text_type(data['name'])
if 'releaseDate' in data: if 'releaseDate' in data:
self.release_date = datetime.strptime(data['releaseDate'], self.release_date = datetime.strptime(data['releaseDate'],
'%Y-%m-%d') '%Y-%m-%d')
if 'artists' in data: if 'artists' in data:
self.artists = [(x['id'], unicode(x['name'])) self.artists = [(x['id'], six.text_type(x['name']))
for x in data['artists']] for x in data['artists']]
if 'genres' in data: if 'genres' in data:
self.genres = [unicode(x['name']) self.genres = [six.text_type(x['name'])
for x in data['genres']] for x in data['genres']]
@ -209,7 +210,7 @@ class BeatportRelease(BeatportObject):
) )
def __repr__(self): def __repr__(self):
return unicode(self).encode('utf8') return six.text_type(self).encode('utf8')
def __init__(self, data): def __init__(self, data):
BeatportObject.__init__(self, data) BeatportObject.__init__(self, data)
@ -231,14 +232,14 @@ class BeatportTrack(BeatportObject):
.format(artist_str, self.name, self.mix_name)) .format(artist_str, self.name, self.mix_name))
def __repr__(self): def __repr__(self):
return unicode(self).encode('utf8') return six.text_type(self).encode('utf8')
def __init__(self, data): def __init__(self, data):
BeatportObject.__init__(self, data) BeatportObject.__init__(self, data)
if 'title' in data: if 'title' in data:
self.title = unicode(data['title']) self.title = six.text_type(data['title'])
if 'mixName' in data: if 'mixName' in data:
self.mix_name = unicode(data['mixName']) self.mix_name = six.text_type(data['mixName'])
self.length = timedelta(milliseconds=data.get('lengthMs', 0) or 0) self.length = timedelta(milliseconds=data.get('lengthMs', 0) or 0)
if not self.length: if not self.length:
try: try:
@ -266,8 +267,8 @@ class BeatportPlugin(BeetsPlugin):
self.register_listener('import_begin', self.setup) self.register_listener('import_begin', self.setup)
def setup(self, session=None): def setup(self, session=None):
c_key = self.config['apikey'].get(unicode) c_key = self.config['apikey'].get(six.text_type)
c_secret = self.config['apisecret'].get(unicode) c_secret = self.config['apisecret'].get(six.text_type)
# Get the OAuth token from a file or log in. # Get the OAuth token from a file or log in.
try: try:

View file

@ -35,6 +35,7 @@ from beets.util import bluelet
from beets.library import Item from beets.library import Item
from beets import dbcore from beets import dbcore
from beets.mediafile import MediaFile from beets.mediafile import MediaFile
import six
PROTOCOL_VERSION = '0.13.0' PROTOCOL_VERSION = '0.13.0'
BUFSIZE = 1024 BUFSIZE = 1024
@ -305,12 +306,12 @@ class BaseServer(object):
playlist, playlistlength, and xfade. playlist, playlistlength, and xfade.
""" """
yield ( yield (
u'volume: ' + unicode(self.volume), u'volume: ' + six.text_type(self.volume),
u'repeat: ' + unicode(int(self.repeat)), u'repeat: ' + six.text_type(int(self.repeat)),
u'random: ' + unicode(int(self.random)), u'random: ' + six.text_type(int(self.random)),
u'playlist: ' + unicode(self.playlist_version), u'playlist: ' + six.text_type(self.playlist_version),
u'playlistlength: ' + unicode(len(self.playlist)), u'playlistlength: ' + six.text_type(len(self.playlist)),
u'xfade: ' + unicode(self.crossfade), u'xfade: ' + six.text_type(self.crossfade),
) )
if self.current_index == -1: if self.current_index == -1:
@ -323,8 +324,8 @@ class BaseServer(object):
if self.current_index != -1: # i.e., paused or playing if self.current_index != -1: # i.e., paused or playing
current_id = self._item_id(self.playlist[self.current_index]) current_id = self._item_id(self.playlist[self.current_index])
yield u'song: ' + unicode(self.current_index) yield u'song: ' + six.text_type(self.current_index)
yield u'songid: ' + unicode(current_id) yield u'songid: ' + six.text_type(current_id)
if self.error: if self.error:
yield u'error: ' + self.error yield u'error: ' + self.error
@ -468,8 +469,8 @@ class BaseServer(object):
Also a dummy implementation. Also a dummy implementation.
""" """
for idx, track in enumerate(self.playlist): for idx, track in enumerate(self.playlist):
yield u'cpos: ' + unicode(idx) yield u'cpos: ' + six.text_type(idx)
yield u'Id: ' + unicode(track.id) yield u'Id: ' + six.text_type(track.id)
def cmd_currentsong(self, conn): def cmd_currentsong(self, conn):
"""Sends information about the currently-playing song. """Sends information about the currently-playing song.
@ -573,7 +574,7 @@ class Connection(object):
lines = [lines] lines = [lines]
out = NEWLINE.join(lines) + NEWLINE out = NEWLINE.join(lines) + NEWLINE
log.debug('{}', out[:-1]) # Don't log trailing newline. log.debug('{}', out[:-1]) # Don't log trailing newline.
if isinstance(out, unicode): if isinstance(out, six.text_type):
out = out.encode('utf8') out = out.encode('utf8')
return self.sock.sendall(out) return self.sock.sendall(out)
@ -771,28 +772,28 @@ class Server(BaseServer):
def _item_info(self, item): def _item_info(self, item):
info_lines = [ info_lines = [
u'file: ' + item.destination(fragment=True), u'file: ' + item.destination(fragment=True),
u'Time: ' + unicode(int(item.length)), u'Time: ' + six.text_type(int(item.length)),
u'Title: ' + item.title, u'Title: ' + item.title,
u'Artist: ' + item.artist, u'Artist: ' + item.artist,
u'Album: ' + item.album, u'Album: ' + item.album,
u'Genre: ' + item.genre, u'Genre: ' + item.genre,
] ]
track = unicode(item.track) track = six.text_type(item.track)
if item.tracktotal: if item.tracktotal:
track += u'/' + unicode(item.tracktotal) track += u'/' + six.text_type(item.tracktotal)
info_lines.append(u'Track: ' + track) info_lines.append(u'Track: ' + track)
info_lines.append(u'Date: ' + unicode(item.year)) info_lines.append(u'Date: ' + six.text_type(item.year))
try: try:
pos = self._id_to_index(item.id) pos = self._id_to_index(item.id)
info_lines.append(u'Pos: ' + unicode(pos)) info_lines.append(u'Pos: ' + six.text_type(pos))
except ArgumentNotFoundError: except ArgumentNotFoundError:
# Don't include position if not in playlist. # Don't include position if not in playlist.
pass pass
info_lines.append(u'Id: ' + unicode(item.id)) info_lines.append(u'Id: ' + six.text_type(item.id))
return info_lines return info_lines
@ -917,7 +918,7 @@ class Server(BaseServer):
for item in self._all_items(self._resolve_path(path)): for item in self._all_items(self._resolve_path(path)):
self.playlist.append(item) self.playlist.append(item)
if send_id: if send_id:
yield u'Id: ' + unicode(item.id) yield u'Id: ' + six.text_type(item.id)
self.playlist_version += 1 self.playlist_version += 1
def cmd_add(self, conn, path): def cmd_add(self, conn, path):
@ -938,11 +939,11 @@ class Server(BaseServer):
if self.current_index > -1: if self.current_index > -1:
item = self.playlist[self.current_index] item = self.playlist[self.current_index]
yield u'bitrate: ' + unicode(item.bitrate / 1000) yield u'bitrate: ' + six.text_type(item.bitrate / 1000)
# Missing 'audio'. # Missing 'audio'.
(pos, total) = self.player.time() (pos, total) = self.player.time()
yield u'time: ' + unicode(pos) + u':' + unicode(total) yield u'time: ' + six.text_type(pos) + u':' + six.text_type(total)
# Also missing 'updating_db'. # Also missing 'updating_db'.
@ -957,13 +958,13 @@ class Server(BaseServer):
artists, albums, songs, totaltime = tx.query(statement)[0] artists, albums, songs, totaltime = tx.query(statement)[0]
yield ( yield (
u'artists: ' + unicode(artists), u'artists: ' + six.text_type(artists),
u'albums: ' + unicode(albums), u'albums: ' + six.text_type(albums),
u'songs: ' + unicode(songs), u'songs: ' + six.text_type(songs),
u'uptime: ' + unicode(int(time.time() - self.startup_time)), u'uptime: ' + six.text_type(int(time.time() - self.startup_time)),
u'playtime: ' + u'0', # Missing. u'playtime: ' + u'0', # Missing.
u'db_playtime: ' + unicode(int(totaltime)), u'db_playtime: ' + six.text_type(int(totaltime)),
u'db_update: ' + unicode(int(self.updated_time)), u'db_update: ' + six.text_type(int(self.updated_time)),
) )
# Searching. # Searching.
@ -1059,7 +1060,7 @@ class Server(BaseServer):
rows = tx.query(statement, subvals) rows = tx.query(statement, subvals)
for row in rows: for row in rows:
yield show_tag_canon + u': ' + unicode(row[0]) yield show_tag_canon + u': ' + six.text_type(row[0])
def cmd_count(self, conn, tag, value): def cmd_count(self, conn, tag, value):
"""Returns the number and total time of songs matching the """Returns the number and total time of songs matching the
@ -1071,8 +1072,8 @@ class Server(BaseServer):
for item in self.lib.items(dbcore.query.MatchQuery(key, value)): for item in self.lib.items(dbcore.query.MatchQuery(key, value)):
songs += 1 songs += 1
playtime += item.length playtime += item.length
yield u'songs: ' + unicode(songs) yield u'songs: ' + six.text_type(songs)
yield u'playtime: ' + unicode(int(playtime)) yield u'playtime: ' + six.text_type(int(playtime))
# "Outputs." Just a dummy implementation because we don't control # "Outputs." Just a dummy implementation because we don't control
# any outputs. # any outputs.
@ -1180,11 +1181,12 @@ class BPDPlugin(BeetsPlugin):
) )
def func(lib, opts, args): def func(lib, opts, args):
host = args.pop(0) if args else self.config['host'].get(unicode) host = self.config['host'].get(six.text_type)
host = args.pop(0) if args else host
port = args.pop(0) if args else self.config['port'].get(int) port = args.pop(0) if args else self.config['port'].get(int)
if args: if args:
raise beets.ui.UserError(u'too many arguments') raise beets.ui.UserError(u'too many arguments')
password = self.config['password'].get(unicode) password = self.config['password'].get(six.text_type)
volume = self.config['volume'].get(int) volume = self.config['volume'].get(int)
debug = opts.debug or False debug = opts.debug or False
self.start_bpd(lib, host, int(port), password, volume, debug) self.start_bpd(lib, host, int(port), password, volume, debug)

View file

@ -19,6 +19,7 @@ music player.
from __future__ import division, absolute_import, print_function from __future__ import division, absolute_import, print_function
import six
import sys import sys
import time import time
from six.moves import _thread from six.moves import _thread
@ -128,7 +129,7 @@ class GstPlayer(object):
path. path.
""" """
self.player.set_state(Gst.State.NULL) self.player.set_state(Gst.State.NULL)
if isinstance(path, unicode): if isinstance(path, six.text_type):
path = path.encode('utf8') path = path.encode('utf8')
uri = 'file://' + urllib.parse.quote(path) uri = 'file://' + urllib.parse.quote(path)
self.player.set_property("uri", uri) self.player.set_property("uri", uri)

View file

@ -26,6 +26,7 @@ from beets.util import confit
from beets.autotag import hooks from beets.autotag import hooks
import acoustid import acoustid
from collections import defaultdict from collections import defaultdict
import six
API_KEY = '1vOwZtEn' API_KEY = '1vOwZtEn'
SCORE_THRESH = 0.5 SCORE_THRESH = 0.5
@ -181,7 +182,7 @@ class AcoustidPlugin(plugins.BeetsPlugin):
def submit_cmd_func(lib, opts, args): def submit_cmd_func(lib, opts, args):
try: try:
apikey = config['acoustid']['apikey'].get(unicode) apikey = config['acoustid']['apikey'].get(six.text_type)
except confit.NotFoundError: except confit.NotFoundError:
raise ui.UserError(u'no Acoustid user API key provided') raise ui.UserError(u'no Acoustid user API key provided')
submit_items(self._log, apikey, lib.items(ui.decargs(args))) submit_items(self._log, apikey, lib.items(ui.decargs(args)))

View file

@ -29,6 +29,7 @@ from beets.plugins import BeetsPlugin
from beets.util.confit import ConfigTypeError from beets.util.confit import ConfigTypeError
from beets import art from beets import art
from beets.util.artresizer import ArtResizer from beets.util.artresizer import ArtResizer
import six
_fs_lock = threading.Lock() _fs_lock = threading.Lock()
_temp_files = [] # Keep track of temporary transcoded files for deletion. _temp_files = [] # Keep track of temporary transcoded files for deletion.
@ -55,7 +56,7 @@ def get_format(fmt=None):
"""Return the command template and the extension from the config. """Return the command template and the extension from the config.
""" """
if not fmt: if not fmt:
fmt = config['convert']['format'].get(unicode).lower() fmt = config['convert']['format'].get(six.text_type).lower()
fmt = ALIASES.get(fmt, fmt) fmt = ALIASES.get(fmt, fmt)
try: try:
@ -74,14 +75,14 @@ def get_format(fmt=None):
# Convenience and backwards-compatibility shortcuts. # Convenience and backwards-compatibility shortcuts.
keys = config['convert'].keys() keys = config['convert'].keys()
if 'command' in keys: if 'command' in keys:
command = config['convert']['command'].get(unicode) command = config['convert']['command'].get(six.text_type)
elif 'opts' in keys: elif 'opts' in keys:
# Undocumented option for backwards compatibility with < 1.3.1. # Undocumented option for backwards compatibility with < 1.3.1.
command = u'ffmpeg -i $source -y {0} $dest'.format( command = u'ffmpeg -i $source -y {0} $dest'.format(
config['convert']['opts'].get(unicode) config['convert']['opts'].get(six.text_type)
) )
if 'extension' in keys: if 'extension' in keys:
extension = config['convert']['extension'].get(unicode) extension = config['convert']['extension'].get(six.text_type)
return (command.encode('utf8'), extension.encode('utf8')) return (command.encode('utf8'), extension.encode('utf8'))
@ -389,7 +390,7 @@ class ConvertPlugin(BeetsPlugin):
path_formats = ui.get_path_formats() path_formats = ui.get_path_formats()
if not opts.format: if not opts.format:
opts.format = self.config['format'].get(unicode).lower() opts.format = self.config['format'].get(six.text_type).lower()
pretend = opts.pretend if opts.pretend is not None else \ pretend = opts.pretend if opts.pretend is not None else \
self.config['pretend'].get(bool) self.config['pretend'].get(bool)
@ -422,7 +423,7 @@ class ConvertPlugin(BeetsPlugin):
"""Transcode a file automatically after it is imported into the """Transcode a file automatically after it is imported into the
library. library.
""" """
fmt = self.config['format'].get(unicode).lower() fmt = self.config['format'].get(six.text_type).lower()
if should_transcode(item, fmt): if should_transcode(item, fmt):
command, ext = get_format() command, ext = get_format()

View file

@ -34,6 +34,7 @@ import time
import json import json
import socket import socket
import os import os
import six
# Silence spurious INFO log lines generated by urllib3. # Silence spurious INFO log lines generated by urllib3.
@ -66,8 +67,8 @@ class DiscogsPlugin(BeetsPlugin):
def setup(self, session=None): def setup(self, session=None):
"""Create the `discogs_client` field. Authenticate if necessary. """Create the `discogs_client` field. Authenticate if necessary.
""" """
c_key = self.config['apikey'].get(unicode) c_key = self.config['apikey'].get(six.text_type)
c_secret = self.config['apisecret'].get(unicode) c_secret = self.config['apisecret'].get(six.text_type)
# Get the OAuth token from a file or log in. # Get the OAuth token from a file or log in.
try: try:
@ -225,7 +226,7 @@ class DiscogsPlugin(BeetsPlugin):
result.data['formats'][0].get('descriptions', [])) or None result.data['formats'][0].get('descriptions', [])) or None
va = result.data['artists'][0]['name'].lower() == 'various' va = result.data['artists'][0]['name'].lower() == 'various'
if va: if va:
artist = config['va_name'].get(unicode) artist = config['va_name'].get(six.text_type)
year = result.data['year'] year = result.data['year']
label = result.data['labels'][0]['name'] label = result.data['labels'][0]['name']
mediums = len(set(t.medium for t in tracks)) mediums = len(set(t.medium for t in tracks))

View file

@ -23,6 +23,7 @@ from beets.plugins import BeetsPlugin
from beets.ui import decargs, print_, vararg_callback, Subcommand, UserError from beets.ui import decargs, print_, vararg_callback, Subcommand, UserError
from beets.util import command_output, displayable_path, subprocess from beets.util import command_output, displayable_path, subprocess
from beets.library import Item, Album from beets.library import Item, Album
import six
PLUGIN = 'duplicates' PLUGIN = 'duplicates'
@ -264,7 +265,7 @@ class DuplicatesPlugin(BeetsPlugin):
# between a bytes object and the empty Unicode # between a bytes object and the empty Unicode
# string ''. # string ''.
return v is not None and \ return v is not None and \
(v != '' if isinstance(v, unicode) else True) (v != '' if isinstance(v, six.text_type) else True)
fields = kind.all_keys() fields = kind.all_keys()
key = lambda x: sum(1 for f in fields if truthy(getattr(x, f))) key = lambda x: sum(1 for f in fields if truthy(getattr(x, f)))
else: else:

View file

@ -27,6 +27,7 @@ import subprocess
import yaml import yaml
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
import os import os
import six
# These "safe" types can avoid the format/parse cycle that most fields go # These "safe" types can avoid the format/parse cycle that most fields go
@ -82,7 +83,7 @@ def load(s):
# Convert all keys to strings. They started out as strings, # Convert all keys to strings. They started out as strings,
# but the user may have inadvertently messed this up. # but the user may have inadvertently messed this up.
out.append({unicode(k): v for k, v in d.items()}) out.append({six.text_type(k): v for k, v in d.items()})
except yaml.YAMLError as e: except yaml.YAMLError as e:
raise ParseError(u'invalid YAML: {}'.format(e)) raise ParseError(u'invalid YAML: {}'.format(e))
@ -141,7 +142,7 @@ def apply_(obj, data):
else: else:
# Either the field was stringified originally or the user changed # Either the field was stringified originally or the user changed
# it from a safe type to an unsafe one. Parse it as a string. # it from a safe type to an unsafe one. Parse it as a string.
obj.set_parse(key, unicode(value)) obj.set_parse(key, six.text_type(value))
class EditPlugin(plugins.BeetsPlugin): class EditPlugin(plugins.BeetsPlugin):

View file

@ -32,6 +32,7 @@ from beets import config
from beets.util.artresizer import ArtResizer from beets.util.artresizer import ArtResizer
from beets.util import confit from beets.util import confit
from beets.util import syspath, bytestring_path from beets.util import syspath, bytestring_path
import six
try: try:
import itunes import itunes
@ -696,7 +697,7 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
confit.String(pattern=self.PAT_PERCENT)])) confit.String(pattern=self.PAT_PERCENT)]))
self.margin_px = None self.margin_px = None
self.margin_percent = None self.margin_percent = None
if type(self.enforce_ratio) is unicode: if type(self.enforce_ratio) is six.text_type:
if self.enforce_ratio[-1] == u'%': if self.enforce_ratio[-1] == u'%':
self.margin_percent = float(self.enforce_ratio[:-1]) / 100 self.margin_percent = float(self.enforce_ratio[:-1]) / 100
elif self.enforce_ratio[-2:] == u'px': elif self.enforce_ratio[-2:] == u'px':

View file

@ -22,6 +22,7 @@ from beets import plugins
from beets.util import displayable_path from beets.util import displayable_path
import os import os
import re import re
import six
# Filename field extraction patterns. # Filename field extraction patterns.
@ -132,7 +133,7 @@ def apply_matches(d):
# Apply the title and track. # Apply the title and track.
for item in d: for item in d:
if bad_title(item.title): if bad_title(item.title):
item.title = unicode(d[item][title_field]) item.title = six.text_type(d[item][title_field])
if 'track' in d[item] and item.track == 0: if 'track' in d[item] and item.track == 0:
item.track = int(d[item]['track']) item.track = int(d[item]['track'])

View file

@ -22,6 +22,7 @@ import re
from beets import plugins from beets import plugins
from beets import ui from beets import ui
from beets.util import displayable_path from beets.util import displayable_path
import six
def split_on_feat(artist): def split_on_feat(artist):
@ -137,7 +138,7 @@ class FtInTitlePlugin(plugins.BeetsPlugin):
# Only update the title if it does not already contain a featured # Only update the title if it does not already contain a featured
# artist and if we do not drop featuring information. # artist and if we do not drop featuring information.
if not drop_feat and not contains_feat(item.title): if not drop_feat and not contains_feat(item.title):
feat_format = self.config['format'].get(unicode) feat_format = self.config['format'].get(six.text_type)
new_format = feat_format.format(feat_part) new_format = feat_format.format(feat_part)
new_title = u"{0} {1}".format(item.title, new_format) new_title = u"{0} {1}".format(item.title, new_format)
self._log.info(u'title: {0} -> {1}', item.title, new_title) self._log.info(u'title: {0} -> {1}', item.title, new_title)

View file

@ -21,6 +21,7 @@ import subprocess
from beets.plugins import BeetsPlugin from beets.plugins import BeetsPlugin
from beets.ui import _arg_encoding from beets.ui import _arg_encoding
from beets.util import shlex_split from beets.util import shlex_split
import six
class CodingFormatter(string.Formatter): class CodingFormatter(string.Formatter):
@ -79,8 +80,8 @@ class HookPlugin(BeetsPlugin):
for hook_index in range(len(hooks)): for hook_index in range(len(hooks)):
hook = self.config['hooks'][hook_index] hook = self.config['hooks'][hook_index]
hook_event = hook['event'].get(unicode) hook_event = hook['event'].get(six.text_type)
hook_command = hook['command'].get(unicode) hook_command = hook['command'].get(six.text_type)
self.create_and_register_hook(hook_event, hook_command) self.create_and_register_hook(hook_event, hook_command)

View file

@ -14,6 +14,7 @@
# included in all copies or substantial portions of the Software. # included in all copies or substantial portions of the Software.
from __future__ import division, absolute_import, print_function from __future__ import division, absolute_import, print_function
import six
"""Write paths of imported files in various formats to ease later import in a """Write paths of imported files in various formats to ease later import in a
music player. Also allow printing the new file locations to stdout in case music player. Also allow printing the new file locations to stdout in case
@ -119,7 +120,7 @@ class ImportFeedsPlugin(BeetsPlugin):
if 'm3u' in formats: if 'm3u' in formats:
m3u_basename = bytestring_path( m3u_basename = bytestring_path(
self.config['m3u_name'].get(unicode)) self.config['m3u_name'].get(six.text_type))
m3u_path = os.path.join(feedsdir, m3u_basename) m3u_path = os.path.join(feedsdir, m3u_basename)
_write_m3u(m3u_path, paths) _write_m3u(m3u_path, paths)

View file

@ -22,6 +22,7 @@ import itertools
from beets.plugins import BeetsPlugin from beets.plugins import BeetsPlugin
from beets import config from beets import config
import six
FUNC_NAME = u'__INLINE_FUNC__' FUNC_NAME = u'__INLINE_FUNC__'
@ -32,7 +33,7 @@ class InlineError(Exception):
def __init__(self, code, exc): def __init__(self, code, exc):
super(InlineError, self).__init__( super(InlineError, self).__init__(
(u"error in inline path field code:\n" (u"error in inline path field code:\n"
u"%s\n%s: %s") % (code, type(exc).__name__, unicode(exc)) u"%s\n%s: %s") % (code, type(exc).__name__, six.text_type(exc))
) )
@ -64,14 +65,14 @@ class InlinePlugin(BeetsPlugin):
for key, view in itertools.chain(config['item_fields'].items(), for key, view in itertools.chain(config['item_fields'].items(),
config['pathfields'].items()): config['pathfields'].items()):
self._log.debug(u'adding item field {0}', key) self._log.debug(u'adding item field {0}', key)
func = self.compile_inline(view.get(unicode), False) func = self.compile_inline(view.get(six.text_type), False)
if func is not None: if func is not None:
self.template_fields[key] = func self.template_fields[key] = func
# Album fields. # Album fields.
for key, view in config['album_fields'].items(): for key, view in config['album_fields'].items():
self._log.debug(u'adding album field {0}', key) self._log.debug(u'adding album field {0}', key)
func = self.compile_inline(view.get(unicode), True) func = self.compile_inline(view.get(six.text_type), True)
if func is not None: if func is not None:
self.album_template_fields[key] = func self.album_template_fields[key] = func

View file

@ -23,6 +23,7 @@ import subprocess
from beets import ui from beets import ui
from beets import util from beets import util
from beets.plugins import BeetsPlugin from beets.plugins import BeetsPlugin
import six
class KeyFinderPlugin(BeetsPlugin): class KeyFinderPlugin(BeetsPlugin):
@ -52,7 +53,7 @@ class KeyFinderPlugin(BeetsPlugin):
def find_key(self, items, write=False): def find_key(self, items, write=False):
overwrite = self.config['overwrite'].get(bool) overwrite = self.config['overwrite'].get(bool)
bin = util.bytestring_path(self.config['bin'].get(unicode)) bin = util.bytestring_path(self.config['bin'].get(six.text_type))
for item in items: for item in items:
if item['initial_key'] and not overwrite: if item['initial_key'] and not overwrite:

View file

@ -14,6 +14,7 @@
# included in all copies or substantial portions of the Software. # included in all copies or substantial portions of the Software.
from __future__ import division, absolute_import, print_function from __future__ import division, absolute_import, print_function
import six
"""Gets genres for imported music based on Last.fm tags. """Gets genres for imported music based on Last.fm tags.
@ -71,7 +72,7 @@ def flatten_tree(elem, path, branches):
for sub in elem: for sub in elem:
flatten_tree(sub, path, branches) flatten_tree(sub, path, branches)
else: else:
branches.append(path + [unicode(elem)]) branches.append(path + [six.text_type(elem)])
def find_parents(candidate, branches): def find_parents(candidate, branches):
@ -186,7 +187,7 @@ class LastGenrePlugin(plugins.BeetsPlugin):
# the original tags list # the original tags list
tags = [x.title() for x in tags if self._is_allowed(x)] tags = [x.title() for x in tags if self._is_allowed(x)]
return self.config['separator'].get(unicode).join( return self.config['separator'].get(six.text_type).join(
tags[:self.config['count'].get(int)] tags[:self.config['count'].get(int)]
) )
@ -221,7 +222,8 @@ class LastGenrePlugin(plugins.BeetsPlugin):
if any(not s for s in args): if any(not s for s in args):
return None return None
key = u'{0}.{1}'.format(entity, u'-'.join(unicode(a) for a in args)) key = u'{0}.{1}'.format(entity,
u'-'.join(six.text_type(a) for a in args))
if key in self._genre_cache: if key in self._genre_cache:
return self._genre_cache[key] return self._genre_cache[key]
else: else:
@ -297,7 +299,7 @@ class LastGenrePlugin(plugins.BeetsPlugin):
result = None result = None
if isinstance(obj, library.Item): if isinstance(obj, library.Item):
result = self.fetch_artist_genre(obj) result = self.fetch_artist_genre(obj)
elif obj.albumartist != config['va_name'].get(unicode): elif obj.albumartist != config['va_name'].get(six.text_type):
result = self.fetch_album_artist_genre(obj) result = self.fetch_album_artist_genre(obj)
else: else:
# For "Various Artists", pick the most popular track genre. # For "Various Artists", pick the most popular track genre.

View file

@ -23,6 +23,7 @@ from beets import dbcore
from beets import config from beets import config
from beets import plugins from beets import plugins
from beets.dbcore import types from beets.dbcore import types
import six
API_URL = 'http://ws.audioscrobbler.com/2.0/' API_URL = 'http://ws.audioscrobbler.com/2.0/'
@ -111,7 +112,7 @@ class CustomUser(pylast.User):
def import_lastfm(lib, log): def import_lastfm(lib, log):
user = config['lastfm']['user'].get(unicode) user = config['lastfm']['user'].get(six.text_type)
per_page = config['lastimport']['per_page'].get(int) per_page = config['lastimport']['per_page'].get(int)
if not user: if not user:

View file

@ -27,6 +27,7 @@ import unicodedata
import warnings import warnings
from six.moves.html_parser import HTMLParseError from six.moves.html_parser import HTMLParseError
from six.moves import urllib from six.moves import urllib
import six
try: try:
from bs4 import SoupStrainer, BeautifulSoup from bs4 import SoupStrainer, BeautifulSoup
@ -177,7 +178,7 @@ class Backend(object):
@staticmethod @staticmethod
def _encode(s): def _encode(s):
"""Encode the string for inclusion in a URL""" """Encode the string for inclusion in a URL"""
if isinstance(s, unicode): if isinstance(s, six.text_type):
for char, repl in URL_CHARACTERS.items(): for char, repl in URL_CHARACTERS.items():
s = s.replace(char, repl) s = s.replace(char, repl)
s = s.encode('utf8', 'ignore') s = s.encode('utf8', 'ignore')
@ -250,7 +251,7 @@ class Genius(Backend):
"""Fetch lyrics from Genius via genius-api.""" """Fetch lyrics from Genius via genius-api."""
def __init__(self, config, log): def __init__(self, config, log):
super(Genius, self).__init__(config, log) super(Genius, self).__init__(config, log)
self.api_key = config['genius_api_key'].get(unicode) self.api_key = config['genius_api_key'].get(six.text_type)
self.headers = {'Authorization': "Bearer %s" % self.api_key} self.headers = {'Authorization': "Bearer %s" % self.api_key}
def search_genius(self, artist, title): def search_genius(self, artist, title):
@ -461,8 +462,8 @@ class Google(Backend):
"""Fetch lyrics from Google search results.""" """Fetch lyrics from Google search results."""
def __init__(self, config, log): def __init__(self, config, log):
super(Google, self).__init__(config, log) super(Google, self).__init__(config, log)
self.api_key = config['google_API_key'].get(unicode) self.api_key = config['google_API_key'].get(six.text_type)
self.engine_id = config['google_engine_ID'].get(unicode) self.engine_id = config['google_engine_ID'].get(six.text_type)
def is_lyrics(self, text, artist=None): def is_lyrics(self, text, artist=None):
"""Determine whether the text seems to be valid lyrics. """Determine whether the text seems to be valid lyrics.
@ -503,7 +504,7 @@ class Google(Backend):
try: try:
text = unicodedata.normalize('NFKD', text).encode('ascii', text = unicodedata.normalize('NFKD', text).encode('ascii',
'ignore') 'ignore')
text = unicode(re.sub('[-\s]+', ' ', text.decode('utf-8'))) text = six.text_type(re.sub('[-\s]+', ' ', text.decode('utf-8')))
except UnicodeDecodeError: except UnicodeDecodeError:
self._log.exception(u"Failing to normalize '{0}'", text) self._log.exception(u"Failing to normalize '{0}'", text)
return text return text

View file

@ -22,6 +22,7 @@ from beets import config
import musicbrainzngs import musicbrainzngs
import re import re
import six
SUBMISSION_CHUNK_SIZE = 200 SUBMISSION_CHUNK_SIZE = 200
UUID_REGEX = r'^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$' UUID_REGEX = r'^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$'
@ -57,8 +58,8 @@ class MusicBrainzCollectionPlugin(BeetsPlugin):
super(MusicBrainzCollectionPlugin, self).__init__() super(MusicBrainzCollectionPlugin, self).__init__()
config['musicbrainz']['pass'].redact = True config['musicbrainz']['pass'].redact = True
musicbrainzngs.auth( musicbrainzngs.auth(
config['musicbrainz']['user'].get(unicode), config['musicbrainz']['user'].get(six.text_type),
config['musicbrainz']['pass'].get(unicode), config['musicbrainz']['pass'].get(six.text_type),
) )
self.config.add({'auto': False}) self.config.add({'auto': False})
if self.config['auto']: if self.config['auto']:

View file

@ -27,6 +27,7 @@ from beets import plugins
from beets import library from beets import library
from beets.util import displayable_path from beets.util import displayable_path
from beets.dbcore import types from beets.dbcore import types
import six
# If we lose the connection, how many times do we want to retry and how # If we lose the connection, how many times do we want to retry and how
# much time should we wait between retries? # much time should we wait between retries?
@ -49,7 +50,7 @@ def is_url(path):
# see http://www.tarmack.eu/code/mpdunicode.py for the general idea # see http://www.tarmack.eu/code/mpdunicode.py for the general idea
class MPDClient(mpd.MPDClient): class MPDClient(mpd.MPDClient):
def _write_command(self, command, args=[]): def _write_command(self, command, args=[]):
args = [unicode(arg).encode('utf-8') for arg in args] args = [six.text_type(arg).encode('utf-8') for arg in args]
super(MPDClient, self)._write_command(command, args) super(MPDClient, self)._write_command(command, args)
def _read_line(self): def _read_line(self):
@ -64,14 +65,14 @@ class MPDClientWrapper(object):
self._log = log self._log = log
self.music_directory = ( self.music_directory = (
mpd_config['music_directory'].get(unicode)) mpd_config['music_directory'].get(six.text_type))
self.client = MPDClient() self.client = MPDClient()
def connect(self): def connect(self):
"""Connect to the MPD. """Connect to the MPD.
""" """
host = mpd_config['host'].get(unicode) host = mpd_config['host'].get(six.text_type)
port = mpd_config['port'].get(int) port = mpd_config['port'].get(int)
if host[0] in ['/', '~']: if host[0] in ['/', '~']:
@ -83,7 +84,7 @@ class MPDClientWrapper(object):
except socket.error as e: except socket.error as e:
raise ui.UserError(u'could not connect to MPD: {0}'.format(e)) raise ui.UserError(u'could not connect to MPD: {0}'.format(e))
password = mpd_config['password'].get(unicode) password = mpd_config['password'].get(six.text_type)
if password: if password:
try: try:
self.client.password(password) self.client.password(password)

View file

@ -27,6 +27,7 @@ from beets.plugins import BeetsPlugin
import os import os
import socket import socket
from beets import config from beets import config
import six
# No need to introduce a dependency on an MPD library for such a # No need to introduce a dependency on an MPD library for such a
@ -86,9 +87,9 @@ class MPDUpdatePlugin(BeetsPlugin):
def update(self, lib): def update(self, lib):
self.update_mpd( self.update_mpd(
config['mpd']['host'].get(unicode), config['mpd']['host'].get(six.text_type),
config['mpd']['port'].get(int), config['mpd']['port'].get(int),
config['mpd']['password'].get(unicode), config['mpd']['password'].get(six.text_type),
) )
def update_mpd(self, host='localhost', port=6600, password=None): def update_mpd(self, host='localhost', port=6600, password=None):
@ -101,7 +102,7 @@ class MPDUpdatePlugin(BeetsPlugin):
s = BufferedSocket(host, port) s = BufferedSocket(host, port)
except socket.error as e: except socket.error as e:
self._log.warning(u'MPD connection failed: {0}', self._log.warning(u'MPD connection failed: {0}',
unicode(e.strerror)) six.text_type(e.strerror))
return return
resp = s.readline() resp = s.readline()

View file

@ -27,6 +27,7 @@ from beets import logging
from beets import ui from beets import ui
from beets.plugins import BeetsPlugin from beets.plugins import BeetsPlugin
from beets.util import syspath, command_output, displayable_path from beets.util import syspath, command_output, displayable_path
import six
# Utilities. # Utilities.
@ -102,7 +103,7 @@ class Bs1770gainBackend(Backend):
'method': 'replaygain', 'method': 'replaygain',
}) })
self.chunk_at = config['chunk_at'].as_number() self.chunk_at = config['chunk_at'].as_number()
self.method = b'--' + bytes(config['method'].get(unicode)) self.method = b'--' + bytes(config['method'].get(six.text_type))
cmd = b'bs1770gain' cmd = b'bs1770gain'
try: try:
@ -256,7 +257,7 @@ class CommandBackend(Backend):
'noclip': True, 'noclip': True,
}) })
self.command = config["command"].get(unicode) self.command = config["command"].get(six.text_type)
if self.command: if self.command:
# Explicit executable path. # Explicit executable path.
@ -809,7 +810,7 @@ class ReplayGainPlugin(BeetsPlugin):
}) })
self.overwrite = self.config['overwrite'].get(bool) self.overwrite = self.config['overwrite'].get(bool)
backend_name = self.config['backend'].get(unicode) backend_name = self.config['backend'].get(six.text_type)
if backend_name not in self.backends: if backend_name not in self.backends:
raise ui.UserError( raise ui.UserError(
u"Selected ReplayGain backend {0} is not supported. " u"Selected ReplayGain backend {0} is not supported. "

View file

@ -24,6 +24,7 @@ from collections import defaultdict
from beets.plugins import BeetsPlugin from beets.plugins import BeetsPlugin
from beets import ui from beets import ui
from beets import library from beets import library
import six
def rewriter(field, rules): def rewriter(field, rules):
@ -51,7 +52,7 @@ class RewritePlugin(BeetsPlugin):
# Gather all the rewrite rules for each field. # Gather all the rewrite rules for each field.
rules = defaultdict(list) rules = defaultdict(list)
for key, view in self.config.items(): for key, view in self.config.items():
value = view.get(unicode) value = view.get(six.text_type)
try: try:
fieldname, pattern = key.split(None, 1) fieldname, pattern = key.split(None, 1)
except ValueError: except ValueError:

View file

@ -9,6 +9,7 @@ from beets.plugins import BeetsPlugin
from beets.ui import decargs from beets.ui import decargs
from beets import ui from beets import ui
from requests.exceptions import HTTPError from requests.exceptions import HTTPError
import six
class SpotifyPlugin(BeetsPlugin): class SpotifyPlugin(BeetsPlugin):
@ -170,6 +171,6 @@ class SpotifyPlugin(BeetsPlugin):
else: else:
for item in ids: for item in ids:
print(unicode.encode(self.open_url + item)) print(six.text_type.encode(self.open_url + item))
else: else:
self._log.warn(u'No Spotify tracks found from beets query') self._log.warn(u'No Spotify tracks found from beets query')

View file

@ -19,6 +19,7 @@ from __future__ import division, absolute_import, print_function
import re import re
from beets.plugins import BeetsPlugin from beets.plugins import BeetsPlugin
import six
__author__ = 'baobab@heresiarch.info' __author__ = 'baobab@heresiarch.info'
__version__ = '1.1' __version__ = '1.1'
@ -81,7 +82,7 @@ class ThePlugin(BeetsPlugin):
if self.config['strip']: if self.config['strip']:
return r return r
else: else:
fmt = self.config['format'].get(unicode) fmt = self.config['format'].get(six.text_type)
return fmt.format(r, t.strip()).strip() return fmt.format(r, t.strip()).strip()
else: else:
return u'' return u''

View file

@ -35,6 +35,7 @@ from beets.plugins import BeetsPlugin
from beets.ui import Subcommand, decargs from beets.ui import Subcommand, decargs
from beets import util from beets import util
from beets.util.artresizer import ArtResizer, get_im_version, get_pil_version from beets.util.artresizer import ArtResizer, get_im_version, get_pil_version
import six
BASE_DIR = os.path.join(BaseDirectory.xdg_cache_home, "thumbnails") BASE_DIR = os.path.join(BaseDirectory.xdg_cache_home, "thumbnails")
@ -169,8 +170,9 @@ class ThumbnailsPlugin(BeetsPlugin):
"""Write required metadata to the thumbnail """Write required metadata to the thumbnail
See http://standards.freedesktop.org/thumbnail-spec/latest/x142.html See http://standards.freedesktop.org/thumbnail-spec/latest/x142.html
""" """
mtime = os.stat(album.artpath).st_mtime
metadata = {"Thumb::URI": self.get_uri(album.artpath), metadata = {"Thumb::URI": self.get_uri(album.artpath),
"Thumb::MTime": unicode(os.stat(album.artpath).st_mtime)} "Thumb::MTime": six.text_type(mtime)}
try: try:
self.write_metadata(image_path, metadata) self.write_metadata(image_path, metadata)
except Exception: except Exception:

View file

@ -25,6 +25,7 @@ from flask import g
from werkzeug.routing import BaseConverter, PathConverter from werkzeug.routing import BaseConverter, PathConverter
import os import os
import json import json
import six
# Utilities. # Utilities.
@ -321,7 +322,7 @@ class WebPlugin(BeetsPlugin):
} }
CORS(app) CORS(app)
# Start the web application. # Start the web application.
app.run(host=self.config['host'].get(unicode), app.run(host=self.config['host'].get(six.text_type),
port=self.config['port'].get(int), port=self.config['port'].get(int),
debug=opts.debug, threaded=True) debug=opts.debug, threaded=True)
cmd.func = func cmd.func = func

View file

@ -22,6 +22,7 @@ from beets.plugins import BeetsPlugin
from beets.mediafile import MediaFile from beets.mediafile import MediaFile
from beets.importer import action from beets.importer import action
from beets.util import confit from beets.util import confit
import six
__author__ = 'baobab@heresiarch.info' __author__ = 'baobab@heresiarch.info'
__version__ = '0.10' __version__ = '0.10'
@ -113,7 +114,7 @@ class ZeroPlugin(BeetsPlugin):
if patterns is True: if patterns is True:
return True return True
for p in patterns: for p in patterns:
if re.search(p, unicode(field), flags=re.IGNORECASE): if re.search(p, six.text_type(field), flags=re.IGNORECASE):
return True return True
return False return False

View file

@ -56,6 +56,7 @@ from beets import util
# TODO Move AutotagMock here # TODO Move AutotagMock here
from test import _common from test import _common
import six
class LogCapture(logging.Handler): class LogCapture(logging.Handler):
@ -65,7 +66,7 @@ class LogCapture(logging.Handler):
self.messages = [] self.messages = []
def emit(self, record): def emit(self, record):
self.messages.append(unicode(record.msg)) self.messages.append(six.text_type(record.msg))
@contextmanager @contextmanager
@ -121,7 +122,7 @@ def has_program(cmd, args=['--version']):
""" """
full_cmd = [cmd] + args full_cmd = [cmd] + args
for i, elem in enumerate(full_cmd): for i, elem in enumerate(full_cmd):
if isinstance(elem, unicode): if isinstance(elem, six.text_type):
full_cmd[i] = elem.encode(_arg_encoding()) full_cmd[i] = elem.encode(_arg_encoding())
try: try:
with open(os.devnull, 'wb') as devnull: with open(os.devnull, 'wb') as devnull:

View file

@ -14,6 +14,7 @@ from beets import config
from test._common import unittest from test._common import unittest
from test.helper import TestHelper, capture_stdout from test.helper import TestHelper, capture_stdout
from beets.library import Library from beets.library import Library
import six
class ConfigCommandTest(unittest.TestCase, TestHelper): class ConfigCommandTest(unittest.TestCase, TestHelper):
@ -114,8 +115,8 @@ class ConfigCommandTest(unittest.TestCase, TestHelper):
execlp.side_effect = OSError('here is problem') execlp.side_effect = OSError('here is problem')
self.run_command('config', '-e') self.run_command('config', '-e')
self.assertIn('Could not edit configuration', self.assertIn('Could not edit configuration',
unicode(user_error.exception)) six.text_type(user_error.exception))
self.assertIn('here is problem', unicode(user_error.exception)) self.assertIn('here is problem', six.text_type(user_error.exception))
def test_edit_invalid_config_file(self): def test_edit_invalid_config_file(self):
self.lib = Library(':memory:') self.lib = Library(':memory:')

View file

@ -26,6 +26,7 @@ from test import _common
from test._common import unittest from test._common import unittest
from beets import dbcore from beets import dbcore
from tempfile import mkstemp from tempfile import mkstemp
import six
# Fixture: concrete database and model classes. For migration tests, we # Fixture: concrete database and model classes. For migration tests, we
@ -350,7 +351,7 @@ class FormatTest(unittest.TestCase):
model = TestModel1() model = TestModel1()
model.other_field = u'caf\xe9'.encode('utf8') model.other_field = u'caf\xe9'.encode('utf8')
value = model.formatted().get('other_field') value = model.formatted().get('other_field')
self.assertTrue(isinstance(value, unicode)) self.assertTrue(isinstance(value, six.text_type))
self.assertEqual(value, u'caf\xe9') self.assertEqual(value, u'caf\xe9')
def test_format_unset_field(self): def test_format_unset_field(self):

View file

@ -38,6 +38,7 @@ from beets import config
from beets.mediafile import MediaFile from beets.mediafile import MediaFile
from beets.util import syspath, bytestring_path from beets.util import syspath, bytestring_path
from test.helper import TestHelper from test.helper import TestHelper
import six
# Shortcut to path normalization. # Shortcut to path normalization.
np = util.normpath np = util.normpath
@ -964,7 +965,7 @@ class PathStringTest(_common.TestCase):
def test_sanitize_path_returns_unicode(self): def test_sanitize_path_returns_unicode(self):
path = u'b\xe1r?' path = u'b\xe1r?'
new_path = util.sanitize_path(path) new_path = util.sanitize_path(path)
self.assertTrue(isinstance(new_path, unicode)) self.assertTrue(isinstance(new_path, six.text_type))
def test_unicode_artpath_becomes_bytestring(self): def test_unicode_artpath_becomes_bytestring(self):
alb = self.lib.add_album([self.i]) alb = self.lib.add_album([self.i])
@ -1051,7 +1052,7 @@ class TemplateTest(_common.LibTestCase):
album.tagada = u'togodo' album.tagada = u'togodo'
self.assertEqual(u"{0}".format(album), u"foö bar") self.assertEqual(u"{0}".format(album), u"foö bar")
self.assertEqual(u"{0:$tagada}".format(album), u"togodo") self.assertEqual(u"{0:$tagada}".format(album), u"togodo")
self.assertEqual(unicode(album), u"foö bar") self.assertEqual(six.text_type(album), u"foö bar")
self.assertEqual(bytes(album), b"fo\xc3\xb6 bar") self.assertEqual(bytes(album), b"fo\xc3\xb6 bar")
config['format_item'] = 'bar $foo' config['format_item'] = 'bar $foo'
@ -1174,7 +1175,8 @@ class LibraryFieldTypesTest(unittest.TestCase):
t = beets.library.DateType() t = beets.library.DateType()
# format # format
time_local = time.strftime(beets.config['time_format'].get(unicode), time_format = beets.config['time_format'].get(six.text_type)
time_local = time.strftime(time_format,
time.localtime(123456789)) time.localtime(123456789))
self.assertEqual(time_local, t.format(123456789)) self.assertEqual(time_local, t.format(123456789))
# parse # parse

View file

@ -29,6 +29,7 @@ from beetsplug import lyrics
from beets.library import Item from beets.library import Item
from beets.util import confit, bytestring_path from beets.util import confit, bytestring_path
from beets import logging from beets import logging
import six
log = logging.getLogger('beets.test_lyrics') log = logging.getLogger('beets.test_lyrics')
raw_backend = lyrics.Backend({}, log) raw_backend = lyrics.Backend({}, log)
@ -354,7 +355,7 @@ class LyricsGooglePluginTest(unittest.TestCase):
present in the title.""" present in the title."""
from bs4 import SoupStrainer, BeautifulSoup from bs4 import SoupStrainer, BeautifulSoup
s = self.source s = self.source
url = unicode(s['url'] + s['path']) url = six.text_type(s['url'] + s['path'])
html = raw_backend.fetch_url(url) html = raw_backend.fetch_url(url)
soup = BeautifulSoup(html, "html.parser", soup = BeautifulSoup(html, "html.parser",
parse_only=SoupStrainer('title')) parse_only=SoupStrainer('title'))

View file

@ -33,6 +33,7 @@ from beets.mediafile import MediaFile, MediaField, Image, \
from beets.library import Item from beets.library import Item
from beets.plugins import BeetsPlugin from beets.plugins import BeetsPlugin
from beets.util import bytestring_path from beets.util import bytestring_path
import six
class ArtTestMixin(object): class ArtTestMixin(object):
@ -353,13 +354,13 @@ class ExtendedFieldTestMixin(object):
with self.assertRaises(ValueError) as cm: with self.assertRaises(ValueError) as cm:
MediaFile.add_field('somekey', True) MediaFile.add_field('somekey', True)
self.assertIn(u'must be an instance of MediaField', self.assertIn(u'must be an instance of MediaField',
unicode(cm.exception)) six.text_type(cm.exception))
def test_overwrite_property(self): def test_overwrite_property(self):
with self.assertRaises(ValueError) as cm: with self.assertRaises(ValueError) as cm:
MediaFile.add_field('artist', MediaField()) MediaFile.add_field('artist', MediaField())
self.assertIn(u'property "artist" already exists', self.assertIn(u'property "artist" already exists',
unicode(cm.exception)) six.text_type(cm.exception))
class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin, class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,

View file

@ -26,6 +26,7 @@ from test.helper import TestHelper
from beets.util import bytestring_path from beets.util import bytestring_path
import beets.mediafile import beets.mediafile
import six
_sc = beets.mediafile._safe_cast _sc = beets.mediafile._safe_cast
@ -127,8 +128,8 @@ class InvalidValueToleranceTest(unittest.TestCase):
self.assertAlmostEqual(_sc(float, u'-1.234'), -1.234) self.assertAlmostEqual(_sc(float, u'-1.234'), -1.234)
def test_safe_cast_special_chars_to_unicode(self): def test_safe_cast_special_chars_to_unicode(self):
us = _sc(unicode, 'caf\xc3\xa9') us = _sc(six.text_type, 'caf\xc3\xa9')
self.assertTrue(isinstance(us, unicode)) self.assertTrue(isinstance(us, six.text_type))
self.assertTrue(us.startswith(u'caf')) self.assertTrue(us.startswith(u'caf'))
def test_safe_cast_float_with_no_numbers(self): def test_safe_cast_float_with_no_numbers(self):
@ -350,7 +351,7 @@ class ID3v23Test(unittest.TestCase, TestHelper):
mf.year = 2013 mf.year = 2013
mf.save() mf.save()
frame = mf.mgfile['TDRC'] frame = mf.mgfile['TDRC']
self.assertTrue('2013' in unicode(frame)) self.assertTrue('2013' in six.text_type(frame))
self.assertTrue('TYER' not in mf.mgfile) self.assertTrue('TYER' not in mf.mgfile)
finally: finally:
self._delete_test() self._delete_test()
@ -361,7 +362,7 @@ class ID3v23Test(unittest.TestCase, TestHelper):
mf.year = 2013 mf.year = 2013
mf.save() mf.save()
frame = mf.mgfile['TYER'] frame = mf.mgfile['TYER']
self.assertTrue('2013' in unicode(frame)) self.assertTrue('2013' in six.text_type(frame))
self.assertTrue('TDRC' not in mf.mgfile) self.assertTrue('TDRC' not in mf.mgfile)
finally: finally:
self._delete_test() self._delete_test()

View file

@ -33,6 +33,7 @@ from beets.dbcore.query import (NoneQuery, ParsingError,
from beets.library import Library, Item from beets.library import Library, Item
from beets import util from beets import util
import platform import platform
import six
class TestHelper(helper.TestHelper): class TestHelper(helper.TestHelper):
@ -301,12 +302,13 @@ class GetTest(DummyDataTestCase):
def test_invalid_query(self): def test_invalid_query(self):
with self.assertRaises(InvalidQueryArgumentTypeError) as raised: with self.assertRaises(InvalidQueryArgumentTypeError) as raised:
dbcore.query.NumericQuery('year', u'199a') dbcore.query.NumericQuery('year', u'199a')
self.assertIn(u'not an int', unicode(raised.exception)) self.assertIn(u'not an int', six.text_type(raised.exception))
with self.assertRaises(InvalidQueryArgumentTypeError) as raised: with self.assertRaises(InvalidQueryArgumentTypeError) as raised:
dbcore.query.RegexpQuery('year', u'199(') dbcore.query.RegexpQuery('year', u'199(')
self.assertIn(u'not a regular expression', unicode(raised.exception)) exception = six.text_type(raised.exception)
self.assertIn(u'unbalanced parenthesis', unicode(raised.exception)) self.assertIn(u'not a regular expression', exception)
self.assertIn(u'unbalanced parenthesis', exception)
self.assertIsInstance(raised.exception, ParsingError) self.assertIsInstance(raised.exception, ParsingError)

View file

@ -21,6 +21,7 @@ import warnings
from test._common import unittest from test._common import unittest
from beets.util import functemplate from beets.util import functemplate
import six
def _normexpr(expr): def _normexpr(expr):
@ -227,7 +228,7 @@ class EvalTest(unittest.TestCase):
u'baz': u'BaR', u'baz': u'BaR',
} }
functions = { functions = {
u'lower': unicode.lower, u'lower': six.text_type.lower,
u'len': len, u'len': len,
} }
return functemplate.Template(template).substitute(values, functions) return functemplate.Template(template).substitute(values, functions)

View file

@ -39,6 +39,7 @@ from beets import config
from beets import plugins from beets import plugins
from beets.util.confit import ConfigError from beets.util.confit import ConfigError
from beets import util from beets import util
import six
class ListTest(unittest.TestCase): class ListTest(unittest.TestCase):
@ -1257,15 +1258,15 @@ class CommonOptionsParserTest(unittest.TestCase, TestHelper):
config['format_item'].set('$foo') config['format_item'].set('$foo')
self.assertEqual(parser.parse_args([]), ({'path': None}, [])) self.assertEqual(parser.parse_args([]), ({'path': None}, []))
self.assertEqual(config['format_item'].get(unicode), u'$foo') self.assertEqual(config['format_item'].get(six.text_type), u'$foo')
self.assertEqual(parser.parse_args([u'-p']), self.assertEqual(parser.parse_args([u'-p']),
({'path': True, 'format': u'$path'}, [])) ({'path': True, 'format': u'$path'}, []))
self.assertEqual(parser.parse_args(['--path']), self.assertEqual(parser.parse_args(['--path']),
({'path': True, 'format': u'$path'}, [])) ({'path': True, 'format': u'$path'}, []))
self.assertEqual(config['format_item'].get(unicode), u'$path') self.assertEqual(config['format_item'].get(six.text_type), u'$path')
self.assertEqual(config['format_album'].get(unicode), u'$path') self.assertEqual(config['format_album'].get(six.text_type), u'$path')
def test_format_option(self): def test_format_option(self):
parser = ui.CommonOptionsParser() parser = ui.CommonOptionsParser()
@ -1274,15 +1275,15 @@ class CommonOptionsParserTest(unittest.TestCase, TestHelper):
config['format_item'].set('$foo') config['format_item'].set('$foo')
self.assertEqual(parser.parse_args([]), ({'format': None}, [])) self.assertEqual(parser.parse_args([]), ({'format': None}, []))
self.assertEqual(config['format_item'].get(unicode), u'$foo') self.assertEqual(config['format_item'].get(six.text_type), u'$foo')
self.assertEqual(parser.parse_args([u'-f', u'$bar']), self.assertEqual(parser.parse_args([u'-f', u'$bar']),
({'format': u'$bar'}, [])) ({'format': u'$bar'}, []))
self.assertEqual(parser.parse_args([u'--format', u'$baz']), self.assertEqual(parser.parse_args([u'--format', u'$baz']),
({'format': u'$baz'}, [])) ({'format': u'$baz'}, []))
self.assertEqual(config['format_item'].get(unicode), u'$baz') self.assertEqual(config['format_item'].get(six.text_type), u'$baz')
self.assertEqual(config['format_album'].get(unicode), u'$baz') self.assertEqual(config['format_album'].get(six.text_type), u'$baz')
def test_format_option_with_target(self): def test_format_option_with_target(self):
with self.assertRaises(KeyError): with self.assertRaises(KeyError):
@ -1297,8 +1298,8 @@ class CommonOptionsParserTest(unittest.TestCase, TestHelper):
self.assertEqual(parser.parse_args([u'-f', u'$bar']), self.assertEqual(parser.parse_args([u'-f', u'$bar']),
({'format': u'$bar'}, [])) ({'format': u'$bar'}, []))
self.assertEqual(config['format_item'].get(unicode), u'$bar') self.assertEqual(config['format_item'].get(six.text_type), u'$bar')
self.assertEqual(config['format_album'].get(unicode), u'$album') self.assertEqual(config['format_album'].get(six.text_type), u'$album')
def test_format_option_with_album(self): def test_format_option_with_album(self):
parser = ui.CommonOptionsParser() parser = ui.CommonOptionsParser()
@ -1309,15 +1310,15 @@ class CommonOptionsParserTest(unittest.TestCase, TestHelper):
config['format_album'].set('$album') config['format_album'].set('$album')
parser.parse_args([u'-f', u'$bar']) parser.parse_args([u'-f', u'$bar'])
self.assertEqual(config['format_item'].get(unicode), u'$bar') self.assertEqual(config['format_item'].get(six.text_type), u'$bar')
self.assertEqual(config['format_album'].get(unicode), u'$album') self.assertEqual(config['format_album'].get(six.text_type), u'$album')
parser.parse_args([u'-a', u'-f', u'$foo']) parser.parse_args([u'-a', u'-f', u'$foo'])
self.assertEqual(config['format_item'].get(unicode), u'$bar') self.assertEqual(config['format_item'].get(six.text_type), u'$bar')
self.assertEqual(config['format_album'].get(unicode), u'$foo') self.assertEqual(config['format_album'].get(six.text_type), u'$foo')
parser.parse_args([u'-f', u'$foo2', u'-a']) parser.parse_args([u'-f', u'$foo2', u'-a'])
self.assertEqual(config['format_album'].get(unicode), u'$foo2') self.assertEqual(config['format_album'].get(six.text_type), u'$foo2')
def test_add_all_common_options(self): def test_add_all_common_options(self):
parser = ui.CommonOptionsParser() parser = ui.CommonOptionsParser()

View file

@ -26,6 +26,7 @@ from test import test_importer
from beets.ui.commands import TerminalImportSession from beets.ui.commands import TerminalImportSession
from beets import importer from beets import importer
from beets import config from beets import config
import six
class TestTerminalImportSession(TerminalImportSession): class TestTerminalImportSession(TerminalImportSession):
@ -69,7 +70,7 @@ class TestTerminalImportSession(TerminalImportSession):
self.io.addinput(u'S') self.io.addinput(u'S')
elif isinstance(choice, int): elif isinstance(choice, int):
self.io.addinput(u'M') self.io.addinput(u'M')
self.io.addinput(unicode(choice)) self.io.addinput(six.text_type(choice))
self._add_choice_input() self._add_choice_input()
else: else:
raise Exception(u'Unknown choice %s' % choice) raise Exception(u'Unknown choice %s' % choice)

View file

@ -26,6 +26,7 @@ from mock import patch, Mock
from test._common import unittest from test._common import unittest
from test import _common from test import _common
from beets import util from beets import util
import six
class UtilTest(unittest.TestCase): class UtilTest(unittest.TestCase):
@ -122,7 +123,7 @@ class PathConversionTest(_common.TestCase):
with _common.platform_windows(): with _common.platform_windows():
path = os.path.join(u'a', u'b', u'c') path = os.path.join(u'a', u'b', u'c')
outpath = util.syspath(path) outpath = util.syspath(path)
self.assertTrue(isinstance(outpath, unicode)) self.assertTrue(isinstance(outpath, six.text_type))
self.assertTrue(outpath.startswith(u'\\\\?\\')) self.assertTrue(outpath.startswith(u'\\\\?\\'))
def test_syspath_windows_format_unc_path(self): def test_syspath_windows_format_unc_path(self):
@ -131,7 +132,7 @@ class PathConversionTest(_common.TestCase):
path = '\\\\server\\share\\file.mp3' path = '\\\\server\\share\\file.mp3'
with _common.platform_windows(): with _common.platform_windows():
outpath = util.syspath(path) outpath = util.syspath(path)
self.assertTrue(isinstance(outpath, unicode)) self.assertTrue(isinstance(outpath, six.text_type))
self.assertEqual(outpath, u'\\\\?\\UNC\\server\\share\\file.mp3') self.assertEqual(outpath, u'\\\\?\\UNC\\server\\share\\file.mp3')
def test_syspath_posix_unchanged(self): def test_syspath_posix_unchanged(self):