Merge pull request #2066 from beetbox/use-six

Use the six package to handle py2/p3 compatibility
This commit is contained in:
Adrian Sampson 2016-06-25 18:09:56 -07:00
commit 736ad66518
84 changed files with 479 additions and 369 deletions

View file

@ -25,6 +25,7 @@ from beets import config
from .hooks import AlbumInfo, TrackInfo, AlbumMatch, TrackMatch # noqa
from .match import tag_item, tag_album # noqa
from .match import Recommendation # noqa
import six
# Global logger.
log = logging.getLogger('beets')
@ -52,7 +53,7 @@ def apply_metadata(album_info, mapping):
"""Set the items' metadata to match an AlbumInfo object using a
mapping from Items to TrackInfo objects.
"""
for item, track_info in mapping.iteritems():
for item, track_info in six.iteritems(mapping):
# Album, artist, track count.
if track_info.artist:
item.artist = track_info.artist

View file

@ -27,6 +27,7 @@ from beets.util import as_string
from beets.autotag import mb
from jellyfish import levenshtein_distance
from unidecode import unidecode
import six
log = logging.getLogger('beets')
@ -205,8 +206,8 @@ def _string_dist_basic(str1, str2):
transliteration/lowering to ASCII characters. Normalized by string
length.
"""
assert isinstance(str1, unicode)
assert isinstance(str2, unicode)
assert isinstance(str1, six.text_type)
assert isinstance(str2, six.text_type)
str1 = as_string(unidecode(str1))
str2 = as_string(unidecode(str2))
str1 = re.sub(r'[^a-z0-9]', '', str1.lower())
@ -291,6 +292,7 @@ class LazyClassProperty(object):
@total_ordering
@six.python_2_unicode_compatible
class Distance(object):
"""Keeps track of multiple distance penalties. Provides a single
weighted distance for all penalties as well as a weighted distance
@ -326,7 +328,7 @@ class Distance(object):
"""Return the maximum distance penalty (normalization factor).
"""
dist_max = 0.0
for key, penalty in self._penalties.iteritems():
for key, penalty in six.iteritems(self._penalties):
dist_max += len(penalty) * self._weights[key]
return dist_max
@ -335,7 +337,7 @@ class Distance(object):
"""Return the raw (denormalized) distance.
"""
dist_raw = 0.0
for key, penalty in self._penalties.iteritems():
for key, penalty in six.iteritems(self._penalties):
dist_raw += sum(penalty) * self._weights[key]
return dist_raw
@ -377,7 +379,9 @@ class Distance(object):
def __rsub__(self, other):
return other - self.distance
def __unicode__(self):
# Behave like a string
def __str__(self):
return "{0:.2f}".format(self.distance)
# Behave like a dict.
@ -407,7 +411,7 @@ class Distance(object):
raise ValueError(
u'`dist` must be a Distance object, not {0}'.format(type(dist))
)
for key, penalties in dist._penalties.iteritems():
for key, penalties in six.iteritems(dist._penalties):
self._penalties.setdefault(key, []).extend(penalties)
# Adding components.

View file

@ -30,6 +30,7 @@ from beets.util import plurality
from beets.autotag import hooks
from beets.util.enumeration import OrderedEnum
from functools import reduce
import six
# Artist signals that indicate "various artists". These are used at the
# album level to determine whether a given release is likely a VA
@ -238,7 +239,7 @@ def distance(items, album_info, mapping):
# Tracks.
dist.tracks = {}
for item, track in mapping.iteritems():
for item, track in six.iteritems(mapping):
dist.tracks[track] = track_distance(item, track, album_info.va)
dist.add('tracks', dist.tracks[track].distance)
@ -312,10 +313,10 @@ def _recommendation(results):
keys = set(min_dist.keys())
if isinstance(results[0], hooks.AlbumMatch):
for track_dist in min_dist.tracks.values():
keys.update(track_dist.keys())
keys.update(list(track_dist.keys()))
max_rec_view = config['match']['max_rec']
for key in keys:
if key in max_rec_view.keys():
if key in list(max_rec_view.keys()):
max_rec = max_rec_view[key].as_choice({
'strong': Recommendation.strong,
'medium': Recommendation.medium,
@ -443,7 +444,7 @@ def tag_album(items, search_artist=None, search_album=None,
_add_candidate(items, candidates, info)
# Sort and get the recommendation.
candidates = sorted(candidates.itervalues())
candidates = sorted(six.itervalues(candidates))
rec = _recommendation(candidates)
return cur_artist, cur_album, candidates, rec
@ -471,16 +472,16 @@ def tag_item(item, search_artist=None, search_title=None,
candidates[track_info.track_id] = \
hooks.TrackMatch(dist, track_info)
# If this is a good match, then don't keep searching.
rec = _recommendation(sorted(candidates.itervalues()))
rec = _recommendation(sorted(six.itervalues(candidates)))
if rec == Recommendation.strong and \
not config['import']['timid']:
log.debug(u'Track ID match.')
return sorted(candidates.itervalues()), rec
return sorted(six.itervalues(candidates)), rec
# If we're searching by ID, don't proceed.
if search_ids:
if candidates:
return sorted(candidates.itervalues()), rec
return sorted(six.itervalues(candidates)), rec
else:
return [], Recommendation.none
@ -496,6 +497,6 @@ def tag_item(item, search_artist=None, search_title=None,
# Sort by distance and return with recommendation.
log.debug(u'Found {0} candidates.', len(candidates))
candidates = sorted(candidates.itervalues())
candidates = sorted(six.itervalues(candidates))
rec = _recommendation(candidates)
return candidates, rec

View file

@ -20,13 +20,14 @@ from __future__ import division, absolute_import, print_function
import musicbrainzngs
import re
import traceback
from urlparse import urljoin
from six.moves.urllib.parse import urljoin
from beets import logging
import beets.autotag.hooks
import beets
from beets import util
from beets import config
import six
VARIOUS_ARTISTS_ID = '89ad4ac3-39f7-470e-963a-56509c546377'
BASE_URL = 'http://musicbrainz.org/'
@ -69,7 +70,8 @@ def configure():
"""Set up the python-musicbrainz-ngs module according to settings
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(
config['musicbrainz']['ratelimit_interval'].as_number(),
config['musicbrainz']['ratelimit'].get(int),
@ -108,7 +110,7 @@ def _flatten_artist_credit(credit):
artist_sort_parts = []
artist_credit_parts = []
for el in credit:
if isinstance(el, basestring):
if isinstance(el, six.string_types):
# Join phrase.
artist_parts.append(el)
artist_credit_parts.append(el)
@ -260,7 +262,7 @@ def album_info(release):
)
info.va = info.artist_id == VARIOUS_ARTISTS_ID
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.releasegroup_id = release['release-group']['id']
info.country = release.get('country')
@ -329,10 +331,10 @@ def match_album(artist, album, tracks=None):
# Various Artists search.
criteria['arid'] = VARIOUS_ARTISTS_ID
if tracks is not None:
criteria['tracks'] = unicode(tracks)
criteria['tracks'] = six.text_type(tracks)
# Abort if we have no search terms.
if not any(criteria.itervalues()):
if not any(six.itervalues(criteria)):
return
try:
@ -358,7 +360,7 @@ def match_track(artist, title):
'recording': title.lower().strip(),
}
if not any(criteria.itervalues()):
if not any(six.itervalues(criteria)):
return
try:

View file

@ -29,6 +29,7 @@ import beets
from beets.util.functemplate import Template
from beets.dbcore import types
from .query import MatchQuery, NullSort, TrueQuery
import six
class FormattedMapping(collections.Mapping):
@ -69,7 +70,7 @@ class FormattedMapping(collections.Mapping):
value = value.decode('utf8', 'ignore')
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):
if sep:
value = value.replace(sep, sep_repl)
@ -176,9 +177,9 @@ class Model(object):
ordinary construction are bypassed.
"""
obj = cls(db)
for key, value in fixed_values.iteritems():
for key, value in six.iteritems(fixed_values):
obj._values_fixed[key] = cls._type(key).from_sql(value)
for key, value in flex_values.iteritems():
for key, value in six.iteritems(flex_values):
obj._values_flex[key] = cls._type(key).from_sql(value)
return obj
@ -452,7 +453,7 @@ class Model(object):
separators will be added to the template.
"""
# Perform substitution.
if isinstance(template, basestring):
if isinstance(template, six.string_types):
template = Template(template)
return template.substitute(self.formatted(for_path),
self._template_funcs())
@ -463,7 +464,7 @@ class Model(object):
def _parse(cls, key, string):
"""Parse a string as a value for the given key.
"""
if not isinstance(string, basestring):
if not isinstance(string, six.string_types):
raise TypeError(u"_parse() argument must be a string")
return cls._type(key).parse(string)

View file

@ -23,6 +23,10 @@ from beets import util
from datetime import datetime, timedelta
import unicodedata
from functools import reduce
import six
if not six.PY2:
buffer = memoryview # sqlite won't accept memoryview in python 2
class ParsingError(ValueError):
@ -229,7 +233,7 @@ class BooleanQuery(MatchQuery):
"""
def __init__(self, field, pattern, fast=True):
super(BooleanQuery, self).__init__(field, pattern, fast)
if isinstance(pattern, basestring):
if isinstance(pattern, six.string_types):
self.pattern = util.str2bool(pattern)
self.pattern = int(self.pattern)
@ -243,11 +247,11 @@ class BytesQuery(MatchQuery):
def __init__(self, field, pattern):
super(BytesQuery, self).__init__(field, pattern)
# Use a buffer representation of the pattern for SQLite
# Use a buffer/memoryview representation of the pattern for SQLite
# matching. This instructs SQLite to treat the blob as binary
# rather than encoded Unicode.
if isinstance(self.pattern, (unicode, bytes)):
if isinstance(self.pattern, unicode):
if isinstance(self.pattern, (six.text_type, bytes)):
if isinstance(self.pattern, six.text_type):
self.pattern = self.pattern.encode('utf8')
self.buf_pattern = buffer(self.pattern)
elif isinstance(self.pattern, buffer):
@ -302,7 +306,7 @@ class NumericQuery(FieldQuery):
if self.field not in item:
return False
value = item[self.field]
if isinstance(value, basestring):
if isinstance(value, six.string_types):
value = self._convert(value)
if self.point is not None:
@ -793,7 +797,7 @@ class FieldSort(Sort):
def key(item):
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()
return field_val

View file

@ -19,6 +19,10 @@ from __future__ import division, absolute_import, print_function
from . import query
from beets.util import str2bool
import six
if not six.PY2:
buffer = memoryview # sqlite won't accept memoryview in python 2
# Abstract base.
@ -37,7 +41,7 @@ class Type(object):
"""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 model is guaranteed to return a value of this type if the field
@ -63,7 +67,7 @@ class Type(object):
if isinstance(value, bytes):
value = value.decode('utf8', 'ignore')
return unicode(value)
return six.text_type(value)
def parse(self, string):
"""Parse a (possibly human-written) string and return the
@ -97,12 +101,12 @@ class Type(object):
https://docs.python.org/2/library/sqlite3.html#sqlite-and-python-types
Flexible fields have the type affinity `TEXT`. This means the
`sql_value` is either a `buffer` or a `unicode` object` and the
method must handle these in addition.
`sql_value` is either a `buffer`/`memoryview` or a `unicode` object`
and the method must handle these in addition.
"""
if isinstance(sql_value, buffer):
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)
else:
return self.normalize(sql_value)
@ -194,7 +198,7 @@ class Boolean(Type):
model_type = bool
def format(self, value):
return unicode(bool(value))
return six.text_type(bool(value))
def parse(self, string):
return str2bool(string)

View file

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

View file

@ -22,6 +22,7 @@ import sys
import unicodedata
import time
import re
import six
from unidecode import unidecode
from beets import logging
@ -34,6 +35,8 @@ from beets import dbcore
from beets.dbcore import types
import beets
if not six.PY2:
buffer = memoryview # sqlite won't accept memoryview in python 2
log = logging.getLogger('beets')
@ -123,14 +126,15 @@ class DateType(types.Float):
query = dbcore.query.DateQuery
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))
def parse(self, string):
try:
# Try a formatted date string.
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:
# Fall back to a plain timestamp number.
@ -152,13 +156,13 @@ class PathType(types.Type):
return normpath(bytestring_path(string))
def normalize(self, value):
if isinstance(value, unicode):
if isinstance(value, six.text_type):
# Paths stored internally as encoded bytes.
return bytestring_path(value)
elif isinstance(value, buffer):
# SQLite must store bytestings as buffers to avoid decoding.
# We unwrap buffers to bytes.
# SQLite must store bytestings as buffers/memoryview
# to avoid decoding. We unwrap buffers to bytes.
return bytes(value)
else:
@ -260,7 +264,7 @@ PF_KEY_DEFAULT = 'default'
# Exceptions.
@six.python_2_unicode_compatible
class FileOperationError(Exception):
"""Indicates an error when interacting with a file on disk.
Possibilities include an unsupported media type, a permissions
@ -274,35 +278,39 @@ class FileOperationError(Exception):
self.path = path
self.reason = reason
def __unicode__(self):
def text(self):
"""Get a string representing the error. Describes both the
underlying reason and the file path in question.
"""
return u'{0}: {1}'.format(
util.displayable_path(self.path),
unicode(self.reason)
six.text_type(self.reason)
)
def __str__(self):
return unicode(self).encode('utf8')
# define __str__ as text to avoid infinite loop on super() calls
# with @six.python_2_unicode_compatible
__str__ = text
@six.python_2_unicode_compatible
class ReadError(FileOperationError):
"""An error while reading a file (i.e. in `Item.read`).
"""
def __unicode__(self):
return u'error reading ' + super(ReadError, self).__unicode__()
def __str__(self):
return u'error reading ' + super(ReadError, self).text()
@six.python_2_unicode_compatible
class WriteError(FileOperationError):
"""An error while writing a file (i.e. in `Item.write`).
"""
def __unicode__(self):
return u'error writing ' + super(WriteError, self).__unicode__()
def __str__(self):
return u'error writing ' + super(WriteError, self).text()
# Item and Album model classes.
@six.python_2_unicode_compatible
class LibModel(dbcore.Model):
"""Shared concrete functionality for Items and Albums.
"""
@ -330,7 +338,7 @@ class LibModel(dbcore.Model):
def __format__(self, 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)
if isinstance(spec, bytes):
# if spec is a byte string then we must return a one as well
@ -339,9 +347,6 @@ class LibModel(dbcore.Model):
return result
def __str__(self):
return format(self).encode('utf8')
def __unicode__(self):
return format(self)
@ -516,7 +521,7 @@ class Item(LibModel):
"""
# Encode unicode paths and read buffers.
if key == 'path':
if isinstance(value, unicode):
if isinstance(value, six.text_type):
value = bytestring_path(value)
elif isinstance(value, buffer):
value = bytes(value)
@ -565,7 +570,7 @@ class Item(LibModel):
for key in self._media_fields:
value = getattr(mediafile, key)
if isinstance(value, (int, long)):
if isinstance(value, six.integer_types):
if value.bit_length() > 63:
value = 0
self[key] = value
@ -1060,7 +1065,8 @@ class Album(LibModel):
image = bytestring_path(image)
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)
if beets.config['asciify_paths']:
subpath = unidecode(subpath)
@ -1178,7 +1184,8 @@ def parse_query_string(s, model_cls):
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:
parts = util.shlex_split(s)
except ValueError as exc:
@ -1254,7 +1261,7 @@ class Library(dbcore.Database):
# Parse the query, if necessary.
try:
parsed_sort = None
if isinstance(query, basestring):
if isinstance(query, six.string_types):
query, parsed_sort = parse_query_string(query, model_cls)
elif isinstance(query, (list, tuple)):
query, parsed_sort = parse_query_parts(query, model_cls)
@ -1404,7 +1411,7 @@ class DefaultTemplateFunctions(object):
def tmpl_time(s, fmt):
"""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))
def tmpl_aunique(self, keys=None, disam=None):

View file

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

View file

@ -59,6 +59,7 @@ import enum
from beets import logging
from beets.util import displayable_path, syspath, as_string
import six
__all__ = ['UnreadableFileError', 'FileTypeError', 'MediaFile']
@ -130,8 +131,8 @@ def _safe_cast(out_type, val):
return int(val)
else:
# Process any other type as a string.
if not isinstance(val, basestring):
val = unicode(val)
if not isinstance(val, six.string_types):
val = six.text_type(val)
# Get a number from the front of the string.
val = re.match(r'[0-9]*', val.strip()).group(0)
if not val:
@ -146,13 +147,13 @@ def _safe_cast(out_type, val):
except ValueError:
return False
elif out_type == unicode:
elif out_type == six.text_type:
if isinstance(val, bytes):
return val.decode('utf8', 'ignore')
elif isinstance(val, unicode):
elif isinstance(val, six.text_type):
return val
else:
return unicode(val)
return six.text_type(val)
elif out_type == float:
if isinstance(val, int) or isinstance(val, float):
@ -161,7 +162,7 @@ def _safe_cast(out_type, val):
if isinstance(val, bytes):
val = val.decode('utf8', 'ignore')
else:
val = unicode(val)
val = six.text_type(val)
match = re.match(r'[\+-]?([0-9]+\.?[0-9]*|[0-9]*\.[0-9]+)',
val.strip())
if match:
@ -220,7 +221,7 @@ def _sc_decode(soundcheck):
"""
# We decode binary data. If one of the formats gives us a text
# string, interpret it as UTF-8.
if isinstance(soundcheck, unicode):
if isinstance(soundcheck, six.text_type):
soundcheck = soundcheck.encode('utf8')
# 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.
"""
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:
- `key`: The key on the Mutagen file object used to access the
@ -426,8 +428,8 @@ class StorageStyle(object):
self.float_places = float_places
# Convert suffix to correct string type.
if self.suffix and self.as_type is unicode \
and not isinstance(self.suffix, unicode):
if self.suffix and self.as_type is six.text_type \
and not isinstance(self.suffix, six.text_type):
self.suffix = self.suffix.decode('utf8')
# Getter.
@ -450,7 +452,7 @@ class StorageStyle(object):
"""Given a raw value stored on a Mutagen object, decode and
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):
return mutagen_value[:-len(self.suffix)]
else:
@ -472,17 +474,17 @@ class StorageStyle(object):
"""Convert the external Python value to a type that is suitable for
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 = self.as_type(value)
elif self.as_type is unicode:
elif self.as_type is six.text_type:
if isinstance(value, bool):
# 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):
value = value.decode('utf8', 'ignore')
else:
value = unicode(value)
value = six.text_type(value)
else:
value = self.as_type(value)
@ -592,7 +594,7 @@ class MP4StorageStyle(StorageStyle):
def serialize(self, 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')
return value
@ -807,7 +809,7 @@ class MP3SlashPackStorageStyle(MP3StorageStyle):
def _fetch_unpacked(self, mutagen_file):
data = self.fetch(mutagen_file)
if data:
items = unicode(data).split('/')
items = six.text_type(data).split('/')
else:
items = []
packing_length = 2
@ -823,7 +825,7 @@ class MP3SlashPackStorageStyle(MP3StorageStyle):
items[0] = ''
if items[1] is None:
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):
if self.pack_pos == 0:
@ -1074,7 +1076,7 @@ class MediaField(object):
getting this property.
"""
self.out_type = kwargs.get('out_type', unicode)
self.out_type = kwargs.get('out_type', six.text_type)
self._styles = styles
def styles(self, mutagen_file):
@ -1113,7 +1115,7 @@ class MediaField(object):
return 0.0
elif self.out_type == bool:
return False
elif self.out_type == unicode:
elif self.out_type == six.text_type:
return u''
@ -1194,9 +1196,9 @@ class DateField(MediaField):
"""
# Get the underlying data and split on hyphens and slashes.
datestring = super(DateField, self).__get__(mediafile, None)
if isinstance(datestring, basestring):
datestring = re.sub(r'[Tt ].*$', '', unicode(datestring))
items = re.split('[-/]', unicode(datestring))
if isinstance(datestring, six.string_types):
datestring = re.sub(r'[Tt ].*$', '', six.text_type(datestring))
items = re.split('[-/]', six.text_type(datestring))
else:
items = []
@ -1233,7 +1235,7 @@ class DateField(MediaField):
date.append(u'{0:02d}'.format(int(month)))
if month and 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))
if hasattr(self, '_year_field'):
@ -1360,7 +1362,7 @@ class MediaFile(object):
try:
self.mgfile = mutagen.File(path)
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)
except IOError as exc:
if type(exc) == IOError:

View file

@ -27,6 +27,7 @@ from functools import wraps
import beets
from beets import logging
from beets import mediafile
import six
PLUGIN_NAMESPACE = 'beetsplug'
@ -54,10 +55,10 @@ class PluginLogFilter(logging.Filter):
def filter(self, record):
if hasattr(record.msg, 'msg') and isinstance(record.msg.msg,
basestring):
six.string_types):
# A _LogMessage from our hacked-up Logging replacement.
record.msg.msg = self.prefix + record.msg.msg
elif isinstance(record.msg, basestring):
elif isinstance(record.msg, six.string_types):
record.msg = self.prefix + record.msg
return True

View file

@ -31,6 +31,7 @@ import re
import struct
import traceback
import os.path
from six.moves import input
from beets import logging
from beets import library
@ -41,6 +42,7 @@ from beets import config
from beets.util import confit, as_string
from beets.autotag import mb
from beets.dbcore import query as db_query
import six
# On Windows platforms, use colorama to support "ANSI" terminal colors.
if sys.platform == 'win32':
@ -139,7 +141,7 @@ def print_(*strings, **kwargs):
"""
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'\n' if end is None else end
else:
@ -147,7 +149,7 @@ def print_(*strings, **kwargs):
txt += b'\n' if end is None else end
# Always send bytes to the stdout stream.
if isinstance(txt, unicode):
if isinstance(txt, six.text_type):
txt = txt.encode(_out_encoding(), 'replace')
sys.stdout.write(txt)
@ -193,7 +195,7 @@ def should_move(move_opt=None):
# Input prompts.
def input_(prompt=None):
"""Like `raw_input`, but decodes the result to a Unicode string.
"""Like `input`, but decodes the result to a Unicode string.
Raises a UserError if stdin is not available. The prompt is sent to
stdout rather than stderr. A printed between the prompt and the
input cursor.
@ -205,7 +207,7 @@ def input_(prompt=None):
print_(prompt, end=' ')
try:
resp = raw_input()
resp = input()
except EOFError:
raise UserError(u'stdin stream ended while input required')
@ -261,7 +263,7 @@ def input_options(options, require=False, prompt=None, fallback_prompt=None,
# Mark the option's shortcut letter for display.
if not require and (
(default is None and not numrange and first) or
(isinstance(default, basestring) and
(isinstance(default, six.string_types) and
found_letter.lower() == default.lower())):
# The first option is the default; mark it.
show_letter = '[%s]' % found_letter.upper()
@ -297,11 +299,11 @@ def input_options(options, require=False, prompt=None, fallback_prompt=None,
prompt_part_lengths = []
if numrange:
if isinstance(default, int):
default_name = unicode(default)
default_name = six.text_type(default)
default_name = colorize('action_default', default_name)
tmpl = '# selection (default %s)'
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:
prompt_parts.append('# selection')
prompt_part_lengths.append(len(prompt_parts[-1]))
@ -521,7 +523,8 @@ def colorize(color_name, text):
if config['ui']['color']:
global 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)
# In case a 3rd party plugin is still passing the actual color ('red')
# instead of the abstract color name ('text_error')
@ -541,10 +544,11 @@ def _colordiff(a, b, highlight='text_highlight',
highlighted intelligently to show differences; other values are
stringified and highlighted in their entirety.
"""
if not isinstance(a, basestring) or not isinstance(b, basestring):
if not isinstance(a, six.string_types) \
or not isinstance(b, six.string_types):
# Non-strings: use ordinary equality.
a = unicode(a)
b = unicode(b)
a = six.text_type(a)
b = six.text_type(b)
if a == b:
return a, b
else:
@ -592,7 +596,7 @@ def colordiff(a, b, highlight='text_highlight'):
if config['ui']['color']:
return _colordiff(a, b, highlight)
else:
return unicode(a), unicode(b)
return six.text_type(a), six.text_type(b)
def get_path_formats(subview=None):
@ -603,7 +607,7 @@ def get_path_formats(subview=None):
subview = subview or config['paths']
for query, view in subview.items():
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
@ -671,7 +675,7 @@ def _field_diff(field, old, new):
# For strings, highlight changes. For others, colorize the whole
# thing.
if isinstance(oldval, basestring):
if isinstance(oldval, six.string_types):
oldstr, newstr = colordiff(oldval, newstr)
else:
oldstr = colorize('text_error', oldstr)
@ -864,7 +868,7 @@ class CommonOptionsParser(optparse.OptionParser, object):
"""
kwargs = {}
if target:
if isinstance(target, basestring):
if isinstance(target, six.string_types):
target = {'item': library.Item,
'album': library.Album}[target]
kwargs['target'] = target

View file

@ -38,6 +38,7 @@ from beets import library
from beets import config
from beets import logging
from beets.util.confit import _package_path
import six
VARIOUS_ARTISTS = u'Various Artists'
PromptChoice = namedtuple('ExtraChoice', ['short', 'long', 'callback'])
@ -163,7 +164,7 @@ def disambig_string(info):
else:
disambig.append(info.media)
if info.year:
disambig.append(unicode(info.year))
disambig.append(six.text_type(info.year))
if info.country:
disambig.append(info.country)
if info.label:
@ -236,9 +237,9 @@ def show_change(cur_artist, cur_album, match):
if mediums > 1:
return u'{0}-{1}'.format(medium, medium_index)
else:
return unicode(medium_index)
return six.text_type(medium_index)
else:
return unicode(index)
return six.text_type(index)
# Identify the album in question.
if cur_artist != match.info.artist or \
@ -806,7 +807,7 @@ class TerminalImportSession(importer.ImportSession):
if search_id:
candidates, rec = autotag.tag_item(
task.item, search_ids=search_id.split())
elif choice in extra_ops.keys():
elif choice in list(extra_ops.keys()):
# Allow extra ops to automatically set the post-choice.
post_choice = extra_ops[choice](self, task)
if isinstance(post_choice, importer.action):

View file

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

View file

@ -18,14 +18,14 @@ public resizing proxy if neither is available.
"""
from __future__ import division, absolute_import, print_function
import urllib
import subprocess
import os
import re
from tempfile import NamedTemporaryFile
from six.moves.urllib.parse import urlencode
from beets import logging
from beets import util
import six
# Resizing methods
PIL = 1
@ -41,7 +41,7 @@ def resize_url(url, maxwidth):
"""Return a proxied image URL that resizes the original image to
maxwidth (preserving aspect ratio).
"""
return '{0}?{1}'.format(PROXY_URL, urllib.urlencode({
return '{0}?{1}'.format(PROXY_URL, urlencode({
'url': url.replace('http://', ''),
'w': bytes(maxwidth),
}))
@ -160,10 +160,9 @@ class Shareable(type):
return self._instance
class ArtResizer(object):
class ArtResizer(six.with_metaclass(Shareable, object)):
"""A singleton class that performs image resizes.
"""
__metaclass__ = Shareable
def __init__(self):
"""Create a resizer object with an inferred method.

View file

@ -9,6 +9,7 @@ Bluelet: easy concurrency without all the messy parallelism.
"""
from __future__ import division, absolute_import, print_function
import six
import socket
import select
import sys
@ -19,20 +20,6 @@ import time
import collections
# A little bit of "six" (Python 2/3 compatibility): cope with PEP 3109 syntax
# changes.
PY3 = sys.version_info[0] == 3
if PY3:
def _reraise(typ, exc, tb):
raise exc.with_traceback(tb)
else:
exec("""
def _reraise(typ, exc, tb):
raise typ, exc, tb
""")
# Basic events used for thread scheduling.
class Event(object):
@ -214,7 +201,7 @@ class ThreadException(Exception):
self.exc_info = exc_info
def reraise(self):
_reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2])
six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2])
SUSPENDED = Event() # Special sentinel placeholder for suspended threads.

View file

@ -25,6 +25,7 @@ import yaml
import collections
import re
from collections import OrderedDict
import six
UNIX_DIR_VAR = 'XDG_CONFIG_HOME'
UNIX_DIR_FALLBACK = '~/.config'
@ -44,7 +45,7 @@ REDACTED_TOMBSTONE = 'REDACTED'
# Utilities.
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
NUMERIC_TYPES = (int, float) if PY3 else (int, float, long) # noqa

View file

@ -34,7 +34,7 @@ import ast
import dis
import types
from .confit import NUMERIC_TYPES
import six
SYMBOL_DELIM = u'$'
FUNC_DELIM = u'%'
@ -74,11 +74,11 @@ def ex_literal(val):
"""
if val is None:
return ast.Name('None', ast.Load())
elif isinstance(val, NUMERIC_TYPES):
elif isinstance(val, six.integer_types):
return ast.Num(val)
elif isinstance(val, bool):
return ast.Name(bytes(val), ast.Load())
elif isinstance(val, basestring):
elif isinstance(val, six.string_types):
return ast.Str(val)
raise TypeError(u'no literal for {0}'.format(type(val)))
@ -97,7 +97,7 @@ def ex_call(func, args):
function may be an expression or the name of a function. Each
argument may be an expression or a value to be used as a literal.
"""
if isinstance(func, basestring):
if isinstance(func, six.string_types):
func = ex_rvalue(func)
args = list(args)
@ -190,8 +190,8 @@ class Call(object):
except Exception as exc:
# Function raised exception! Maybe inlining the name of
# the exception will help debug.
return u'<%s>' % unicode(exc)
return unicode(out)
return u'<%s>' % six.text_type(exc)
return six.text_type(out)
else:
return self.original
@ -242,11 +242,11 @@ class Expression(object):
"""
out = []
for part in self.parts:
if isinstance(part, basestring):
if isinstance(part, six.string_types):
out.append(part)
else:
out.append(part.evaluate(env))
return u''.join(map(unicode, out))
return u''.join(map(six.text_type, out))
def translate(self):
"""Compile the expression to a list of Python AST expressions, a
@ -256,7 +256,7 @@ class Expression(object):
varnames = set()
funcnames = set()
for part in self.parts:
if isinstance(part, basestring):
if isinstance(part, six.string_types):
expressions.append(ex_literal(part))
else:
e, v, f = part.translate()
@ -508,7 +508,8 @@ class Template(object):
def __init__(self, template):
self.expr = _parse(template)
self.original = template
self.compiled = self.translate()
if six.PY2:
self.compiled = self.translate()
def __eq__(self, other):
return self.original == other.original
@ -524,9 +525,12 @@ class Template(object):
def substitute(self, values={}, functions={}):
"""Evaluate the template given the values and functions.
"""
try:
res = self.compiled(values, functions)
except: # Handle any exceptions thrown by compiled version.
if six.PY2:
try:
res = self.compiled(values, functions)
except: # Handle any exceptions thrown by compiled version.
res = self.interpret(values, functions)
else:
res = self.interpret(values, functions)
return res
@ -563,7 +567,7 @@ if __name__ == '__main__':
import timeit
_tmpl = Template(u'foo $bar %baz{foozle $bar barzle} $bar')
_vars = {'bar': 'qux'}
_funcs = {'baz': unicode.upper}
_funcs = {'baz': six.text_type.upper}
interp_time = timeit.timeit('_tmpl.interpret(_vars, _funcs)',
'from __main__ import _tmpl, _vars, _funcs',
number=10000)

View file

@ -20,6 +20,7 @@ import os
import stat
import ctypes
import sys
import six
def _is_hidden_osx(path):
@ -74,7 +75,7 @@ def is_hidden(path):
work out if a file is hidden.
"""
# 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')
# Run platform specific functions depending on the platform

View file

@ -34,9 +34,10 @@ in place of any single coroutine.
from __future__ import division, absolute_import, print_function
import Queue
from six.moves import queue
from threading import Thread, Lock
import sys
import six
BUBBLE = '__PIPELINE_BUBBLE__'
POISON = '__PIPELINE_POISON__'
@ -75,13 +76,13 @@ def _invalidate_queue(q, val=None, sync=True):
q.mutex.release()
class CountedQueue(Queue.Queue):
class CountedQueue(queue.Queue):
"""A queue that keeps track of the number of threads that are
still feeding into it. The queue is poisoned when all threads are
finished with the queue.
"""
def __init__(self, maxsize=0):
Queue.Queue.__init__(self, maxsize)
queue.Queue.__init__(self, maxsize)
self.nthreads = 0
self.poisoned = False
@ -431,7 +432,7 @@ class Pipeline(object):
exc_info = thread.exc_info
if exc_info:
# Make the exception appear as it was raised originally.
raise exc_info[0], exc_info[1], exc_info[2]
six.reraise(exc_info[0], exc_info[1], exc_info[2])
def pull(self):
"""Yield elements from the end of the pipeline. Runs the stages

View file

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

View file

@ -18,6 +18,7 @@ from __future__ import division, absolute_import, print_function
import json
import re
import six
from datetime import datetime, timedelta
from requests_oauthlib import OAuth1Session
@ -42,15 +43,15 @@ class BeatportAPIError(Exception):
class BeatportObject(object):
def __init__(self, data):
self.beatport_id = data['id']
self.name = unicode(data['name'])
self.name = six.text_type(data['name'])
if 'releaseDate' in data:
self.release_date = datetime.strptime(data['releaseDate'],
'%Y-%m-%d')
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']]
if 'genres' in data:
self.genres = [unicode(x['name'])
self.genres = [six.text_type(x['name'])
for x in data['genres']]
@ -196,8 +197,9 @@ class BeatportClient(object):
return response.json()['results']
@six.python_2_unicode_compatible
class BeatportRelease(BeatportObject):
def __unicode__(self):
def __str__(self):
if len(self.artists) < 4:
artist_str = ", ".join(x[1] for x in self.artists)
else:
@ -209,7 +211,7 @@ class BeatportRelease(BeatportObject):
)
def __repr__(self):
return unicode(self).encode('utf8')
return six.text_type(self).encode('utf8')
def __init__(self, data):
BeatportObject.__init__(self, data)
@ -224,21 +226,22 @@ class BeatportRelease(BeatportObject):
data['slug'], data['id'])
@six.python_2_unicode_compatible
class BeatportTrack(BeatportObject):
def __unicode__(self):
def __str__(self):
artist_str = ", ".join(x[1] for x in self.artists)
return (u"<BeatportTrack: {0} - {1} ({2})>"
.format(artist_str, self.name, self.mix_name))
def __repr__(self):
return unicode(self).encode('utf8')
return six.text_type(self).encode('utf8')
def __init__(self, data):
BeatportObject.__init__(self, data)
if 'title' in data:
self.title = unicode(data['title'])
self.title = six.text_type(data['title'])
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)
if not self.length:
try:
@ -266,8 +269,8 @@ class BeatportPlugin(BeetsPlugin):
self.register_listener('import_begin', self.setup)
def setup(self, session=None):
c_key = self.config['apikey'].get(unicode)
c_secret = self.config['apisecret'].get(unicode)
c_key = self.config['apikey'].get(six.text_type)
c_secret = self.config['apisecret'].get(six.text_type)
# Get the OAuth token from a file or log in.
try:

View file

@ -35,6 +35,7 @@ from beets.util import bluelet
from beets.library import Item
from beets import dbcore
from beets.mediafile import MediaFile
import six
PROTOCOL_VERSION = '0.13.0'
BUFSIZE = 1024
@ -305,12 +306,12 @@ class BaseServer(object):
playlist, playlistlength, and xfade.
"""
yield (
u'volume: ' + unicode(self.volume),
u'repeat: ' + unicode(int(self.repeat)),
u'random: ' + unicode(int(self.random)),
u'playlist: ' + unicode(self.playlist_version),
u'playlistlength: ' + unicode(len(self.playlist)),
u'xfade: ' + unicode(self.crossfade),
u'volume: ' + six.text_type(self.volume),
u'repeat: ' + six.text_type(int(self.repeat)),
u'random: ' + six.text_type(int(self.random)),
u'playlist: ' + six.text_type(self.playlist_version),
u'playlistlength: ' + six.text_type(len(self.playlist)),
u'xfade: ' + six.text_type(self.crossfade),
)
if self.current_index == -1:
@ -323,8 +324,8 @@ class BaseServer(object):
if self.current_index != -1: # i.e., paused or playing
current_id = self._item_id(self.playlist[self.current_index])
yield u'song: ' + unicode(self.current_index)
yield u'songid: ' + unicode(current_id)
yield u'song: ' + six.text_type(self.current_index)
yield u'songid: ' + six.text_type(current_id)
if self.error:
yield u'error: ' + self.error
@ -468,8 +469,8 @@ class BaseServer(object):
Also a dummy implementation.
"""
for idx, track in enumerate(self.playlist):
yield u'cpos: ' + unicode(idx)
yield u'Id: ' + unicode(track.id)
yield u'cpos: ' + six.text_type(idx)
yield u'Id: ' + six.text_type(track.id)
def cmd_currentsong(self, conn):
"""Sends information about the currently-playing song.
@ -569,11 +570,11 @@ class Connection(object):
added after every string. Returns a Bluelet event that sends
the data.
"""
if isinstance(lines, basestring):
if isinstance(lines, six.string_types):
lines = [lines]
out = NEWLINE.join(lines) + NEWLINE
log.debug('{}', out[:-1]) # Don't log trailing newline.
if isinstance(out, unicode):
if isinstance(out, six.text_type):
out = out.encode('utf8')
return self.sock.sendall(out)
@ -771,28 +772,28 @@ class Server(BaseServer):
def _item_info(self, item):
info_lines = [
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'Artist: ' + item.artist,
u'Album: ' + item.album,
u'Genre: ' + item.genre,
]
track = unicode(item.track)
track = six.text_type(item.track)
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'Date: ' + unicode(item.year))
info_lines.append(u'Date: ' + six.text_type(item.year))
try:
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:
# Don't include position if not in playlist.
pass
info_lines.append(u'Id: ' + unicode(item.id))
info_lines.append(u'Id: ' + six.text_type(item.id))
return info_lines
@ -852,7 +853,7 @@ class Server(BaseServer):
for name, itemid in iter(sorted(node.files.items())):
item = self.lib.get_item(itemid)
yield self._item_info(item)
for name, _ in iter(sorted(node.dirs.iteritems())):
for name, _ in iter(sorted(six.iteritems(node.dirs))):
dirpath = self._path_join(path, name)
if dirpath.startswith(u"/"):
# Strip leading slash (libmpc rejects this).
@ -872,12 +873,12 @@ class Server(BaseServer):
yield u'file: ' + basepath
else:
# List a directory. Recurse into both directories and files.
for name, itemid in sorted(node.files.iteritems()):
for name, itemid in sorted(six.iteritems(node.files)):
newpath = self._path_join(basepath, name)
# "yield from"
for v in self._listall(newpath, itemid, info):
yield v
for name, subdir in sorted(node.dirs.iteritems()):
for name, subdir in sorted(six.iteritems(node.dirs)):
newpath = self._path_join(basepath, name)
yield u'directory: ' + newpath
for v in self._listall(newpath, subdir, info):
@ -902,11 +903,11 @@ class Server(BaseServer):
yield self.lib.get_item(node)
else:
# Recurse into a directory.
for name, itemid in sorted(node.files.iteritems()):
for name, itemid in sorted(six.iteritems(node.files)):
# "yield from"
for v in self._all_items(itemid):
yield v
for name, subdir in sorted(node.dirs.iteritems()):
for name, subdir in sorted(six.iteritems(node.dirs)):
for v in self._all_items(subdir):
yield v
@ -917,7 +918,7 @@ class Server(BaseServer):
for item in self._all_items(self._resolve_path(path)):
self.playlist.append(item)
if send_id:
yield u'Id: ' + unicode(item.id)
yield u'Id: ' + six.text_type(item.id)
self.playlist_version += 1
def cmd_add(self, conn, path):
@ -938,11 +939,11 @@ class Server(BaseServer):
if self.current_index > -1:
item = self.playlist[self.current_index]
yield u'bitrate: ' + unicode(item.bitrate / 1000)
yield u'bitrate: ' + six.text_type(item.bitrate / 1000)
# Missing 'audio'.
(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'.
@ -957,13 +958,13 @@ class Server(BaseServer):
artists, albums, songs, totaltime = tx.query(statement)[0]
yield (
u'artists: ' + unicode(artists),
u'albums: ' + unicode(albums),
u'songs: ' + unicode(songs),
u'uptime: ' + unicode(int(time.time() - self.startup_time)),
u'artists: ' + six.text_type(artists),
u'albums: ' + six.text_type(albums),
u'songs: ' + six.text_type(songs),
u'uptime: ' + six.text_type(int(time.time() - self.startup_time)),
u'playtime: ' + u'0', # Missing.
u'db_playtime: ' + unicode(int(totaltime)),
u'db_update: ' + unicode(int(self.updated_time)),
u'db_playtime: ' + six.text_type(int(totaltime)),
u'db_update: ' + six.text_type(int(self.updated_time)),
)
# Searching.
@ -1059,7 +1060,7 @@ class Server(BaseServer):
rows = tx.query(statement, subvals)
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):
"""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)):
songs += 1
playtime += item.length
yield u'songs: ' + unicode(songs)
yield u'playtime: ' + unicode(int(playtime))
yield u'songs: ' + six.text_type(songs)
yield u'playtime: ' + six.text_type(int(playtime))
# "Outputs." Just a dummy implementation because we don't control
# any outputs.
@ -1180,11 +1181,12 @@ class BPDPlugin(BeetsPlugin):
)
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)
if args:
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)
debug = opts.debug or False
self.start_bpd(lib, host, int(port), password, volume, debug)

View file

@ -19,12 +19,13 @@ music player.
from __future__ import division, absolute_import, print_function
import six
import sys
import time
import thread
from six.moves import _thread
import os
import copy
import urllib
from six.moves import urllib
from beets import ui
import gi
@ -128,9 +129,9 @@ class GstPlayer(object):
path.
"""
self.player.set_state(Gst.State.NULL)
if isinstance(path, unicode):
if isinstance(path, six.text_type):
path = path.encode('utf8')
uri = 'file://' + urllib.quote(path)
uri = 'file://' + urllib.parse.quote(path)
self.player.set_property("uri", uri)
self.player.set_state(Gst.State.PLAYING)
self.playing = True
@ -164,7 +165,7 @@ class GstPlayer(object):
loop = GLib.MainLoop()
loop.run()
thread.start_new_thread(start, ())
_thread.start_new_thread(start, ())
def time(self):
"""Returns a tuple containing (position, length) where both

View file

@ -18,6 +18,7 @@
from __future__ import division, absolute_import, print_function
import time
from six.moves import input
from beets import ui
from beets.plugins import BeetsPlugin
@ -31,7 +32,7 @@ def bpm(max_strokes):
dt = []
for i in range(max_strokes):
# Press enter to the rhythm...
s = raw_input()
s = input()
if s == '':
t1 = time.time()
# Only start measuring at the second stroke

View file

@ -21,7 +21,8 @@ from __future__ import division, absolute_import, print_function
from datetime import datetime
import re
import string
from itertools import tee, izip
from six.moves import zip
from itertools import tee
from beets import plugins, ui
@ -37,7 +38,7 @@ def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return izip(a, b)
return zip(a, b)
def span_from_str(span_str):

View file

@ -26,6 +26,7 @@ from beets.util import confit
from beets.autotag import hooks
import acoustid
from collections import defaultdict
import six
API_KEY = '1vOwZtEn'
SCORE_THRESH = 0.5
@ -121,7 +122,7 @@ def _all_releases(items):
for release_id in release_ids:
relcounts[release_id] += 1
for release_id, count in relcounts.iteritems():
for release_id, count in six.iteritems(relcounts):
if float(count) / len(items) > COMMON_REL_THRESH:
yield release_id
@ -181,7 +182,7 @@ class AcoustidPlugin(plugins.BeetsPlugin):
def submit_cmd_func(lib, opts, args):
try:
apikey = config['acoustid']['apikey'].get(unicode)
apikey = config['acoustid']['apikey'].get(six.text_type)
except confit.NotFoundError:
raise ui.UserError(u'no Acoustid user API key provided')
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 import art
from beets.util.artresizer import ArtResizer
import six
_fs_lock = threading.Lock()
_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.
"""
if not fmt:
fmt = config['convert']['format'].get(unicode).lower()
fmt = config['convert']['format'].get(six.text_type).lower()
fmt = ALIASES.get(fmt, fmt)
try:
@ -74,14 +75,14 @@ def get_format(fmt=None):
# Convenience and backwards-compatibility shortcuts.
keys = config['convert'].keys()
if 'command' in keys:
command = config['convert']['command'].get(unicode)
command = config['convert']['command'].get(six.text_type)
elif 'opts' in keys:
# Undocumented option for backwards compatibility with < 1.3.1.
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:
extension = config['convert']['extension'].get(unicode)
extension = config['convert']['extension'].get(six.text_type)
return (command.encode('utf8'), extension.encode('utf8'))
@ -389,7 +390,7 @@ class ConvertPlugin(BeetsPlugin):
path_formats = ui.get_path_formats()
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 \
self.config['pretend'].get(bool)
@ -422,7 +423,7 @@ class ConvertPlugin(BeetsPlugin):
"""Transcode a file automatically after it is imported into the
library.
"""
fmt = self.config['format'].get(unicode).lower()
fmt = self.config['format'].get(six.text_type).lower()
if should_transcode(item, fmt):
command, ext = get_format()

View file

@ -27,13 +27,14 @@ from beets.util import confit
from discogs_client import Release, Client
from discogs_client.exceptions import DiscogsAPIError
from requests.exceptions import ConnectionError
from six.moves import http_client
import beets
import re
import time
import json
import socket
import httplib
import os
import six
# Silence spurious INFO log lines generated by urllib3.
@ -43,7 +44,7 @@ urllib3_logger.setLevel(logging.CRITICAL)
USER_AGENT = u'beets/{0} +http://beets.io/'.format(beets.__version__)
# Exceptions that discogs_client should really handle but does not.
CONNECTION_ERRORS = (ConnectionError, socket.error, httplib.HTTPException,
CONNECTION_ERRORS = (ConnectionError, socket.error, http_client.HTTPException,
ValueError, # JSON decoding raises a ValueError.
DiscogsAPIError)
@ -66,8 +67,8 @@ class DiscogsPlugin(BeetsPlugin):
def setup(self, session=None):
"""Create the `discogs_client` field. Authenticate if necessary.
"""
c_key = self.config['apikey'].get(unicode)
c_secret = self.config['apisecret'].get(unicode)
c_key = self.config['apikey'].get(six.text_type)
c_secret = self.config['apisecret'].get(six.text_type)
# Get the OAuth token from a file or log in.
try:
@ -225,7 +226,7 @@ class DiscogsPlugin(BeetsPlugin):
result.data['formats'][0].get('descriptions', [])) or None
va = result.data['artists'][0]['name'].lower() == 'various'
if va:
artist = config['va_name'].get(unicode)
artist = config['va_name'].get(six.text_type)
year = result.data['year']
label = result.data['labels'][0]['name']
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.util import command_output, displayable_path, subprocess
from beets.library import Item, Album
import six
PLUGIN = 'duplicates'
@ -264,7 +265,7 @@ class DuplicatesPlugin(BeetsPlugin):
# between a bytes object and the empty Unicode
# string ''.
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()
key = lambda x: sum(1 for f in fields if truthy(getattr(x, f)))
else:
@ -329,7 +330,7 @@ class DuplicatesPlugin(BeetsPlugin):
"""Generate triples of keys, duplicate counts, and constituent objects.
"""
offset = 0 if full else 1
for k, objs in self._group_by(objs, keys, strict).iteritems():
for k, objs in six.iteritems(self._group_by(objs, keys, strict)):
if len(objs) > 1:
objs = self._order(objs, tiebreak)
if merge:

View file

@ -27,6 +27,7 @@ import subprocess
import yaml
from tempfile import NamedTemporaryFile
import os
import six
# 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,
# 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:
raise ParseError(u'invalid YAML: {}'.format(e))
@ -141,7 +142,7 @@ def apply_(obj, data):
else:
# Either the field was stringified originally or the user changed
# 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):

View file

@ -12,8 +12,8 @@ from __future__ import division, absolute_import, print_function
from beets import config
from beets.plugins import BeetsPlugin
from urllib import urlencode
from urlparse import urljoin, parse_qs, urlsplit, urlunsplit
from six.moves.urllib.parse import urlencode
from six.moves.urllib.parse import urljoin, parse_qs, urlsplit, urlunsplit
import hashlib
import requests

View file

@ -33,6 +33,7 @@ from beets.mediafile import _image_mime_type
from beets.util.artresizer import ArtResizer
from beets.util import confit
from beets.util import syspath, bytestring_path
import six
try:
import itunes
@ -600,7 +601,7 @@ class Wikipedia(RemoteArtSource):
try:
data = wikipedia_response.json()
results = data['query']['pages']
for _, result in results.iteritems():
for _, result in six.iteritems(results):
image_url = result['imageinfo'][0]['url']
yield self._candidate(url=image_url,
match=Candidate.MATCH_EXACT)
@ -727,7 +728,7 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin):
confit.String(pattern=self.PAT_PERCENT)]))
self.margin_px = 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'%':
self.margin_percent = float(self.enforce_ratio[:-1]) / 100
elif self.enforce_ratio[-2:] == u'px':

View file

@ -22,6 +22,7 @@ from beets import plugins
from beets.util import displayable_path
import os
import re
import six
# Filename field extraction patterns.
@ -132,7 +133,7 @@ def apply_matches(d):
# Apply the title and track.
for item in d:
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:
item.track = int(d[item]['track'])

View file

@ -22,6 +22,7 @@ import re
from beets import plugins
from beets import ui
from beets.util import displayable_path
import six
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
# artist and if we do not drop featuring information.
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_title = u"{0} {1}".format(item.title, new_format)
self._log.info(u'title: {0} -> {1}', item.title, new_title)

View file

@ -22,6 +22,7 @@ from beets.plugins import BeetsPlugin
from beets.dbcore.query import StringFieldQuery
from beets import config
import difflib
import six
class FuzzyQuery(StringFieldQuery):
@ -44,5 +45,5 @@ class FuzzyPlugin(BeetsPlugin):
})
def queries(self):
prefix = self.config['prefix'].get(basestring)
prefix = self.config['prefix'].get(six.string_types)
return {prefix: FuzzyQuery}

View file

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

View file

@ -12,6 +12,7 @@ import os
from beets import util
from beets import importer
from beets.plugins import BeetsPlugin
import six
class ImportAddedPlugin(BeetsPlugin):
@ -62,7 +63,7 @@ class ImportAddedPlugin(BeetsPlugin):
def record_reimported(self, task, session):
self.reimported_item_ids = set(item.id for item, replaced_items
in task.replaced_items.iteritems()
in six.iteritems(task.replaced_items)
if replaced_items)
self.replaced_album_paths = set(task.replaced_albums.keys())

View file

@ -14,6 +14,7 @@
# included in all copies or substantial portions of the Software.
from __future__ import division, absolute_import, print_function
import six
"""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
@ -119,7 +120,7 @@ class ImportFeedsPlugin(BeetsPlugin):
if 'm3u' in formats:
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)
_write_m3u(m3u_path, paths)

View file

@ -26,6 +26,7 @@ from beets import ui
from beets import mediafile
from beets.library import Item
from beets.util import displayable_path, normpath, syspath
import six
def tag_data(lib, args):
@ -73,7 +74,7 @@ def library_data_emitter(item):
def update_summary(summary, tags):
for key, value in tags.iteritems():
for key, value in six.iteritems(tags):
if key not in summary:
summary[key] = value
elif summary[key] != value:
@ -96,7 +97,7 @@ def print_data(data, item=None, fmt=None):
path = displayable_path(item.path) if item else None
formatted = {}
for key, value in data.iteritems():
for key, value in six.iteritems(data):
if isinstance(value, list):
formatted[key] = u'; '.join(value)
if value is not None:
@ -123,7 +124,7 @@ def print_data_keys(data, item=None):
"""
path = displayable_path(item.path) if item else None
formatted = []
for key, value in data.iteritems():
for key, value in six.iteritems(data):
formatted.append(key)
if len(formatted) == 0:

View file

@ -22,6 +22,7 @@ import itertools
from beets.plugins import BeetsPlugin
from beets import config
import six
FUNC_NAME = u'__INLINE_FUNC__'
@ -32,7 +33,7 @@ class InlineError(Exception):
def __init__(self, code, exc):
super(InlineError, self).__init__(
(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(),
config['pathfields'].items()):
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:
self.template_fields[key] = func
# Album fields.
for key, view in config['album_fields'].items():
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:
self.album_template_fields[key] = func

View file

@ -23,6 +23,7 @@ import subprocess
from beets import ui
from beets import util
from beets.plugins import BeetsPlugin
import six
class KeyFinderPlugin(BeetsPlugin):
@ -52,7 +53,7 @@ class KeyFinderPlugin(BeetsPlugin):
def find_key(self, items, write=False):
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:
if item['initial_key'] and not overwrite:

View file

@ -14,6 +14,7 @@
# included in all copies or substantial portions of the Software.
from __future__ import division, absolute_import, print_function
import six
"""Gets genres for imported music based on Last.fm tags.
@ -71,7 +72,7 @@ def flatten_tree(elem, path, branches):
for sub in elem:
flatten_tree(sub, path, branches)
else:
branches.append(path + [unicode(elem)])
branches.append(path + [six.text_type(elem)])
def find_parents(candidate, branches):
@ -186,7 +187,7 @@ class LastGenrePlugin(plugins.BeetsPlugin):
# the original tags list
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)]
)
@ -221,7 +222,8 @@ class LastGenrePlugin(plugins.BeetsPlugin):
if any(not s for s in args):
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:
return self._genre_cache[key]
else:
@ -297,7 +299,7 @@ class LastGenrePlugin(plugins.BeetsPlugin):
result = None
if isinstance(obj, library.Item):
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)
else:
# For "Various Artists", pick the most popular track genre.

View file

@ -15,6 +15,7 @@
from __future__ import division, absolute_import, print_function
from six.moves import range
import pylast
from pylast import TopItem, _extract, _number
from beets import ui
@ -22,6 +23,7 @@ from beets import dbcore
from beets import config
from beets import plugins
from beets.dbcore import types
import six
API_URL = 'http://ws.audioscrobbler.com/2.0/'
@ -110,7 +112,7 @@ class CustomUser(pylast.User):
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)
if not user:
@ -192,7 +194,7 @@ def process_tracks(lib, tracks, log):
total_fails = 0
log.info(u'Received {0} tracks in this page, processing...', total)
for num in xrange(0, total):
for num in range(0, total):
song = None
trackid = tracks[num]['mbid'].strip()
artist = tracks[num]['artist'].get('name', '').strip()

View file

@ -24,9 +24,9 @@ import json
import re
import requests
import unicodedata
import urllib
import warnings
from HTMLParser import HTMLParseError
from six.moves import urllib
import six
try:
from bs4 import SoupStrainer, BeautifulSoup
@ -40,6 +40,15 @@ try:
except ImportError:
HAS_LANGDETECT = False
try:
# PY3: HTMLParseError was removed in 3.5 as strict mode
# was deprecated in 3.3.
# https://docs.python.org/3.3/library/html.parser.html
from six.moves.html_parser import HTMLParseError
except ImportError:
class HTMLParseError(Exception):
pass
from beets import plugins
from beets import ui
@ -177,11 +186,11 @@ class Backend(object):
@staticmethod
def _encode(s):
"""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():
s = s.replace(char, repl)
s = s.encode('utf8', 'ignore')
return urllib.quote(s)
return urllib.parse.quote(s)
def build_url(self, artist, title):
return self.URL_PATTERN % (self._encode(artist.title()),
@ -223,7 +232,7 @@ class SymbolsReplaced(Backend):
@classmethod
def _encode(cls, s):
for old, new in cls.REPLACEMENTS.iteritems():
for old, new in six.iteritems(cls.REPLACEMENTS):
s = re.sub(old, new, s)
return super(SymbolsReplaced, cls)._encode(s)
@ -250,13 +259,13 @@ class Genius(Backend):
"""Fetch lyrics from Genius via genius-api."""
def __init__(self, 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}
def search_genius(self, artist, title):
query = u"%s %s" % (artist, title)
url = u'https://api.genius.com/search?q=%s' \
% (urllib.quote(query.encode('utf8')))
% (urllib.parse.quote(query.encode('utf8')))
self._log.debug(u'genius: requesting search {}', url)
try:
@ -461,8 +470,8 @@ class Google(Backend):
"""Fetch lyrics from Google search results."""
def __init__(self, config, log):
super(Google, self).__init__(config, log)
self.api_key = config['google_API_key'].get(unicode)
self.engine_id = config['google_engine_ID'].get(unicode)
self.api_key = config['google_API_key'].get(six.text_type)
self.engine_id = config['google_engine_ID'].get(six.text_type)
def is_lyrics(self, text, artist=None):
"""Determine whether the text seems to be valid lyrics.
@ -503,7 +512,7 @@ class Google(Backend):
try:
text = unicodedata.normalize('NFKD', text).encode('ascii',
'ignore')
text = unicode(re.sub('[-\s]+', ' ', text.decode('utf-8')))
text = six.text_type(re.sub('[-\s]+', ' ', text.decode('utf-8')))
except UnicodeDecodeError:
self._log.exception(u"Failing to normalize '{0}'", text)
return text
@ -542,9 +551,9 @@ class Google(Backend):
query = u"%s %s" % (artist, title)
url = u'https://www.googleapis.com/customsearch/v1?key=%s&cx=%s&q=%s' \
% (self.api_key, self.engine_id,
urllib.quote(query.encode('utf8')))
urllib.parse.quote(query.encode('utf8')))
data = urllib.urlopen(url)
data = urllib.request.urlopen(url)
data = json.load(data)
if 'error' in data:
reason = data['error']['errors'][0]['reason']
@ -643,7 +652,7 @@ class LyricsPlugin(plugins.BeetsPlugin):
oauth_url = 'https://datamarket.accesscontrol.windows.net/v2/OAuth2-13'
oauth_token = json.loads(requests.post(
oauth_url,
data=urllib.urlencode(params)).content)
data=urllib.parse.urlencode(params)).content)
if 'access_token' in oauth_token:
return "Bearer " + oauth_token['access_token']
else:

View file

@ -22,6 +22,7 @@ from beets import config
import musicbrainzngs
import re
import six
SUBMISSION_CHUNK_SIZE = 200
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__()
config['musicbrainz']['pass'].redact = True
musicbrainzngs.auth(
config['musicbrainz']['user'].get(unicode),
config['musicbrainz']['pass'].get(unicode),
config['musicbrainz']['user'].get(six.text_type),
config['musicbrainz']['pass'].get(six.text_type),
)
self.config.add({'auto': False})
if self.config['auto']:

View file

@ -24,6 +24,7 @@ from importlib import import_module
from beets.util.confit import ConfigValueError
from beets import ui
from beets.plugins import BeetsPlugin
import six
METASYNC_MODULE = 'beetsplug.metasync'
@ -35,9 +36,7 @@ SOURCES = {
}
class MetaSource(object):
__metaclass__ = ABCMeta
class MetaSource(six.with_metaclass(ABCMeta, object)):
def __init__(self, config, log):
self.item_types = {}
self.config = config

View file

@ -23,8 +23,8 @@ import os
import shutil
import tempfile
import plistlib
import urllib
from urlparse import urlparse
from six.moves.urllib.parse import urlparse, unquote
from time import mktime
from beets import util
@ -57,7 +57,7 @@ def _norm_itunes_path(path):
# E.g., '\\G:\\Music\\bar' needs to be stripped to 'G:\\Music\\bar'
return util.bytestring_path(os.path.normpath(
urllib.unquote(urlparse(path).path)).lstrip('\\')).lower()
unquote(urlparse(path).path)).lstrip('\\')).lower()
class Itunes(MetaSource):

View file

@ -27,6 +27,7 @@ from beets import plugins
from beets import library
from beets.util import displayable_path
from beets.dbcore import types
import six
# If we lose the connection, how many times do we want to retry and how
# 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
class MPDClient(mpd.MPDClient):
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)
def _read_line(self):
@ -64,14 +65,14 @@ class MPDClientWrapper(object):
self._log = log
self.music_directory = (
mpd_config['music_directory'].get(unicode))
mpd_config['music_directory'].get(six.text_type))
self.client = MPDClient()
def connect(self):
"""Connect to the MPD.
"""
host = mpd_config['host'].get(unicode)
host = mpd_config['host'].get(six.text_type)
port = mpd_config['port'].get(int)
if host[0] in ['/', '~']:
@ -83,7 +84,7 @@ class MPDClientWrapper(object):
except socket.error as 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:
try:
self.client.password(password)

View file

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

View file

@ -12,9 +12,8 @@ Put something like the following in your config.yaml to configure:
from __future__ import division, absolute_import, print_function
import requests
from urlparse import urljoin
from urllib import urlencode
import xml.etree.ElementTree as ET
from six.moves.urllib.parse import urljoin, urlencode
from beets import config
from beets.plugins import BeetsPlugin

View file

@ -18,15 +18,16 @@ from __future__ import division, absolute_import, print_function
import subprocess
import os
import collections
import itertools
import sys
import warnings
import re
from six.moves import zip
from beets import logging
from beets import ui
from beets.plugins import BeetsPlugin
from beets.util import syspath, command_output, displayable_path
import six
# Utilities.
@ -102,7 +103,7 @@ class Bs1770gainBackend(Backend):
'method': 'replaygain',
})
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'
try:
@ -256,7 +257,7 @@ class CommandBackend(Backend):
'noclip': True,
})
self.command = config["command"].get(unicode)
self.command = config["command"].get(six.text_type)
if self.command:
# Explicit executable path.
@ -809,7 +810,7 @@ class ReplayGainPlugin(BeetsPlugin):
})
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:
raise ui.UserError(
u"Selected ReplayGain backend {0} is not supported. "
@ -883,8 +884,7 @@ class ReplayGainPlugin(BeetsPlugin):
)
self.store_album_gain(album, album_gain.album_gain)
for item, track_gain in itertools.izip(album.items(),
album_gain.track_gains):
for item, track_gain in zip(album.items(), album_gain.track_gains):
self.store_track_gain(item, track_gain)
if write:
item.try_write()

View file

@ -24,6 +24,7 @@ from collections import defaultdict
from beets.plugins import BeetsPlugin
from beets import ui
from beets import library
import six
def rewriter(field, rules):
@ -51,7 +52,7 @@ class RewritePlugin(BeetsPlugin):
# Gather all the rewrite rules for each field.
rules = defaultdict(list)
for key, view in self.config.items():
value = view.get(unicode)
value = view.get(six.text_type)
try:
fieldname, pattern = key.split(None, 1)
except ValueError:
@ -68,7 +69,7 @@ class RewritePlugin(BeetsPlugin):
rules['albumartist'].append((pattern, value))
# Replace each template field with the new rewriter function.
for fieldname, fieldrules in rules.iteritems():
for fieldname, fieldrules in six.iteritems(rules):
getter = rewriter(fieldname, fieldrules)
self.template_fields[fieldname] = getter
if fieldname in library.Album._fields:

View file

@ -25,6 +25,7 @@ from beets.library import Item, Album, parse_query_string
from beets.dbcore import OrQuery
from beets.dbcore.query import MultipleSort, ParsingError
import os
import six
class SmartPlaylistPlugin(BeetsPlugin):
@ -106,7 +107,7 @@ class SmartPlaylistPlugin(BeetsPlugin):
qs = playlist.get(key)
if qs is None:
query_and_sort = None, None
elif isinstance(qs, basestring):
elif isinstance(qs, six.string_types):
query_and_sort = parse_query_string(qs, Model)
elif len(qs) == 1:
query_and_sort = parse_query_string(qs[0], Model)

View file

@ -9,6 +9,7 @@ from beets.plugins import BeetsPlugin
from beets.ui import decargs
from beets import ui
from requests.exceptions import HTTPError
import six
class SpotifyPlugin(BeetsPlugin):
@ -170,6 +171,6 @@ class SpotifyPlugin(BeetsPlugin):
else:
for item in ids:
print(unicode.encode(self.open_url + item))
print(six.text_type.encode(self.open_url + item))
else:
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
from beets.plugins import BeetsPlugin
import six
__author__ = 'baobab@heresiarch.info'
__version__ = '1.1'
@ -81,7 +82,7 @@ class ThePlugin(BeetsPlugin):
if self.config['strip']:
return r
else:
fmt = self.config['format'].get(unicode)
fmt = self.config['format'].get(six.text_type)
return fmt.format(r, t.strip()).strip()
else:
return u''

View file

@ -35,6 +35,7 @@ from beets.plugins import BeetsPlugin
from beets.ui import Subcommand, decargs
from beets import util
from beets.util.artresizer import ArtResizer, get_im_version, get_pil_version
import six
BASE_DIR = os.path.join(BaseDirectory.xdg_cache_home, "thumbnails")
@ -169,8 +170,9 @@ class ThumbnailsPlugin(BeetsPlugin):
"""Write required metadata to the thumbnail
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),
"Thumb::MTime": unicode(os.stat(album.artpath).st_mtime)}
"Thumb::MTime": six.text_type(mtime)}
try:
self.write_metadata(image_path, metadata)
except Exception:

View file

@ -25,6 +25,7 @@ from flask import g
from werkzeug.routing import BaseConverter, PathConverter
import os
import json
import six
# Utilities.
@ -321,7 +322,7 @@ class WebPlugin(BeetsPlugin):
}
CORS(app)
# 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),
debug=opts.debug, threaded=True)
cmd.func = func

View file

@ -22,6 +22,7 @@ from beets.plugins import BeetsPlugin
from beets.mediafile import MediaFile
from beets.importer import action
from beets.util import confit
import six
__author__ = 'baobab@heresiarch.info'
__version__ = '0.10'
@ -113,7 +114,7 @@ class ZeroPlugin(BeetsPlugin):
if patterns is True:
return True
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 False

View file

@ -289,7 +289,7 @@ Also note that beets may take some time to quit after ^C is typed; it
tries to clean up after itself briefly even when canceled.
(For developers: this is because the UI thread is blocking on
``raw_input`` and cannot be interrupted by the main thread, which is
``input`` and cannot be interrupted by the main thread, which is
trying to close all pipeline stages in the exception handler by setting
a flag. There is no simple way to remedy this.)

View file

@ -86,6 +86,7 @@ setup(
},
install_requires=[
'six',
'enum34>=1.0.4',
'mutagen>=1.27',
'munkres',

View file

@ -40,7 +40,7 @@ import shutil
import subprocess
from tempfile import mkdtemp, mkstemp
from contextlib import contextmanager
from StringIO import StringIO
from six import StringIO
from enum import Enum
import beets
@ -56,6 +56,7 @@ from beets import util
# TODO Move AutotagMock here
from test import _common
import six
class LogCapture(logging.Handler):
@ -65,7 +66,7 @@ class LogCapture(logging.Handler):
self.messages = []
def emit(self, record):
self.messages.append(unicode(record.msg))
self.messages.append(six.text_type(record.msg))
@contextmanager
@ -89,7 +90,8 @@ def control_stdin(input=None):
"""
org = sys.stdin
sys.stdin = StringIO(input)
sys.stdin.encoding = 'utf8'
if six.PY2: # StringIO encoding attr isn't writable in python >= 3
sys.stdin.encoding = 'utf8'
try:
yield sys.stdin
finally:
@ -108,7 +110,8 @@ def capture_stdout():
"""
org = sys.stdout
sys.stdout = capture = StringIO()
sys.stdout.encoding = 'utf8'
if six.PY2: # StringIO encoding attr isn't writable in python >= 3
sys.stdout.encoding = 'utf8'
try:
yield sys.stdout
finally:
@ -121,7 +124,7 @@ def has_program(cmd, args=['--version']):
"""
full_cmd = [cmd] + args
for i, elem in enumerate(full_cmd):
if isinstance(elem, unicode):
if isinstance(elem, six.text_type):
full_cmd[i] = elem.encode(_arg_encoding())
try:
with open(os.devnull, 'wb') as devnull:

View file

@ -29,6 +29,7 @@ from beets.library import Item
from beets.util import plurality
from beets.autotag import AlbumInfo, TrackInfo
from beets import config
import six
class PluralityTest(_common.TestCase):
@ -611,7 +612,7 @@ class AssignmentTest(unittest.TestCase):
match.assign_items(items, trackinfo)
self.assertEqual(extra_items, [])
self.assertEqual(extra_tracks, [])
for item, info in mapping.iteritems():
for item, info in six.iteritems(mapping):
self.assertEqual(items.index(item), trackinfo.index(info))

View file

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

View file

@ -20,11 +20,13 @@ from __future__ import division, absolute_import, print_function
import os
import shutil
import sqlite3
from six import assertRaisesRegex
from test import _common
from test._common import unittest
from beets import dbcore
from tempfile import mkstemp
import six
# Fixture: concrete database and model classes. For migration tests, we
@ -298,9 +300,9 @@ class ModelTest(unittest.TestCase):
self.assertNotIn('flex_field', model2)
def test_check_db_fails(self):
with self.assertRaisesRegexp(ValueError, 'no database'):
with assertRaisesRegex(self, ValueError, 'no database'):
dbcore.Model()._check_db()
with self.assertRaisesRegexp(ValueError, 'no id'):
with assertRaisesRegex(self, ValueError, 'no id'):
TestModel1(self.db)._check_db()
dbcore.Model(self.db)._check_db(need_id=False)
@ -312,7 +314,7 @@ class ModelTest(unittest.TestCase):
def test_computed_field(self):
model = TestModelWithGetters()
self.assertEqual(model.aComputedField, 'thing')
with self.assertRaisesRegexp(KeyError, u'computed field .+ deleted'):
with assertRaisesRegex(self, KeyError, u'computed field .+ deleted'):
del model.aComputedField
def test_items(self):
@ -328,7 +330,7 @@ class ModelTest(unittest.TestCase):
model._db
def test_parse_nonstring(self):
with self.assertRaisesRegexp(TypeError, u"must be a string"):
with assertRaisesRegex(self, TypeError, u"must be a string"):
dbcore.Model._parse(None, 42)
@ -349,7 +351,7 @@ class FormatTest(unittest.TestCase):
model = TestModel1()
model.other_field = u'caf\xe9'.encode('utf8')
value = model.formatted().get('other_field')
self.assertTrue(isinstance(value, unicode))
self.assertTrue(isinstance(value, six.text_type))
self.assertEqual(value, u'caf\xe9')
def test_format_unset_field(self):

View file

@ -23,6 +23,7 @@ from test.test_ui_importer import TerminalImportSessionSetup
from test.test_importer import ImportHelper, AutotagStub
from beets.library import Item
from beetsplug.edit import EditPlugin
import six
class ModifyFileMocker(object):
@ -63,7 +64,7 @@ class ModifyFileMocker(object):
"""
with codecs.open(filename, 'r', encoding='utf8') as f:
contents = f.read()
for old, new_ in self.replacements.iteritems():
for old, new_ in six.iteritems(self.replacements):
contents = contents.replace(old, new_)
with codecs.open(filename, 'w', encoding='utf8') as f:
f.write(contents)

View file

@ -14,6 +14,7 @@
# included in all copies or substantial portions of the Software.
from __future__ import division, absolute_import, print_function
import six
"""Tests for the `importadded` plugin."""
@ -124,7 +125,7 @@ class ImportAddedTest(unittest.TestCase, ImportHelper):
self.assertEqualTimes(album.added, album_added_before)
items_added_after = dict((item.path, item.added)
for item in album.items())
for item_path, added_after in items_added_after.iteritems():
for item_path, added_after in six.iteritems(items_added_after):
self.assertEqualTimes(items_added_before[item_path], added_after,
u"reimport modified Item.added for " +
util.displayable_path(item_path))
@ -162,7 +163,7 @@ class ImportAddedTest(unittest.TestCase, ImportHelper):
# Verify the reimported items
items_added_after = dict((item.path, item.added)
for item in self.lib.items())
for item_path, added_after in items_added_after.iteritems():
for item_path, added_after in six.iteritems(items_added_after):
self.assertEqualTimes(items_added_before[item_path], added_after,
u"reimport modified Item.added for " +
util.displayable_path(item_path))

View file

@ -20,9 +20,9 @@ from __future__ import division, absolute_import, print_function
import os
import re
import shutil
import StringIO
import unicodedata
import sys
from six import StringIO
from tempfile import mkstemp
from zipfile import ZipFile
from tarfile import TarFile
@ -1250,14 +1250,14 @@ class ImportDuplicateSingletonTest(unittest.TestCase, TestHelper,
class TagLogTest(_common.TestCase):
def test_tag_log_line(self):
sio = StringIO.StringIO()
sio = StringIO()
handler = logging.StreamHandler(sio)
session = _common.import_session(loghandler=handler)
session.tag_log('status', 'path')
self.assertIn('status path', sio.getvalue())
def test_tag_log_unicode(self):
sio = StringIO.StringIO()
sio = StringIO()
handler = logging.StreamHandler(sio)
session = _common.import_session(loghandler=handler)
session.tag_log('status', u'caf\xe9') # send unicode

View file

@ -25,6 +25,7 @@ from beetsplug import lastgenre
from beets import config
from test.helper import TestHelper
import six
class LastGenrePluginTest(unittest.TestCase, TestHelper):
@ -38,11 +39,11 @@ class LastGenrePluginTest(unittest.TestCase, TestHelper):
def _setup_config(self, whitelist=False, canonical=False, count=1):
config['lastgenre']['canonical'] = canonical
config['lastgenre']['count'] = count
if isinstance(whitelist, (bool, basestring)):
if isinstance(whitelist, (bool, six.string_types)):
# Filename, default, or disabled.
config['lastgenre']['whitelist'] = whitelist
self.plugin.setup()
if not isinstance(whitelist, (bool, basestring)):
if not isinstance(whitelist, (bool, six.string_types)):
# Explicit list of genres.
self.plugin.whitelist = whitelist

View file

@ -38,6 +38,7 @@ from beets import config
from beets.mediafile import MediaFile
from beets.util import syspath, bytestring_path
from test.helper import TestHelper
import six
# Shortcut to path normalization.
np = util.normpath
@ -964,7 +965,7 @@ class PathStringTest(_common.TestCase):
def test_sanitize_path_returns_unicode(self):
path = u'b\xe1r?'
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):
alb = self.lib.add_album([self.i])
@ -1051,7 +1052,7 @@ class TemplateTest(_common.LibTestCase):
album.tagada = u'togodo'
self.assertEqual(u"{0}".format(album), u"foö bar")
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")
config['format_item'] = 'bar $foo'
@ -1174,7 +1175,8 @@ class LibraryFieldTypesTest(unittest.TestCase):
t = beets.library.DateType()
# 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))
self.assertEqual(time_local, t.format(123456789))
# parse

View file

@ -6,7 +6,7 @@ from __future__ import division, absolute_import, print_function
import sys
import threading
import logging as log
from StringIO import StringIO
from six import StringIO
import beets.logging as blog
from beets import plugins, ui
@ -14,6 +14,7 @@ import beetsplug
from test import _common
from test._common import unittest, TestCase
from test import helper
import six
class LoggingTest(TestCase):
@ -218,7 +219,7 @@ class ConcurrentEventsTest(TestCase, helper.TestHelper):
def check_dp_exc():
if dp.exc_info:
raise dp.exc_info[1], None, dp.exc_info[2]
six.reraise(dp.exc_info[1], None, dp.exc_info[2])
try:
dp.lock1.acquire()

View file

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

View file

@ -23,6 +23,7 @@ import shutil
import tempfile
import datetime
import time
from six import assertCountEqual
from test import _common
from test._common import unittest
@ -32,6 +33,7 @@ from beets.mediafile import MediaFile, MediaField, Image, \
from beets.library import Item
from beets.plugins import BeetsPlugin
from beets.util import bytestring_path
import six
class ArtTestMixin(object):
@ -268,7 +270,7 @@ class GenreListTestMixin(object):
def test_read_genre_list(self):
mediafile = self._mediafile_fixture('full')
self.assertItemsEqual(mediafile.genres, ['the genre'])
assertCountEqual(self, mediafile.genres, ['the genre'])
def test_write_genre_list(self):
mediafile = self._mediafile_fixture('empty')
@ -276,7 +278,7 @@ class GenreListTestMixin(object):
mediafile.save()
mediafile = MediaFile(mediafile.path)
self.assertItemsEqual(mediafile.genres, [u'one', u'two'])
assertCountEqual(self, mediafile.genres, [u'one', u'two'])
def test_write_genre_list_get_first(self):
mediafile = self._mediafile_fixture('empty')
@ -293,7 +295,7 @@ class GenreListTestMixin(object):
mediafile.save()
mediafile = MediaFile(mediafile.path)
self.assertItemsEqual(mediafile.genres, [u'the genre', u'another'])
assertCountEqual(self, mediafile.genres, [u'the genre', u'another'])
field_extension = MediaField(
@ -352,13 +354,13 @@ class ExtendedFieldTestMixin(object):
with self.assertRaises(ValueError) as cm:
MediaFile.add_field('somekey', True)
self.assertIn(u'must be an instance of MediaField',
unicode(cm.exception))
six.text_type(cm.exception))
def test_overwrite_property(self):
with self.assertRaises(ValueError) as cm:
MediaFile.add_field('artist', MediaField())
self.assertIn(u'property "artist" already exists',
unicode(cm.exception))
six.text_type(cm.exception))
class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin,
@ -949,7 +951,7 @@ class MediaFieldTest(unittest.TestCase):
def test_known_fields(self):
fields = list(ReadWriteTestBase.tag_fields)
fields.extend(('encoder', 'images', 'genres', 'albumtype'))
self.assertItemsEqual(MediaFile.fields(), fields)
assertCountEqual(self, MediaFile.fields(), fields)
def test_fields_in_readable_fields(self):
readable = MediaFile.readable_fields()

View file

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

View file

@ -17,6 +17,8 @@
"""
from __future__ import division, absolute_import, print_function
import six
from test._common import unittest
from beets.util import pipeline
@ -134,7 +136,10 @@ class ExceptionTest(unittest.TestCase):
pull = pl.pull()
for i in range(3):
next(pull)
self.assertRaises(TestException, pull.next)
if six.PY2:
self.assertRaises(TestException, pull.next)
else:
self.assertRaises(TestException, pull.__next__)
class ParallelExceptionTest(unittest.TestCase):
@ -157,6 +162,7 @@ class ConstrainedThreadedPipelineTest(unittest.TestCase):
pl.run_parallel(1)
self.assertEqual(l, [i * 2 for i in range(1000)])
@unittest.skipIf(six.PY3, u'freezes the test suite in py3')
def test_constrained_exception(self):
# Raise an exception in a constrained pipeline.
l = []

View file

@ -34,6 +34,7 @@ from beets.dbcore.query import (NoneQuery, ParsingError,
from beets.library import Library, Item
from beets import util
import platform
import six
class TestHelper(helper.TestHelper):
@ -302,11 +303,11 @@ class GetTest(DummyDataTestCase):
def test_invalid_query(self):
with self.assertRaises(InvalidQueryArgumentTypeError) as raised:
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:
dbcore.query.RegexpQuery('year', u'199(')
exception_text = unicode(raised.exception)
exception_text = six.text_type(raised.exception)
self.assertIn(u'not a regular expression', exception_text)
if sys.version_info >= (3, 5):
self.assertIn(u'unterminated subpattern', exception_text)

View file

@ -23,6 +23,7 @@ from beets import config
from beets.mediafile import MediaFile
from beetsplug.replaygain import (FatalGstreamerPluginReplayGainError,
GStreamerBackend)
import six
try:
import gi
@ -62,7 +63,7 @@ class ReplayGainCliTestBase(TestHelper):
# teardown operations may fail. In particular # {Item,Album}
# may not have the _original_types attribute in unload_plugins
pass
raise exc_info[1], None, exc_info[2]
six.reraise(exc_info[1], None, exc_info[2])
album = self.add_album_fixture(2)
for item in album.items():

View file

@ -13,7 +13,7 @@ from beets import config
from beets.library import Item
from beetsplug import spotify
from test.helper import TestHelper
import urlparse
from six.moves.urllib.parse import parse_qs, urlparse
class ArgumentsMock(object):
@ -25,7 +25,7 @@ class ArgumentsMock(object):
def _params(url):
"""Get the query parameters from a URL."""
return urlparse.parse_qs(urlparse.urlparse(url).query)
return parse_qs(urlparse(url).query)
class SpotifyPluginTest(_common.TestCase, TestHelper):

View file

@ -21,6 +21,7 @@ import warnings
from test._common import unittest
from beets.util import functemplate
import six
def _normexpr(expr):
@ -30,7 +31,7 @@ def _normexpr(expr):
"""
textbuf = []
for part in expr.parts:
if isinstance(part, basestring):
if isinstance(part, six.string_types):
textbuf.append(part)
else:
if textbuf:
@ -227,7 +228,7 @@ class EvalTest(unittest.TestCase):
u'baz': u'BaR',
}
functions = {
u'lower': unicode.lower,
u'lower': six.text_type.lower,
u'len': len,
}
return functemplate.Template(template).substitute(values, functions)
@ -258,7 +259,7 @@ class EvalTest(unittest.TestCase):
def test_function_call_exception(self):
res = self._eval(u"%lower{a,b,c,d,e}")
self.assertTrue(isinstance(res, basestring))
self.assertTrue(isinstance(res, six.string_types))
def test_function_returning_integer(self):
self.assertEqual(self._eval(u"%len{foo}"), u"3")

View file

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

View file

@ -26,6 +26,7 @@ from test import test_importer
from beets.ui.commands import TerminalImportSession
from beets import importer
from beets import config
import six
class TestTerminalImportSession(TerminalImportSession):
@ -69,7 +70,7 @@ class TestTerminalImportSession(TerminalImportSession):
self.io.addinput(u'S')
elif isinstance(choice, int):
self.io.addinput(u'M')
self.io.addinput(unicode(choice))
self.io.addinput(six.text_type(choice))
self._add_choice_input()
else:
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 import _common
from beets import util
import six
class UtilTest(unittest.TestCase):
@ -122,7 +123,7 @@ class PathConversionTest(_common.TestCase):
with _common.platform_windows():
path = os.path.join(u'a', u'b', u'c')
outpath = util.syspath(path)
self.assertTrue(isinstance(outpath, unicode))
self.assertTrue(isinstance(outpath, six.text_type))
self.assertTrue(outpath.startswith(u'\\\\?\\'))
def test_syspath_windows_format_unc_path(self):
@ -131,7 +132,7 @@ class PathConversionTest(_common.TestCase):
path = '\\\\server\\share\\file.mp3'
with _common.platform_windows():
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')
def test_syspath_posix_unchanged(self):

View file

@ -4,6 +4,8 @@
from __future__ import division, absolute_import, print_function
from six import assertCountEqual
from test._common import unittest
from test import _common
import json
@ -52,7 +54,7 @@ class WebPluginTest(_common.LibTestCase):
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.json['items']), 2)
response_titles = [item['title'] for item in response.json['items']]
self.assertItemsEqual(response_titles, [u'title', u'another title'])
assertCountEqual(self, response_titles, [u'title', u'another title'])
def test_get_single_item_not_found(self):
response = self.client.get('/item/3')
@ -80,7 +82,7 @@ class WebPluginTest(_common.LibTestCase):
self.assertEqual(response.status_code, 200)
response_albums = [album['album'] for album in response.json['albums']]
self.assertItemsEqual(response_albums, [u'album', u'another album'])
assertCountEqual(self, response_albums, [u'album', u'another album'])
def test_get_single_album_by_id(self):
response = self.client.get('/album/2')
@ -96,7 +98,7 @@ class WebPluginTest(_common.LibTestCase):
self.assertEqual(response.status_code, 200)
response_albums = [album['album'] for album in response.json['albums']]
self.assertItemsEqual(response_albums, [u'album', u'another album'])
assertCountEqual(self, response_albums, [u'album', u'another album'])
def test_get_album_empty_query(self):
response = self.client.get('/album/query/')