diff --git a/beets/__init__.py b/beets/__init__.py index be95bdcb4..8072d793b 100644 --- a/beets/__init__.py +++ b/beets/__init__.py @@ -12,6 +12,8 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import absolute_import, unicode_literals + __version__ = '1.3.11' __author__ = 'Adrian Sampson ' diff --git a/beets/autotag/__init__.py b/beets/autotag/__init__.py index 7ed7ce6bc..fab0fbbae 100644 --- a/beets/autotag/__init__.py +++ b/beets/autotag/__init__.py @@ -15,6 +15,9 @@ """Facilities for automatically determining files' correct metadata. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from beets import logging from beets import config diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 6b592e1de..c8e068e62 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -13,6 +13,9 @@ # included in all copies or substantial portions of the Software. """Glue between metadata sources and the matching logic.""" +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from collections import namedtuple import re @@ -109,7 +112,7 @@ class AlbumInfo(object): 'catalognum', 'script', 'language', 'country', 'albumstatus', 'albumdisambig', 'artist_credit', 'media']: value = getattr(self, fld) - if isinstance(value, str): + if isinstance(value, bytes): setattr(self, fld, value.decode(codec, 'ignore')) if self.tracks: @@ -168,7 +171,7 @@ class TrackInfo(object): for fld in ['title', 'artist', 'medium', 'artist_sort', 'disctitle', 'artist_credit', 'media']: value = getattr(self, fld) - if isinstance(value, str): + if isinstance(value, bytes): setattr(self, fld, value.decode(codec, 'ignore')) diff --git a/beets/autotag/match.py b/beets/autotag/match.py index ea80ae111..ed02cdf6b 100644 --- a/beets/autotag/match.py +++ b/beets/autotag/match.py @@ -15,7 +15,9 @@ """Matches existing metadata with canonical information to identify releases and tracks. """ -from __future__ import division + +from __future__ import (division, absolute_import, print_function, + unicode_literals) import datetime import re @@ -405,7 +407,7 @@ def tag_album(items, search_artist=None, search_album=None, if id_info: _add_candidate(items, candidates, id_info) rec = _recommendation(candidates.values()) - log.debug(u'Album ID match recommendation is {0}', str(rec)) + log.debug(u'Album ID match recommendation is {0}', rec) if candidates and not config['import']['timid']: # If we have a very good MBID match, return immediately. # Otherwise, this match will compete against metadata-based @@ -424,7 +426,7 @@ def tag_album(items, search_artist=None, search_album=None, va_likely = ((not consensus['artist']) or (search_artist.lower() in VA_ARTISTS) or any(item.comp for item in items)) - log.debug(u'Album might be VA: {0}', str(va_likely)) + log.debug(u'Album might be VA: {0}', va_likely) # Get the results from the data sources. search_cands = hooks.album_candidates(items, search_artist, diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index 6889eeaf6..1cf92ce5d 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -14,6 +14,9 @@ """Searches for albums in the MusicBrainz database. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import musicbrainzngs import re import traceback @@ -316,7 +319,7 @@ def match_album(artist, album, tracks=None): # Various Artists search. criteria['arid'] = VARIOUS_ARTISTS_ID if tracks is not None: - criteria['tracks'] = str(tracks) + criteria['tracks'] = bytes(tracks) # Abort if we have no search terms. if not any(criteria.itervalues()): diff --git a/beets/dbcore/__init__.py b/beets/dbcore/__init__.py index d08bd5013..100f546b5 100644 --- a/beets/dbcore/__init__.py +++ b/beets/dbcore/__init__.py @@ -15,6 +15,8 @@ """DBCore is an abstract database package that forms the basis for beets' Library. """ +from __future__ import absolute_import + from .db import Model, Database from .query import Query, FieldQuery, MatchQuery, AndQuery, OrQuery from .types import Type diff --git a/beets/dbcore/db.py b/beets/dbcore/db.py index 7017e3e62..6fb259607 100644 --- a/beets/dbcore/db.py +++ b/beets/dbcore/db.py @@ -14,6 +14,9 @@ """The central Model and Database constructs for DBCore. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import time import os from collections import defaultdict @@ -545,13 +548,13 @@ class Results(object): 'SELECT * FROM {0} WHERE entity_id=?'.format( self.model_class._flex_table ), - (row['id'],) + (row[b'id'],) ) cols = dict(row) values = dict((k, v) for (k, v) in cols.items() if not k[:4] == 'flex') - flex_values = dict((row['key'], row['value']) for row in flex_rows) + flex_values = dict((row[b'key'], row[b'value']) for row in flex_rows) # Construct the Python object obj = self.model_class._awaken(self.db, values, flex_values) diff --git a/beets/dbcore/query.py b/beets/dbcore/query.py index bfe6ea6d7..8379b725a 100644 --- a/beets/dbcore/query.py +++ b/beets/dbcore/query.py @@ -14,6 +14,9 @@ """The Query type hierarchy for DBCore. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import re from operator import attrgetter from beets import util @@ -178,7 +181,7 @@ class BooleanQuery(MatchQuery): class BytesQuery(MatchQuery): """Match a raw bytes field (i.e., a path). This is a necessary hack - to work around the `sqlite3` module's desire to treat `str` and + to work around the `sqlite3` module's desire to treat `bytes` and `unicode` equivalently in Python 2. Always use this query instead of `MatchQuery` when matching on BLOB values. """ @@ -423,7 +426,7 @@ class Period(object): precision (a string, one of "year", "month", or "day"). """ if precision not in Period.precisions: - raise ValueError('Invalid precision ' + str(precision)) + raise ValueError('Invalid precision {0}'.format(precision)) self.date = date self.precision = precision @@ -463,7 +466,7 @@ class Period(object): elif 'day' == precision: return date + timedelta(days=1) else: - raise ValueError('unhandled precision ' + str(precision)) + raise ValueError('unhandled precision {0}'.format(precision)) class DateInterval(object): diff --git a/beets/dbcore/queryparse.py b/beets/dbcore/queryparse.py index 89a6f5ca2..6628bebf0 100644 --- a/beets/dbcore/queryparse.py +++ b/beets/dbcore/queryparse.py @@ -14,6 +14,8 @@ """Parsing of strings into DBCore queries. """ +from __future__ import division, absolute_import, print_function + import re import itertools from . import query diff --git a/beets/dbcore/types.py b/beets/dbcore/types.py index c171a9310..90c1d7c2e 100644 --- a/beets/dbcore/types.py +++ b/beets/dbcore/types.py @@ -14,6 +14,9 @@ """Representation of type information for DBCore model fields. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from . import query from beets.util import str2bool diff --git a/beets/importer.py b/beets/importer.py index a00021eee..f7d6aedba 100644 --- a/beets/importer.py +++ b/beets/importer.py @@ -12,10 +12,12 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + """Provides the basic, interface-agnostic workflow for importing and autotagging music files. """ -from __future__ import print_function import os import re diff --git a/beets/library.py b/beets/library.py index df6b6acc6..cb46d99c3 100644 --- a/beets/library.py +++ b/beets/library.py @@ -14,6 +14,9 @@ """The core data store and collection logic for beets. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os import sys import shlex @@ -42,7 +45,7 @@ class PathQuery(dbcore.FieldQuery): """A query that matches all items under a given path.""" escape_re = re.compile(r'[\\_%]') - escape_char = '\\' + escape_char = b'\\' def __init__(self, field, pattern, fast=True): super(PathQuery, self).__init__(field, pattern, fast) @@ -50,7 +53,7 @@ class PathQuery(dbcore.FieldQuery): # Match the path as a single file. self.file_path = util.bytestring_path(util.normpath(pattern)) # As a directory (prefix). - self.dir_path = util.bytestring_path(os.path.join(self.file_path, '')) + self.dir_path = util.bytestring_path(os.path.join(self.file_path, b'')) def match(self, item): return (item.path == self.file_path) or \ @@ -59,7 +62,7 @@ class PathQuery(dbcore.FieldQuery): def clause(self): escape = lambda m: self.escape_char + m.group(0) dir_pattern = self.escape_re.sub(escape, self.dir_path) - dir_pattern = buffer(dir_pattern + '%') + dir_pattern = buffer(dir_pattern + b'%') file_blob = buffer(self.file_path) return '({0} = ?) || ({0} LIKE ? ESCAPE ?)'.format(self.field), \ (file_blob, dir_pattern, self.escape_char) @@ -118,7 +121,7 @@ class PathType(types.Type): return self.normalize(sql_value) def to_sql(self, value): - if isinstance(value, str): + if isinstance(value, bytes): value = buffer(value) return value @@ -390,7 +393,7 @@ class Item(LibModel): _search_fields = ('artist', 'title', 'comments', 'album', 'albumartist', 'genre') - _media_fields = set(MediaFile.readable_fields()) \ + _media_fields = set(f.decode('utf8') for f in MediaFile.readable_fields()) \ .intersection(_fields.keys()) """Set of item fields that are backed by `MediaFile` fields. @@ -429,7 +432,7 @@ class Item(LibModel): if isinstance(value, unicode): value = bytestring_path(value) elif isinstance(value, buffer): - value = str(value) + value = bytes(value) if key in MediaFile.fields(): self.mtime = 0 # Reset mtime on dirty. @@ -532,7 +535,7 @@ class Item(LibModel): self.write(path) return True except FileOperationError as exc: - log.error(str(exc)) + log.error("{0}", exc) return False def try_sync(self, write=None): @@ -1196,7 +1199,7 @@ class DefaultTemplateFunctions(object): additional context to the functions -- specifically, the Item being evaluated. """ - _prefix = 'tmpl_' + _prefix = b'tmpl_' def __init__(self, item=None, lib=None): """Paramaterize the functions. If `item` or `lib` is None, then diff --git a/beets/logging.py b/beets/logging.py index 31b12fbb3..c03055856 100644 --- a/beets/logging.py +++ b/beets/logging.py @@ -20,7 +20,9 @@ that when getLogger(name) instantiates a logger that logger uses {}-style formatting. """ -from __future__ import absolute_import +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from copy import copy from logging import * # noqa import sys diff --git a/beets/mediafile.py b/beets/mediafile.py index c820dcb3b..9c55f582e 100644 --- a/beets/mediafile.py +++ b/beets/mediafile.py @@ -32,6 +32,9 @@ Internally ``MediaFile`` uses ``MediaField`` descriptors to access the data from the tags. In turn ``MediaField`` uses a number of ``StorageStyle`` strategies to handle format specific logic. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import mutagen import mutagen.mp3 import mutagen.oggopus @@ -141,7 +144,7 @@ def _safe_cast(out_type, val): return False elif out_type == unicode: - if isinstance(val, str): + if isinstance(val, bytes): return val.decode('utf8', 'ignore') elif isinstance(val, unicode): return val @@ -176,15 +179,15 @@ def _unpack_asf_image(data): of exceptions (out-of-bounds, etc.). We should clean this up sometime so that the failure modes are well-defined. """ - type, size = struct.unpack_from(" 0 else None image_data = frame.value[text_delimiter_index + 1:] @@ -1011,7 +1015,7 @@ class APEv2ImageStorageStyle(ListStorageStyle): for image in values: image_type = image.type or ImageType.other comment = image.desc or '' - image_data = comment + "\x00" + image.data + image_data = comment.encode('utf8') + b'\x00' + image.data cover_tag = self.TAG_NAMES[image_type] mutagen_file[cover_tag] = image_data @@ -1045,7 +1049,7 @@ class MediaField(object): getting this property. """ - self.out_type = kwargs.get('out_type', unicode) + self.out_type = kwargs.get(b'out_type', unicode) self._styles = styles def styles(self, mutagen_file): @@ -1109,7 +1113,7 @@ class ListMediaField(MediaField): """Returns a ``MediaField`` descriptor that gets and sets the first item. """ - options = {'out_type': self.out_type} + options = {b'out_type': self.out_type} return MediaField(*self._styles, **options) @@ -1130,7 +1134,7 @@ class DateField(MediaField): storage styles do not return a value. """ super(DateField, self).__init__(*date_styles) - year_style = kwargs.get('year', None) + year_style = kwargs.get(b'year', None) if year_style: self._year_field = MediaField(*year_style) @@ -1476,25 +1480,25 @@ class MediaFile(object): title = MediaField( MP3StorageStyle('TIT2'), - MP4StorageStyle("\xa9nam"), + MP4StorageStyle(b"\xa9nam"), StorageStyle('TITLE'), ASFStorageStyle('Title'), ) artist = MediaField( MP3StorageStyle('TPE1'), - MP4StorageStyle("\xa9ART"), + MP4StorageStyle(b"\xa9ART"), StorageStyle('ARTIST'), ASFStorageStyle('Author'), ) album = MediaField( MP3StorageStyle('TALB'), - MP4StorageStyle("\xa9alb"), + MP4StorageStyle(b"\xa9alb"), StorageStyle('ALBUM'), ASFStorageStyle('WM/AlbumTitle'), ) genres = ListMediaField( MP3ListStorageStyle('TCON'), - MP4ListStorageStyle("\xa9gen"), + MP4ListStorageStyle(b"\xa9gen"), ListStorageStyle('GENRE'), ASFStorageStyle('WM/Genre'), ) @@ -1502,19 +1506,19 @@ class MediaFile(object): composer = MediaField( MP3StorageStyle('TCOM'), - MP4StorageStyle("\xa9wrt"), + MP4StorageStyle(b"\xa9wrt"), StorageStyle('COMPOSER'), ASFStorageStyle('WM/Composer'), ) grouping = MediaField( MP3StorageStyle('TIT1'), - MP4StorageStyle("\xa9grp"), + MP4StorageStyle(b"\xa9grp"), StorageStyle('GROUPING'), ASFStorageStyle('WM/ContentGroupDescription'), ) track = MediaField( MP3SlashPackStorageStyle('TRCK', pack_pos=0), - MP4TupleStorageStyle('trkn', index=0), + MP4TupleStorageStyle(b'trkn', index=0), StorageStyle('TRACK'), StorageStyle('TRACKNUMBER'), ASFStorageStyle('WM/TrackNumber'), @@ -1522,7 +1526,7 @@ class MediaFile(object): ) tracktotal = MediaField( MP3SlashPackStorageStyle('TRCK', pack_pos=1), - MP4TupleStorageStyle('trkn', index=1), + MP4TupleStorageStyle(b'trkn', index=1), StorageStyle('TRACKTOTAL'), StorageStyle('TRACKC'), StorageStyle('TOTALTRACKS'), @@ -1531,7 +1535,7 @@ class MediaFile(object): ) disc = MediaField( MP3SlashPackStorageStyle('TPOS', pack_pos=0), - MP4TupleStorageStyle('disk', index=0), + MP4TupleStorageStyle(b'disk', index=0), StorageStyle('DISC'), StorageStyle('DISCNUMBER'), ASFStorageStyle('WM/PartOfSet'), @@ -1539,7 +1543,7 @@ class MediaFile(object): ) disctotal = MediaField( MP3SlashPackStorageStyle('TPOS', pack_pos=1), - MP4TupleStorageStyle('disk', index=1), + MP4TupleStorageStyle(b'disk', index=1), StorageStyle('DISCTOTAL'), StorageStyle('DISCC'), StorageStyle('TOTALDISCS'), @@ -1548,13 +1552,13 @@ class MediaFile(object): ) lyrics = MediaField( MP3DescStorageStyle(key='USLT'), - MP4StorageStyle("\xa9lyr"), + MP4StorageStyle(b"\xa9lyr"), StorageStyle('LYRICS'), ASFStorageStyle('WM/Lyrics'), ) comments = MediaField( MP3DescStorageStyle(key='COMM'), - MP4StorageStyle("\xa9cmt"), + MP4StorageStyle(b"\xa9cmt"), StorageStyle('DESCRIPTION'), StorageStyle('COMMENT'), ASFStorageStyle('WM/Comments'), @@ -1562,111 +1566,111 @@ class MediaFile(object): ) bpm = MediaField( MP3StorageStyle('TBPM'), - MP4StorageStyle('tmpo', as_type=int), + MP4StorageStyle(b'tmpo', as_type=int), StorageStyle('BPM'), ASFStorageStyle('WM/BeatsPerMinute'), out_type=int, ) comp = MediaField( MP3StorageStyle('TCMP'), - MP4BoolStorageStyle('cpil'), + MP4BoolStorageStyle(b'cpil'), StorageStyle('COMPILATION'), ASFStorageStyle('WM/IsCompilation', as_type=bool), out_type=bool, ) albumartist = MediaField( MP3StorageStyle('TPE2'), - MP4StorageStyle('aART'), + MP4StorageStyle(b'aART'), StorageStyle('ALBUM ARTIST'), StorageStyle('ALBUMARTIST'), ASFStorageStyle('WM/AlbumArtist'), ) albumtype = MediaField( MP3DescStorageStyle(u'MusicBrainz Album Type'), - MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Album Type'), + MP4StorageStyle(b'----:com.apple.iTunes:MusicBrainz Album Type'), StorageStyle('MUSICBRAINZ_ALBUMTYPE'), ASFStorageStyle('MusicBrainz/Album Type'), ) label = MediaField( MP3StorageStyle('TPUB'), - MP4StorageStyle('----:com.apple.iTunes:Label'), - MP4StorageStyle('----:com.apple.iTunes:publisher'), + MP4StorageStyle(b'----:com.apple.iTunes:Label'), + MP4StorageStyle(b'----:com.apple.iTunes:publisher'), StorageStyle('LABEL'), StorageStyle('PUBLISHER'), # Traktor ASFStorageStyle('WM/Publisher'), ) artist_sort = MediaField( MP3StorageStyle('TSOP'), - MP4StorageStyle("soar"), + MP4StorageStyle(b"soar"), StorageStyle('ARTISTSORT'), ASFStorageStyle('WM/ArtistSortOrder'), ) albumartist_sort = MediaField( MP3DescStorageStyle(u'ALBUMARTISTSORT'), - MP4StorageStyle("soaa"), + MP4StorageStyle(b"soaa"), StorageStyle('ALBUMARTISTSORT'), ASFStorageStyle('WM/AlbumArtistSortOrder'), ) asin = MediaField( MP3DescStorageStyle(u'ASIN'), - MP4StorageStyle("----:com.apple.iTunes:ASIN"), + MP4StorageStyle(b"----:com.apple.iTunes:ASIN"), StorageStyle('ASIN'), ASFStorageStyle('MusicBrainz/ASIN'), ) catalognum = MediaField( MP3DescStorageStyle(u'CATALOGNUMBER'), - MP4StorageStyle("----:com.apple.iTunes:CATALOGNUMBER"), + MP4StorageStyle(b"----:com.apple.iTunes:CATALOGNUMBER"), StorageStyle('CATALOGNUMBER'), ASFStorageStyle('WM/CatalogNo'), ) disctitle = MediaField( MP3StorageStyle('TSST'), - MP4StorageStyle("----:com.apple.iTunes:DISCSUBTITLE"), + MP4StorageStyle(b"----:com.apple.iTunes:DISCSUBTITLE"), StorageStyle('DISCSUBTITLE'), ASFStorageStyle('WM/SetSubTitle'), ) encoder = MediaField( MP3StorageStyle('TENC'), - MP4StorageStyle("\xa9too"), + MP4StorageStyle(b"\xa9too"), StorageStyle('ENCODEDBY'), StorageStyle('ENCODER'), ASFStorageStyle('WM/EncodedBy'), ) script = MediaField( MP3DescStorageStyle(u'Script'), - MP4StorageStyle("----:com.apple.iTunes:SCRIPT"), + MP4StorageStyle(b"----:com.apple.iTunes:SCRIPT"), StorageStyle('SCRIPT'), ASFStorageStyle('WM/Script'), ) language = MediaField( MP3StorageStyle('TLAN'), - MP4StorageStyle("----:com.apple.iTunes:LANGUAGE"), + MP4StorageStyle(b"----:com.apple.iTunes:LANGUAGE"), StorageStyle('LANGUAGE'), ASFStorageStyle('WM/Language'), ) country = MediaField( MP3DescStorageStyle('MusicBrainz Album Release Country'), - MP4StorageStyle("----:com.apple.iTunes:MusicBrainz " - "Album Release Country"), + MP4StorageStyle(b"----:com.apple.iTunes:MusicBrainz " + b"Album Release Country"), StorageStyle('RELEASECOUNTRY'), ASFStorageStyle('MusicBrainz/Album Release Country'), ) albumstatus = MediaField( MP3DescStorageStyle(u'MusicBrainz Album Status'), - MP4StorageStyle("----:com.apple.iTunes:MusicBrainz Album Status"), + MP4StorageStyle(b"----:com.apple.iTunes:MusicBrainz Album Status"), StorageStyle('MUSICBRAINZ_ALBUMSTATUS'), ASFStorageStyle('MusicBrainz/Album Status'), ) media = MediaField( MP3StorageStyle('TMED'), - MP4StorageStyle("----:com.apple.iTunes:MEDIA"), + MP4StorageStyle(b"----:com.apple.iTunes:MEDIA"), StorageStyle('MEDIA'), ASFStorageStyle('WM/Media'), ) albumdisambig = MediaField( # This tag mapping was invented for beets (not used by Picard, etc). MP3DescStorageStyle(u'MusicBrainz Album Comment'), - MP4StorageStyle("----:com.apple.iTunes:MusicBrainz Album Comment"), + MP4StorageStyle(b"----:com.apple.iTunes:MusicBrainz Album Comment"), StorageStyle('MUSICBRAINZ_ALBUMCOMMENT'), ASFStorageStyle('MusicBrainz/Album Comment'), ) @@ -1674,7 +1678,7 @@ class MediaFile(object): # Release date. date = DateField( MP3StorageStyle('TDRC'), - MP4StorageStyle("\xa9day"), + MP4StorageStyle(b"\xa9day"), StorageStyle('DATE'), ASFStorageStyle('WM/Year'), year=(StorageStyle('YEAR'),)) @@ -1686,7 +1690,7 @@ class MediaFile(object): # *Original* release date. original_date = DateField( MP3StorageStyle('TDOR'), - MP4StorageStyle('----:com.apple.iTunes:ORIGINAL YEAR'), + MP4StorageStyle(b'----:com.apple.iTunes:ORIGINAL YEAR'), StorageStyle('ORIGINALDATE'), ASFStorageStyle('WM/OriginalReleaseYear')) @@ -1697,13 +1701,13 @@ class MediaFile(object): # Nonstandard metadata. artist_credit = MediaField( MP3DescStorageStyle(u'Artist Credit'), - MP4StorageStyle("----:com.apple.iTunes:Artist Credit"), + MP4StorageStyle(b"----:com.apple.iTunes:Artist Credit"), StorageStyle('ARTIST_CREDIT'), ASFStorageStyle('beets/Artist Credit'), ) albumartist_credit = MediaField( MP3DescStorageStyle(u'Album Artist Credit'), - MP4StorageStyle("----:com.apple.iTunes:Album Artist Credit"), + MP4StorageStyle(b"----:com.apple.iTunes:Album Artist Credit"), StorageStyle('ALBUMARTIST_CREDIT'), ASFStorageStyle('beets/Album Artist Credit'), ) @@ -1717,31 +1721,31 @@ class MediaFile(object): # MusicBrainz IDs. mb_trackid = MediaField( MP3UFIDStorageStyle(owner='http://musicbrainz.org'), - MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Track Id'), + MP4StorageStyle(b'----:com.apple.iTunes:MusicBrainz Track Id'), StorageStyle('MUSICBRAINZ_TRACKID'), ASFStorageStyle('MusicBrainz/Track Id'), ) mb_albumid = MediaField( MP3DescStorageStyle(u'MusicBrainz Album Id'), - MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Album Id'), + MP4StorageStyle(b'----:com.apple.iTunes:MusicBrainz Album Id'), StorageStyle('MUSICBRAINZ_ALBUMID'), ASFStorageStyle('MusicBrainz/Album Id'), ) mb_artistid = MediaField( MP3DescStorageStyle(u'MusicBrainz Artist Id'), - MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Artist Id'), + MP4StorageStyle(b'----:com.apple.iTunes:MusicBrainz Artist Id'), StorageStyle('MUSICBRAINZ_ARTISTID'), ASFStorageStyle('MusicBrainz/Artist Id'), ) mb_albumartistid = MediaField( MP3DescStorageStyle(u'MusicBrainz Album Artist Id'), - MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Album Artist Id'), + MP4StorageStyle(b'----:com.apple.iTunes:MusicBrainz Album Artist Id'), StorageStyle('MUSICBRAINZ_ALBUMARTISTID'), ASFStorageStyle('MusicBrainz/Album Artist Id'), ) mb_releasegroupid = MediaField( MP3DescStorageStyle(u'MusicBrainz Release Group Id'), - MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Release Group Id'), + MP4StorageStyle(b'----:com.apple.iTunes:MusicBrainz Release Group Id'), StorageStyle('MUSICBRAINZ_RELEASEGROUPID'), ASFStorageStyle('MusicBrainz/Release Group Id'), ) @@ -1749,13 +1753,13 @@ class MediaFile(object): # Acoustid fields. acoustid_fingerprint = MediaField( MP3DescStorageStyle(u'Acoustid Fingerprint'), - MP4StorageStyle('----:com.apple.iTunes:Acoustid Fingerprint'), + MP4StorageStyle(b'----:com.apple.iTunes:Acoustid Fingerprint'), StorageStyle('ACOUSTID_FINGERPRINT'), ASFStorageStyle('Acoustid/Fingerprint'), ) acoustid_id = MediaField( MP3DescStorageStyle(u'Acoustid Id'), - MP4StorageStyle('----:com.apple.iTunes:Acoustid Id'), + MP4StorageStyle(b'----:com.apple.iTunes:Acoustid Id'), StorageStyle('ACOUSTID_ID'), ASFStorageStyle('Acoustid/Id'), ) @@ -1776,11 +1780,11 @@ class MediaFile(object): id3_lang='eng' ), MP4StorageStyle( - '----:com.apple.iTunes:replaygain_track_gain', + b'----:com.apple.iTunes:replaygain_track_gain', float_places=2, suffix=b' dB' ), MP4SoundCheckStorageStyle( - '----:com.apple.iTunes:iTunNORM', + b'----:com.apple.iTunes:iTunNORM', index=0 ), StorageStyle( @@ -1803,7 +1807,7 @@ class MediaFile(object): float_places=2, suffix=u' dB' ), MP4SoundCheckStorageStyle( - '----:com.apple.iTunes:iTunNORM', + b'----:com.apple.iTunes:iTunNORM', index=1 ), StorageStyle( @@ -1831,11 +1835,11 @@ class MediaFile(object): id3_lang='eng' ), MP4StorageStyle( - '----:com.apple.iTunes:replaygain_track_peak', + b'----:com.apple.iTunes:replaygain_track_peak', float_places=6 ), MP4SoundCheckStorageStyle( - '----:com.apple.iTunes:iTunNORM', + b'----:com.apple.iTunes:iTunNORM', index=1 ), StorageStyle(u'REPLAYGAIN_TRACK_PEAK', float_places=6), @@ -1852,7 +1856,7 @@ class MediaFile(object): float_places=6 ), MP4StorageStyle( - '----:com.apple.iTunes:replaygain_album_peak', + b'----:com.apple.iTunes:replaygain_album_peak', float_places=6 ), StorageStyle(u'REPLAYGAIN_ALBUM_PEAK', float_places=6), @@ -1862,7 +1866,7 @@ class MediaFile(object): initial_key = MediaField( MP3StorageStyle('TKEY'), - MP4StorageStyle('----:com.apple.iTunes:initialkey'), + MP4StorageStyle(b'----:com.apple.iTunes:initialkey'), StorageStyle('INITIALKEY'), ASFStorageStyle('INITIALKEY'), ) diff --git a/beets/plugins.py b/beets/plugins.py index 7ca80da0d..bd285da2d 100755 --- a/beets/plugins.py +++ b/beets/plugins.py @@ -14,6 +14,9 @@ """Support for beets plugins.""" +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import traceback import inspect import re @@ -25,7 +28,7 @@ import beets from beets import logging from beets import mediafile -PLUGIN_NAMESPACE = 'beetsplug' +PLUGIN_NAMESPACE = b'beetsplug' # Plugins using the Last.fm API can share the same API key. LASTFM_KEY = '2dc3914abf35f0d9c92d97d8f8e42b43' @@ -69,7 +72,7 @@ class BeetsPlugin(object): def __init__(self, name=None): """Perform one-time plugin setup. """ - self.name = name or self.__module__.split('.')[-1] + self.name = name or self.__module__.decode('utf8').split('.')[-1] self.config = beets.config[self.name] if not self.template_funcs: self.template_funcs = {} @@ -251,7 +254,8 @@ def load_plugins(names=()): BeetsPlugin subclasses desired. """ for name in names: - modname = '%s.%s' % (PLUGIN_NAMESPACE, name) + bname = name.encode('utf8') + modname = b'%s.%s' % (PLUGIN_NAMESPACE, bname) try: try: namespace = __import__(modname, None, None) @@ -262,7 +266,7 @@ def load_plugins(names=()): else: raise else: - for obj in getattr(namespace, name).__dict__.values(): + for obj in getattr(namespace, bname).__dict__.values(): if isinstance(obj, type) and issubclass(obj, BeetsPlugin) \ and obj != BeetsPlugin and obj not in _classes: _classes.add(obj) @@ -313,7 +317,7 @@ def queries(): def types(model_cls): # Gives us `item_types` and `album_types` - attr_name = '{0}_types'.format(model_cls.__name__.lower()) + attr_name = b'{0}_types'.format(model_cls.__name__.lower()) types = {} for plugin in find_plugins(): plugin_types = getattr(plugin, attr_name, {}) diff --git a/beets/ui/__init__.py b/beets/ui/__init__.py index a6b7e5518..558030a25 100644 --- a/beets/ui/__init__.py +++ b/beets/ui/__init__.py @@ -16,7 +16,9 @@ interface. To invoke the CLI, just call beets.ui.main(). The actual CLI commands are implemented in the ui.commands module. """ -from __future__ import print_function + +from __future__ import (division, absolute_import, print_function, + unicode_literals) import locale import optparse @@ -41,7 +43,7 @@ from beets.autotag import mb from beets.dbcore import query as db_query # On Windows platforms, use colorama to support "ANSI" terminal colors. -if sys.platform == 'win32': +if sys.platform == b'win32': try: import colorama except ImportError: @@ -220,11 +222,11 @@ def input_options(options, require=False, prompt=None, fallback_prompt=None, prompt_part_lengths = [] if numrange: if isinstance(default, int): - default_name = str(default) + default_name = unicode(default) default_name = colorize('turquoise', default_name) tmpl = '# selection (default %s)' prompt_parts.append(tmpl % default_name) - prompt_part_lengths.append(len(tmpl % str(default))) + prompt_part_lengths.append(len(tmpl % unicode(default))) else: prompt_parts.append('# selection') prompt_part_lengths.append(len(prompt_parts[-1])) @@ -491,7 +493,7 @@ def term_width(): except IOError: return fallback try: - height, width = struct.unpack('hh', buf) + height, width = struct.unpack(b'hh', buf) except struct.error: return fallback return width @@ -611,8 +613,8 @@ class Subcommand(object): @root_parser.setter def root_parser(self, root_parser): self._root_parser = root_parser - self.parser.prog = '{0} {1}'.format(root_parser.get_prog_name(), - self.name) + self.parser.prog = '{0} {1}'.format( + root_parser.get_prog_name().decode('utf8'), self.name) class SubcommandsOptionParser(optparse.OptionParser): @@ -831,7 +833,7 @@ def _configure(options): # Add any additional config files specified with --config. This # special handling lets specified plugins get loaded before we # finish parsing the command line. - if getattr(options, 'config', None) is not None: + if getattr(options, b'config', None) is not None: config_path = options.config del options.config config.set_file(config_path) diff --git a/beets/ui/commands.py b/beets/ui/commands.py index 34e8b7518..f6c1312d2 100644 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -15,7 +15,9 @@ """This module provides the default commands for beets' command-line interface. """ -from __future__ import print_function + +from __future__ import (division, absolute_import, print_function, + unicode_literals) import os import platform @@ -1487,9 +1489,9 @@ def config_edit(): path = config.user_config_path() if 'EDITOR' in os.environ: - editor = os.environ['EDITOR'] + editor = os.environ['EDITOR'].encode('utf8') try: - editor = shlex.split(editor) + editor = [e.decode('utf8') for e in shlex.split(editor)] except ValueError: # Malformed shell tokens. editor = [editor] args = editor + [path] diff --git a/beets/util/__init__.py b/beets/util/__init__.py index 3e7027e89..f6396f9da 100644 --- a/beets/util/__init__.py +++ b/beets/util/__init__.py @@ -13,7 +13,9 @@ # included in all copies or substantial portions of the Software. """Miscellaneous utility functions.""" -from __future__ import division + +from __future__ import (division, absolute_import, print_function, + unicode_literals) import os import sys @@ -299,28 +301,28 @@ def _fsencoding(): UTF-8 (not MBCS). """ encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() - if encoding == 'mbcs': + if encoding == b'mbcs': # On Windows, a broken encoding known to Python as "MBCS" is # used for the filesystem. However, we only use the Unicode API # for Windows paths, so the encoding is actually immaterial so # we can avoid dealing with this nastiness. We arbitrarily # choose UTF-8. - encoding = 'utf8' + encoding = b'utf8' return encoding def bytestring_path(path): - """Given a path, which is either a str or a unicode, returns a str + """Given a path, which is either a bytes or a unicode, returns a str path (ensuring that we never deal with Unicode pathnames). """ # Pass through bytestrings. - if isinstance(path, str): + if isinstance(path, bytes): return path # On Windows, remove the magic prefix added by `syspath`. This makes # ``bytestring_path(syspath(X)) == X``, i.e., we can safely # round-trip through `syspath`. - if os.path.__name__ == 'ntpath' and path.startswith(WINDOWS_MAGIC_PREFIX): + if os.path.__name__ == b'ntpath' and path.startswith(WINDOWS_MAGIC_PREFIX): path = path[len(WINDOWS_MAGIC_PREFIX):] # Try to encode with default encodings, but fall back to UTF8. @@ -339,7 +341,7 @@ def displayable_path(path, separator=u'; '): return separator.join(displayable_path(p) for p in path) elif isinstance(path, unicode): return path - elif not isinstance(path, str): + elif not isinstance(path, bytes): # A non-string object: just get its unicode representation. return unicode(path) @@ -357,7 +359,7 @@ def syspath(path, prefix=True): *really* know what you're doing. """ # Don't do anything if we're not on windows - if os.path.__name__ != 'ntpath': + if os.path.__name__ != b'ntpath': return path if not isinstance(path, unicode): @@ -495,12 +497,12 @@ def unique_path(path): # shares, which are sufficiently common as to cause frequent problems. # http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx CHAR_REPLACE = [ - (re.compile(ur'[\\/]'), u'_'), # / and \ -- forbidden everywhere. - (re.compile(ur'^\.'), u'_'), # Leading dot (hidden files on Unix). - (re.compile(ur'[\x00-\x1f]'), u''), # Control characters. - (re.compile(ur'[<>:"\?\*\|]'), u'_'), # Windows "reserved characters". - (re.compile(ur'\.$'), u'_'), # Trailing dots. - (re.compile(ur'\s+$'), u''), # Trailing whitespace. + (re.compile(r'[\\/]'), u'_'), # / and \ -- forbidden everywhere. + (re.compile(r'^\.'), u'_'), # Leading dot (hidden files on Unix). + (re.compile(r'[\x00-\x1f]'), u''), # Control characters. + (re.compile(r'[<>:"\?\*\|]'), u'_'), # Windows "reserved characters". + (re.compile(r'\.$'), u'_'), # Trailing dots. + (re.compile(r'\s+$'), u''), # Trailing whitespace. ] @@ -554,8 +556,8 @@ def as_string(value): if value is None: return u'' elif isinstance(value, buffer): - return str(value).decode('utf8', 'ignore') - elif isinstance(value, str): + return bytes(value).decode('utf8', 'ignore') + elif isinstance(value, bytes): return value.decode('utf8', 'ignore') else: return unicode(value) @@ -614,19 +616,19 @@ def cpu_count(): """ # Adapted from the soundconverter project: # https://github.com/kassoulet/soundconverter - if sys.platform == 'win32': + if sys.platform == b'win32': try: num = int(os.environ['NUMBER_OF_PROCESSORS']) except (ValueError, KeyError): num = 0 - elif sys.platform == 'darwin': + elif sys.platform == b'darwin': try: num = int(command_output(['sysctl', '-n', 'hw.ncpu'])) except ValueError: num = 0 else: try: - num = os.sysconf('SC_NPROCESSORS_ONLN') + num = os.sysconf(b'SC_NPROCESSORS_ONLN') except (ValueError, OSError, AttributeError): num = 0 if num >= 1: @@ -654,7 +656,7 @@ def command_output(cmd, shell=False): cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - close_fds=platform.system() != 'Windows', + close_fds=platform.system() != b'Windows', shell=shell ) stdout, stderr = proc.communicate() @@ -673,7 +675,7 @@ def max_filename_length(path, limit=MAX_FILENAME_LENGTH): misreports its capacity). If it cannot be determined (e.g., on Windows), return `limit`. """ - if hasattr(os, 'statvfs'): + if hasattr(os, b'statvfs'): try: res = os.statvfs(path) except OSError: diff --git a/beets/util/artresizer.py b/beets/util/artresizer.py index 09092bbe8..b1920d8ac 100644 --- a/beets/util/artresizer.py +++ b/beets/util/artresizer.py @@ -15,6 +15,9 @@ """Abstraction layer to resize images using PIL, ImageMagick, or a public resizing proxy if neither is available. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import urllib import subprocess import os @@ -40,7 +43,7 @@ def resize_url(url, maxwidth): """ return '{0}?{1}'.format(PROXY_URL, urllib.urlencode({ 'url': url.replace('http://', ''), - 'w': str(maxwidth), + 'w': bytes(maxwidth), })) diff --git a/beets/util/bluelet.py b/beets/util/bluelet.py index a12ec945e..2fa10a57c 100644 --- a/beets/util/bluelet.py +++ b/beets/util/bluelet.py @@ -5,6 +5,9 @@ asyncore. Bluelet: easy concurrency without all the messy parallelism. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import socket import select import sys @@ -550,7 +553,7 @@ def spawn(coro): and child coroutines run concurrently. """ if not isinstance(coro, types.GeneratorType): - raise ValueError('%s is not a coroutine' % str(coro)) + raise ValueError('%s is not a coroutine' % coro) return SpawnEvent(coro) @@ -560,7 +563,7 @@ def call(coro): returns a value using end(), then this event returns that value. """ if not isinstance(coro, types.GeneratorType): - raise ValueError('%s is not a coroutine' % str(coro)) + raise ValueError('%s is not a coroutine' % coro) return DelegationEvent(coro) diff --git a/beets/util/confit.py b/beets/util/confit.py index 4317045b3..c30e52c5d 100644 --- a/beets/util/confit.py +++ b/beets/util/confit.py @@ -14,7 +14,9 @@ """Worry-free YAML configuration files. """ -from __future__ import unicode_literals +from __future__ import (unicode_literals, absolute_import, print_function, + division) + import platform import os import pkgutil @@ -245,7 +247,7 @@ class ConfigView(object): def __str__(self): """Gets the value for this view as a byte string.""" - return str(self.get()) + return bytes(self.get()) def __unicode__(self): """Gets the value for this view as a unicode string. (Python 2 @@ -421,7 +423,7 @@ class Subview(ConfigView): if isinstance(self.key, int): self.name += '#{0}'.format(self.key) elif isinstance(self.key, BASESTRING): - self.name += '{0}'.format(self.key) + self.name += '{0}'.format(self.key.decode('utf8')) else: self.name += '{0}'.format(repr(self.key)) @@ -464,10 +466,10 @@ def _package_path(name): ``name == "__main__"``). """ loader = pkgutil.get_loader(name) - if loader is None or name == '__main__': + if loader is None or name == b'__main__': return None - if hasattr(loader, 'get_filename'): + if hasattr(loader, b'get_filename'): filepath = loader.get_filename(name) else: # Fall back to importing the specified module. @@ -487,13 +489,13 @@ def config_dirs(): """ paths = [] - if platform.system() == 'Darwin': + if platform.system() == b'Darwin': paths.append(MAC_DIR) paths.append(UNIX_DIR_FALLBACK) if UNIX_DIR_VAR in os.environ: paths.append(os.environ[UNIX_DIR_VAR]) - elif platform.system() == 'Windows': + elif platform.system() == b'Windows': paths.append(WINDOWS_DIR_FALLBACK) if WINDOWS_DIR_VAR in os.environ: paths.append(os.environ[WINDOWS_DIR_VAR]) @@ -576,7 +578,7 @@ def load_yaml(filename): parsed, a ConfigReadError is raised. """ try: - with open(filename, 'r') as f: + with open(filename, b'r') as f: return yaml.load(f, Loader=Loader) except (IOError, yaml.error.YAMLError) as exc: raise ConfigReadError(filename, exc) diff --git a/beets/util/enumeration.py b/beets/util/enumeration.py index 86e11874a..dc0a6fa42 100644 --- a/beets/util/enumeration.py +++ b/beets/util/enumeration.py @@ -12,6 +12,9 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from enum import Enum diff --git a/beets/util/functemplate.py b/beets/util/functemplate.py index 6d236c8a2..f1066761d 100644 --- a/beets/util/functemplate.py +++ b/beets/util/functemplate.py @@ -25,7 +25,9 @@ library: unknown symbols are left intact. This is sort of like a tiny, horrible degeneration of a real templating engine like Jinja2 or Mustache. """ -from __future__ import print_function + +from __future__ import (division, absolute_import, print_function, + unicode_literals) import re import ast @@ -39,8 +41,8 @@ GROUP_CLOSE = u'}' ARG_SEP = u',' ESCAPE_CHAR = u'$' -VARIABLE_PREFIX = '__var_' -FUNCTION_PREFIX = '__func_' +VARIABLE_PREFIX = b'__var_' +FUNCTION_PREFIX = b'__func_' class Environment(object): @@ -69,11 +71,11 @@ def ex_literal(val): value. """ if val is None: - return ast.Name('None', ast.Load()) + return ast.Name(b'None', ast.Load()) elif isinstance(val, (int, float, long)): return ast.Num(val) elif isinstance(val, bool): - return ast.Name(str(val), ast.Load()) + return ast.Name(bytes(val), ast.Load()) elif isinstance(val, basestring): return ast.Str(val) raise TypeError('no literal for {0}'.format(type(val))) @@ -110,7 +112,7 @@ def compile_func(arg_names, statements, name='_the_func', debug=False): bytecode of the compiled function. """ func_def = ast.FunctionDef( - name, + name.encode('utf8'), ast.arguments( [ast.Name(n, ast.Param()) for n in arg_names], None, None, @@ -122,7 +124,7 @@ def compile_func(arg_names, statements, name='_the_func', debug=False): mod = ast.Module([func_def]) ast.fix_missing_locations(mod) - prog = compile(mod, '', 'exec') + prog = compile(mod, b'', b'exec') # Debug: show bytecode. if debug: @@ -205,11 +207,11 @@ class Call(object): # Create a subexpression that joins the result components of # the arguments. arg_exprs.append(ex_call( - ast.Attribute(ex_literal(u''), 'join', ast.Load()), + ast.Attribute(ex_literal(u''), b'join', ast.Load()), [ex_call( - 'map', + b'map', [ - ex_rvalue('unicode'), + ex_rvalue(b'unicode'), ast.List(subexprs, ast.Load()), ] )], @@ -289,7 +291,7 @@ class Parser(object): # Common parsing resources. special_chars = (SYMBOL_DELIM, FUNC_DELIM, GROUP_OPEN, GROUP_CLOSE, ARG_SEP, ESCAPE_CHAR) - special_char_re = re.compile(ur'[%s]|$' % + special_char_re = re.compile(r'[%s]|$' % u''.join(re.escape(c) for c in special_chars)) def parse_expression(self): @@ -477,7 +479,7 @@ class Parser(object): Updates ``pos``. """ remainder = self.string[self.pos:] - ident = re.match(ur'\w*', remainder).group(0) + ident = re.match(r'\w*', remainder).group(0) self.pos += len(ident) return ident @@ -532,9 +534,9 @@ class Template(object): argnames = [] for varname in varnames: - argnames.append(VARIABLE_PREFIX.encode('utf8') + varname) + argnames.append(VARIABLE_PREFIX + varname) for funcname in funcnames: - argnames.append(FUNCTION_PREFIX.encode('utf8') + funcname) + argnames.append(FUNCTION_PREFIX + funcname) func = compile_func( argnames, @@ -555,7 +557,7 @@ class Template(object): # Performance tests. -if __name__ == '__main__': +if __name__ == b'__main__': import timeit _tmpl = Template(u'foo $bar %baz{foozle $bar barzle} $bar') _vars = {'bar': 'qux'} diff --git a/beets/util/pipeline.py b/beets/util/pipeline.py index 9b4446d9f..a0bb6af38 100644 --- a/beets/util/pipeline.py +++ b/beets/util/pipeline.py @@ -30,14 +30,16 @@ up a bottleneck stage by dividing its work among multiple threads. To do so, pass an iterable of coroutines to the Pipeline constructor in place of any single coroutine. """ -from __future__ import print_function + +from __future__ import (division, absolute_import, print_function, + unicode_literals) import Queue from threading import Thread, Lock import sys -BUBBLE = '__PIPELINE_BUBBLE__' -POISON = '__PIPELINE_POISON__' +BUBBLE = b'__PIPELINE_BUBBLE__' +POISON = b'__PIPELINE_POISON__' DEFAULT_QUEUE_SIZE = 16 @@ -457,7 +459,7 @@ class Pipeline(object): yield msg # Smoke test. -if __name__ == '__main__': +if __name__ == b'__main__': import time # Test a normally-terminating pipeline both in sequence and diff --git a/beets/vfs.py b/beets/vfs.py index f0032d63b..e3cebafb1 100644 --- a/beets/vfs.py +++ b/beets/vfs.py @@ -15,6 +15,9 @@ """A simple utility for constructing filesystem-like trees from beets libraries. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from collections import namedtuple from beets import util diff --git a/beetsplug/__init__.py b/beetsplug/__init__.py index 337f84daa..811ee0539 100644 --- a/beetsplug/__init__.py +++ b/beetsplug/__init__.py @@ -15,5 +15,7 @@ """A namespace package for beets plugins.""" # Make this a namespace package. +from __future__ import absolute_import + from pkgutil import extend_path __path__ = extend_path(__path__, __name__) diff --git a/beetsplug/bench.py b/beetsplug/bench.py index 80c9a39ec..ebb9f981f 100644 --- a/beetsplug/bench.py +++ b/beetsplug/bench.py @@ -14,7 +14,8 @@ """Some simple performance benchmarks for beets. """ -from __future__ import print_function + +from __future__ import division, absolute_import, print_function from beets.plugins import BeetsPlugin from beets import ui diff --git a/beetsplug/bpd/__init__.py b/beetsplug/bpd/__init__.py index 3380011f5..04260d824 100644 --- a/beetsplug/bpd/__init__.py +++ b/beetsplug/bpd/__init__.py @@ -16,7 +16,9 @@ Beets library. Attempts to implement a compatible protocol to allow use of the wide range of MPD clients. """ -from __future__ import print_function + +from __future__ import (division, absolute_import, print_function, + unicode_literals) import re from string import Template diff --git a/beetsplug/bpd/gstplayer.py b/beetsplug/bpd/gstplayer.py index 2e7c05201..a2872f7c4 100644 --- a/beetsplug/bpd/gstplayer.py +++ b/beetsplug/bpd/gstplayer.py @@ -15,7 +15,9 @@ """A wrapper for the GStreamer Python bindings that exposes a simple music player. """ -from __future__ import print_function + +from __future__ import (division, absolute_import, print_function, + unicode_literals) import sys import time @@ -88,7 +90,7 @@ class GstPlayer(object): # error self.player.set_state(gst.STATE_NULL) err, debug = message.parse_error() - print("Error: " + str(err)) + print("Error: {0}".format(err)) self.playing = False def _set_volume(self, volume): @@ -212,7 +214,7 @@ def play_complicated(paths): while my_paths: time.sleep(1) -if __name__ == '__main__': +if __name__ == b'__main__': # A very simple command-line player. Just give it names of audio # files on the command line; these are all played in sequence. paths = [os.path.abspath(os.path.expanduser(p)) diff --git a/beetsplug/bpm.py b/beetsplug/bpm.py index aa395719f..85c8d6636 100644 --- a/beetsplug/bpm.py +++ b/beetsplug/bpm.py @@ -14,6 +14,9 @@ """Determine BPM by pressing a key to the rhythm.""" +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import time from beets import ui diff --git a/beetsplug/bucket.py b/beetsplug/bucket.py index 45ad163f9..d673b0bd4 100644 --- a/beetsplug/bucket.py +++ b/beetsplug/bucket.py @@ -15,6 +15,9 @@ """Provides the %bucket{} function for path formatting. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from datetime import datetime import re import string @@ -131,9 +134,9 @@ def str2fmt(s): def format_span(fmt, yearfrom, yearto, fromnchars, tonchars): """Return a span string representation. """ - args = (str(yearfrom)[-fromnchars:]) + args = (bytes(yearfrom)[-fromnchars:]) if tonchars: - args = (str(yearfrom)[-fromnchars:], str(yearto)[-tonchars:]) + args = (bytes(yearfrom)[-fromnchars:], bytes(yearto)[-tonchars:]) return fmt % args diff --git a/beetsplug/chroma.py b/beetsplug/chroma.py index 5e17efe82..f2af6d803 100644 --- a/beetsplug/chroma.py +++ b/beetsplug/chroma.py @@ -15,6 +15,9 @@ """Adds Chromaprint/Acoustid acoustic fingerprinting support to the autotagger. Requires the pyacoustid library. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from beets import plugins from beets import ui from beets import util @@ -62,7 +65,7 @@ def acoustid_match(log, path): duration, fp = acoustid.fingerprint_file(util.syspath(path)) except acoustid.FingerprintGenerationError as exc: log.error(u'fingerprinting of {0} failed: {1}', - util.displayable_path(repr(path)), str(exc)) + util.displayable_path(repr(path)), exc) return None _fingerprints[path] = fp try: diff --git a/beetsplug/convert.py b/beetsplug/convert.py index 4274448ad..2f49b5ef8 100644 --- a/beetsplug/convert.py +++ b/beetsplug/convert.py @@ -14,6 +14,9 @@ """Converts tracks or albums to external directory """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os import threading import subprocess @@ -43,7 +46,7 @@ def replace_ext(path, ext): The new extension must not contain a leading dot. """ - return os.path.splitext(path)[0] + '.' + ext + return os.path.splitext(path)[0] + b'.' + ext def get_format(format=None): @@ -63,7 +66,7 @@ def get_format(format=None): .format(format) ) except ConfigTypeError: - command = config['convert']['formats'][format].get(str) + command = config['convert']['formats'][format].get(bytes) extension = format # Convenience and backwards-compatibility shortcuts. @@ -175,8 +178,8 @@ class ConvertPlugin(BeetsPlugin): args = shlex.split(command) for i, arg in enumerate(args): args[i] = Template(arg).safe_substitute({ - 'source': source, - 'dest': dest, + 'source': source.decode('utf8'), + 'dest': dest.decode('utf8'), }) if pretend: diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py index c5e9dd2f8..86af9da06 100644 --- a/beetsplug/discogs.py +++ b/beetsplug/discogs.py @@ -15,6 +15,9 @@ """Adds Discogs album search support to the autotagger. Requires the discogs-client library. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import beets.ui from beets import logging from beets.autotag.hooks import AlbumInfo, TrackInfo, Distance @@ -33,7 +36,7 @@ import json urllib3_logger = logging.getLogger('requests.packages.urllib3') urllib3_logger.setLevel(logging.CRITICAL) -USER_AGENT = 'beets/{0} +http://beets.radbox.org/'.format(beets.__version__) +USER_AGENT = u'beets/{0} +http://beets.radbox.org/'.format(beets.__version__) class DiscogsPlugin(BeetsPlugin): diff --git a/beetsplug/duplicates.py b/beetsplug/duplicates.py index ac5b19c17..8f2359dce 100644 --- a/beetsplug/duplicates.py +++ b/beetsplug/duplicates.py @@ -14,6 +14,9 @@ """List duplicate tracks or albums. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import shlex from beets.plugins import BeetsPlugin @@ -85,7 +88,7 @@ def _group_by(objs, keys, log): counts[key].append(obj) else: log.debug(u'{0}: all keys {1} on item {2} are null: skipping', - PLUGIN, str(keys), displayable_path(obj.path)) + PLUGIN, keys, displayable_path(obj.path)) return counts diff --git a/beetsplug/echonest.py b/beetsplug/echonest.py index fc66ad775..c8d45f3b0 100644 --- a/beetsplug/echonest.py +++ b/beetsplug/echonest.py @@ -14,6 +14,9 @@ """Fetch a variety of acoustic metrics from The Echo Nest. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import time import socket import os diff --git a/beetsplug/embedart.py b/beetsplug/embedart.py index 27f45243c..e84b39737 100644 --- a/beetsplug/embedart.py +++ b/beetsplug/embedart.py @@ -13,6 +13,9 @@ # included in all copies or substantial portions of the Software. """Allows beets to embed album art into file metadata.""" +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os.path import imghdr import subprocess diff --git a/beetsplug/fetchart.py b/beetsplug/fetchart.py index c5b2d8737..e39f88b7b 100644 --- a/beetsplug/fetchart.py +++ b/beetsplug/fetchart.py @@ -14,6 +14,9 @@ """Fetches album art. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from contextlib import closing import os import re @@ -251,14 +254,14 @@ class FileSystem(ArtSource): images = [] for fn in os.listdir(path): for ext in IMAGE_EXTENSIONS: - if fn.lower().endswith('.' + ext) and \ + if fn.lower().endswith(b'.' + ext.encode('utf8')) and \ os.path.isfile(os.path.join(path, fn)): images.append(fn) # Look for "preferred" filenames. images = sorted(images, key=lambda x: self.filename_priority(x, cover_names)) - cover_pat = r"(\b|_)({0})(\b|_)".format('|'.join(cover_names)) + cover_pat = br"(\b|_)({0})(\b|_)".format(b'|'.join(cover_names)) for fn in images: if re.search(cover_pat, os.path.splitext(fn)[0], re.I): self._log.debug(u'using well-named art file {0}', diff --git a/beetsplug/freedesktop.py b/beetsplug/freedesktop.py index 6411328ff..c4ca24d3a 100644 --- a/beetsplug/freedesktop.py +++ b/beetsplug/freedesktop.py @@ -15,6 +15,9 @@ """Creates freedesktop.org-compliant .directory files on an album level. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from beets.plugins import BeetsPlugin from beets.ui import Subcommand from beets.ui import decargs diff --git a/beetsplug/fromfilename.py b/beetsplug/fromfilename.py index 4121c40e3..951af07c4 100644 --- a/beetsplug/fromfilename.py +++ b/beetsplug/fromfilename.py @@ -15,6 +15,9 @@ """If the title is empty, try to extract track and title from the filename. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from beets import plugins from beets.util import displayable_path import os diff --git a/beetsplug/ftintitle.py b/beetsplug/ftintitle.py index 839e4d16d..60ae3f032 100644 --- a/beetsplug/ftintitle.py +++ b/beetsplug/ftintitle.py @@ -14,6 +14,9 @@ """Moves "featured" artists to the title from the artist field. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import re from beets import plugins diff --git a/beetsplug/fuzzy.py b/beetsplug/fuzzy.py index 789f862b8..e3ba372f8 100644 --- a/beetsplug/fuzzy.py +++ b/beetsplug/fuzzy.py @@ -15,6 +15,9 @@ """Provides a fuzzy matching query. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from beets.plugins import BeetsPlugin from beets.dbcore.query import StringFieldQuery import difflib diff --git a/beetsplug/ihate.py b/beetsplug/ihate.py index 62e0b5e58..b4a877fbc 100644 --- a/beetsplug/ihate.py +++ b/beetsplug/ihate.py @@ -12,6 +12,9 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + """Warns you about things you hate (or even blocks import).""" from beets.plugins import BeetsPlugin diff --git a/beetsplug/importadded.py b/beetsplug/importadded.py index b55d67171..5893d98a3 100644 --- a/beetsplug/importadded.py +++ b/beetsplug/importadded.py @@ -3,8 +3,8 @@ modification time (mtime) of the item's source file before import. Reimported albums and items are skipped. """ - -from __future__ import unicode_literals, absolute_import, print_function +from __future__ import (unicode_literals, absolute_import, print_function, + division) import os diff --git a/beetsplug/importfeeds.py b/beetsplug/importfeeds.py index 027532ff9..d1d21f839 100644 --- a/beetsplug/importfeeds.py +++ b/beetsplug/importfeeds.py @@ -12,6 +12,9 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + """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 one wants to manually add music to a player by its path. diff --git a/beetsplug/info.py b/beetsplug/info.py index 27aeabf0f..9dd78f250 100644 --- a/beetsplug/info.py +++ b/beetsplug/info.py @@ -15,6 +15,9 @@ """Shows file metadata. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os from beets.plugins import BeetsPlugin diff --git a/beetsplug/inline.py b/beetsplug/inline.py index 71a2e6e6b..fb99e729a 100644 --- a/beetsplug/inline.py +++ b/beetsplug/inline.py @@ -14,6 +14,9 @@ """Allows inline path template customization code in the config file. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import traceback import itertools diff --git a/beetsplug/keyfinder.py b/beetsplug/keyfinder.py index 882ae3a88..fa86b1c69 100644 --- a/beetsplug/keyfinder.py +++ b/beetsplug/keyfinder.py @@ -15,6 +15,9 @@ """Uses the `KeyFinder` program to add the `initial_key` field. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import subprocess from beets import ui diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py index 727ab08fe..e65593730 100644 --- a/beetsplug/lastgenre/__init__.py +++ b/beetsplug/lastgenre/__init__.py @@ -12,6 +12,9 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + """Gets genres for imported music based on Last.fm tags. Uses a provided whitelist file to determine which tags are valid genres. @@ -122,7 +125,7 @@ class LastGenrePlugin(plugins.BeetsPlugin): wl_filename = WHITELIST if wl_filename: wl_filename = normpath(wl_filename) - with open(wl_filename, 'r') as f: + with open(wl_filename, b'r') as f: for line in f: line = line.decode('utf8').strip().lower() if line and not line.startswith(u'#'): diff --git a/beetsplug/lastimport.py b/beetsplug/lastimport.py index 5c3995d2a..66da6880d 100644 --- a/beetsplug/lastimport.py +++ b/beetsplug/lastimport.py @@ -12,6 +12,9 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import requests from beets import ui from beets import dbcore @@ -105,8 +108,8 @@ def fetch_tracks(user, page, limit): 'method': 'library.gettracks', 'user': user, 'api_key': plugins.LASTFM_KEY, - 'page': str(page), - 'limit': str(limit), + 'page': bytes(page), + 'limit': bytes(limit), 'format': 'json', }).json() diff --git a/beetsplug/lyrics.py b/beetsplug/lyrics.py index 9dd1fce34..5ead7096e 100644 --- a/beetsplug/lyrics.py +++ b/beetsplug/lyrics.py @@ -14,7 +14,9 @@ """Fetches, embeds, and displays lyrics. """ -from __future__ import print_function + +from __future__ import (division, absolute_import, print_function, + unicode_literals) import re import requests @@ -55,7 +57,7 @@ URL_CHARACTERS = { def unescape(text): """Resolves &#xxx; HTML entities (and some others).""" - if isinstance(text, str): + if isinstance(text, bytes): text = text.decode('utf8', 'ignore') out = text.replace(u' ', u' ') diff --git a/beetsplug/mbcollection.py b/beetsplug/mbcollection.py index b7cac8024..6dc724636 100644 --- a/beetsplug/mbcollection.py +++ b/beetsplug/mbcollection.py @@ -12,6 +12,9 @@ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from beets.plugins import BeetsPlugin from beets.ui import Subcommand from beets import ui diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py index aea50fd15..365d83193 100644 --- a/beetsplug/mbsync.py +++ b/beetsplug/mbsync.py @@ -14,6 +14,9 @@ """Update library's tags using MusicBrainz. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from beets.plugins import BeetsPlugin from beets import autotag, library, ui, util from beets.autotag import hooks diff --git a/beetsplug/missing.py b/beetsplug/missing.py index 1748c731d..517a20758 100644 --- a/beetsplug/missing.py +++ b/beetsplug/missing.py @@ -14,6 +14,9 @@ """List missing tracks. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from beets.autotag import hooks from beets.library import Item from beets.plugins import BeetsPlugin diff --git a/beetsplug/mpdstats.py b/beetsplug/mpdstats.py index f361b0ee8..c021d7465 100644 --- a/beetsplug/mpdstats.py +++ b/beetsplug/mpdstats.py @@ -13,6 +13,9 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import mpd import socket import select diff --git a/beetsplug/mpdupdate.py b/beetsplug/mpdupdate.py index 42ef55fa8..96141c567 100644 --- a/beetsplug/mpdupdate.py +++ b/beetsplug/mpdupdate.py @@ -20,6 +20,9 @@ Put something like the following in your config.yaml to configure: port: 6600 password: seekrit """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from beets.plugins import BeetsPlugin import os import socket diff --git a/beetsplug/permissions.py b/beetsplug/permissions.py index 3caec40e0..256f09e52 100644 --- a/beetsplug/permissions.py +++ b/beetsplug/permissions.py @@ -1,3 +1,6 @@ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + """Fixes file permissions after the file gets written on import. Put something like the following in your config.yaml to configure: @@ -14,7 +17,7 @@ def convert_perm(perm): to an oct int. Else it just converts it to oct. """ if isinstance(perm, int): - return int(str(perm), 8) + return int(bytes(perm), 8) else: return int(perm, 8) diff --git a/beetsplug/play.py b/beetsplug/play.py index 5072234ca..b32d3c75b 100644 --- a/beetsplug/play.py +++ b/beetsplug/play.py @@ -14,6 +14,9 @@ """Send the results of a query to the configured music player as a playlist. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from functools import partial from beets.plugins import BeetsPlugin diff --git a/beetsplug/plexupdate.py b/beetsplug/plexupdate.py index f132cd4b3..9781e300e 100644 --- a/beetsplug/plexupdate.py +++ b/beetsplug/plexupdate.py @@ -5,6 +5,9 @@ Put something like the following in your config.yaml to configure: host: localhost port: 32400 """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import requests from urlparse import urljoin import xml.etree.ElementTree as ET diff --git a/beetsplug/random.py b/beetsplug/random.py index fefe46aaf..f6a664a4c 100644 --- a/beetsplug/random.py +++ b/beetsplug/random.py @@ -14,7 +14,9 @@ """Get a random song or album from the library. """ -from __future__ import absolute_import +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from beets.plugins import BeetsPlugin from beets.ui import Subcommand, decargs, print_ import random diff --git a/beetsplug/replaygain.py b/beetsplug/replaygain.py index ec3941d12..4ec407cfb 100644 --- a/beetsplug/replaygain.py +++ b/beetsplug/replaygain.py @@ -12,6 +12,9 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import subprocess import os import collections @@ -177,7 +180,7 @@ class CommandBackend(Backend): # Disable clipping warning. cmd = cmd + ['-c'] cmd = cmd + ['-a' if is_album else '-r'] - cmd = cmd + ['-d', str(self.gain_offset)] + cmd = cmd + ['-d', bytes(self.gain_offset)] cmd = cmd + [syspath(i.path) for i in items] self._log.debug(u'analyzing {0} files', len(items)) @@ -195,9 +198,9 @@ class CommandBackend(Backend): containing information about each analyzed file. """ out = [] - for line in text.split('\n')[1:num_lines + 1]: - parts = line.split('\t') - if len(parts) != 6 or parts[0] == 'File': + for line in text.split(b'\n')[1:num_lines + 1]: + parts = line.split(b'\t') + if len(parts) != 6 or parts[0] == b'File': self._log.debug(u'bad tool output: {0}', text) raise ReplayGainError('mp3gain failed') d = { diff --git a/beetsplug/rewrite.py b/beetsplug/rewrite.py index 34c2e0ed4..4663d9155 100644 --- a/beetsplug/rewrite.py +++ b/beetsplug/rewrite.py @@ -15,6 +15,9 @@ """Uses user-specified rewriting rules to canonicalize names for path formats. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import re from collections import defaultdict diff --git a/beetsplug/scrub.py b/beetsplug/scrub.py index 64bbcc9b5..a832c9466 100644 --- a/beetsplug/scrub.py +++ b/beetsplug/scrub.py @@ -16,6 +16,9 @@ automatically whenever tags are written. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from beets.plugins import BeetsPlugin from beets import ui from beets import util diff --git a/beetsplug/smartplaylist.py b/beetsplug/smartplaylist.py index ad9eed2d5..e2c256b2b 100644 --- a/beetsplug/smartplaylist.py +++ b/beetsplug/smartplaylist.py @@ -14,7 +14,9 @@ """Generates smart playlists based on beets queries. """ -from __future__ import print_function + +from __future__ import (division, absolute_import, print_function, + unicode_literals) from beets.plugins import BeetsPlugin from beets import ui @@ -101,5 +103,5 @@ class SmartPlaylistPlugin(BeetsPlugin): mkdirall(m3u_path) with open(syspath(m3u_path), 'w') as f: for path in m3us[m3u]: - f.write(path + '\n') + f.write(path + b'\n') self._log.info("{0} playlists updated", len(playlists)) diff --git a/beetsplug/spotify.py b/beetsplug/spotify.py index 5833efb58..90791d0ad 100644 --- a/beetsplug/spotify.py +++ b/beetsplug/spotify.py @@ -1,4 +1,6 @@ -from __future__ import print_function +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import re import webbrowser import requests diff --git a/beetsplug/the.py b/beetsplug/the.py index b3ff8b930..3fb16dcf9 100644 --- a/beetsplug/the.py +++ b/beetsplug/the.py @@ -14,6 +14,9 @@ """Moves patterns in path formats (suitable for moving articles).""" +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import re from beets.plugins import BeetsPlugin diff --git a/beetsplug/types.py b/beetsplug/types.py index 8a1e6c2d6..dad892bc9 100644 --- a/beetsplug/types.py +++ b/beetsplug/types.py @@ -12,6 +12,9 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from beets.plugins import BeetsPlugin from beets.dbcore import types from beets.util.confit import ConfigValueError diff --git a/beetsplug/web/__init__.py b/beetsplug/web/__init__.py index 8ff826328..18750ebb3 100644 --- a/beetsplug/web/__init__.py +++ b/beetsplug/web/__init__.py @@ -13,6 +13,9 @@ # included in all copies or substantial portions of the Software. """A Web interface to beets.""" +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from beets.plugins import BeetsPlugin from beets import ui from beets import util @@ -87,7 +90,7 @@ def resource(name): ) else: return flask.abort(404) - responder.__name__ = 'get_%s' % name + responder.__name__ = b'get_%s' % name.encode('utf8') return responder return make_responder @@ -101,7 +104,7 @@ def resource_query(name): json_generator(query_func(queries), root='results'), mimetype='application/json' ) - responder.__name__ = 'query_%s' % name + responder.__name__ = b'query_%s' % name.encode('utf8') return responder return make_responder @@ -116,7 +119,7 @@ def resource_list(name): json_generator(list_all(), root=name), mimetype='application/json' ) - responder.__name__ = 'all_%s' % name + responder.__name__ = b'all_%s' % name.encode('utf8') return responder return make_responder @@ -274,7 +277,7 @@ class WebPlugin(BeetsPlugin): app.config['lib'] = lib # Enable CORS if required. if self.config['cors']: - self._log.info(u'Enabling CORS with origin: {0}', + self._log.info('Enabling CORS with origin: {0}', self.config['cors']) from flask.ext.cors import CORS app.config['CORS_ALLOW_HEADERS'] = "Content-Type" diff --git a/beetsplug/zero.py b/beetsplug/zero.py index 6b4875c62..48ca930ca 100644 --- a/beetsplug/zero.py +++ b/beetsplug/zero.py @@ -14,6 +14,9 @@ """ Clears tag fields in media files.""" +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import re from beets.plugins import BeetsPlugin from beets.mediafile import MediaFile diff --git a/extra/release.py b/extra/release.py index 3f2f9ce6b..f95ca3f0d 100755 --- a/extra/release.py +++ b/extra/release.py @@ -1,6 +1,8 @@ #!/usr/bin/env python3 """A utility script for automating the beets release process. """ +from __future__ import division, absolute_import, print_function + import click import os import re @@ -275,7 +277,7 @@ def prep(): # FIXME It should be possible to specify this as an argument. version_parts = [int(n) for n in cur_version.split('.')] version_parts[-1] += 1 - next_version = '.'.join(map(str, version_parts)) + next_version = u'.'.join(map(unicode, version_parts)) bump_version(next_version) @@ -297,5 +299,5 @@ def publish(): subprocess.check_call(['twine', 'upload', path]) -if __name__ == '__main__': +if __name__ == b'__main__': release() diff --git a/setup.py b/setup.py index 61bdd461b..277d5c249 100755 --- a/setup.py +++ b/setup.py @@ -14,6 +14,8 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import division, absolute_import, print_function + import os import sys import subprocess diff --git a/test/_common.py b/test/_common.py index 6107601c8..723915d01 100644 --- a/test/_common.py +++ b/test/_common.py @@ -13,6 +13,9 @@ # included in all copies or substantial portions of the Software. """Some common functionality for beets' test cases.""" +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import time import sys import os @@ -40,7 +43,7 @@ beetsplug.__path__ = [os.path.abspath( )] # Test resources path. -RSRC = os.path.join(os.path.dirname(__file__), 'rsrc') +RSRC = os.path.join(os.path.dirname(__file__), b'rsrc') # Propagate to root loger so nosetest can capture it log = logging.getLogger('beets') @@ -73,7 +76,7 @@ def item(lib=None): comments=u'the comments', bpm=8, comp=True, - path='somepath' + str(_item_ident), + path='somepath{0}'.format(_item_ident), length=60.0, bitrate=128000, format='FLAC', @@ -235,7 +238,7 @@ class DummyOut(object): self.buf.append(s) def get(self): - return ''.join(self.buf) + return b''.join(self.buf) def clear(self): self.buf = [] @@ -250,7 +253,7 @@ class DummyIn(object): self.out = out def add(self, s): - self.buf.append(s + '\n') + self.buf.append(s + b'\n') def readline(self): if not self.buf: diff --git a/test/helper.py b/test/helper.py index a1faf2a63..2d38599ce 100644 --- a/test/helper.py +++ b/test/helper.py @@ -30,6 +30,9 @@ information or mock the environment. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import sys import os import os.path @@ -50,7 +53,7 @@ from beets.autotag.hooks import AlbumInfo, TrackInfo from beets.mediafile import MediaFile, Image # TODO Move AutotagMock here -import _common +from test import _common class LogCapture(logging.Handler): @@ -324,7 +327,7 @@ class TestHelper(object): items = [] path = os.path.join(_common.RSRC, 'full.' + ext) for i in range(count): - item = Item.from_path(str(path)) + item = Item.from_path(bytes(path)) item.album = u'\u00e4lbum {0}'.format(i) # Check unicode paths item.title = u't\u00eftle {0}'.format(i) item.add(self.lib) @@ -339,7 +342,7 @@ class TestHelper(object): items = [] path = os.path.join(_common.RSRC, 'full.' + ext) for i in range(track_count): - item = Item.from_path(str(path)) + item = Item.from_path(bytes(path)) item.album = u'\u00e4lbum' # Check unicode paths item.title = u't\u00eftle {0}'.format(i) item.add(self.lib) diff --git a/test/lyrics_download_samples.py b/test/lyrics_download_samples.py index 819d6f9f7..f144263fa 100644 --- a/test/lyrics_download_samples.py +++ b/test/lyrics_download_samples.py @@ -12,6 +12,9 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os import sys import requests @@ -41,9 +44,9 @@ def main(argv=None): """ if argv is None: argv = sys.argv - print 'Fetching samples from:' + print('Fetching samples from:') for s in test_lyrics.GOOGLE_SOURCES + test_lyrics.DEFAULT_SOURCES: - print s['url'] + print(s['url']) url = s['url'] + s['path'] fn = test_lyrics.url_to_filename(url) if not os.path.isfile(fn): @@ -51,5 +54,5 @@ def main(argv=None): with safe_open_w(fn) as f: f.write(html.encode('utf8')) -if __name__ == "__main__": +if __name__ == b'__main__': sys.exit(main()) diff --git a/test/rsrc/beetsplug/test.py b/test/rsrc/beetsplug/test.py index 68c08774f..79f787ae0 100644 --- a/test/rsrc/beetsplug/test.py +++ b/test/rsrc/beetsplug/test.py @@ -1,3 +1,6 @@ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from beets.plugins import BeetsPlugin from beets import ui diff --git a/test/test_art.py b/test/test_art.py index 02e589a3a..25b254568 100644 --- a/test/test_art.py +++ b/test/test_art.py @@ -14,13 +14,16 @@ """Tests for the album art fetchers.""" +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os import shutil import responses -import _common -from _common import unittest +from test import _common +from test._common import unittest from beetsplug import fetchart from beets.autotag import AlbumInfo, AlbumMatch from beets import library @@ -357,5 +360,5 @@ class ArtImporterTest(UseThePlugin): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_autotag.py b/test/test_autotag.py index 3405351e6..7e018a4c1 100644 --- a/test/test_autotag.py +++ b/test/test_autotag.py @@ -14,11 +14,14 @@ """Tests for autotagging functionality. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import re import copy -import _common -from _common import unittest +from test import _common +from test._common import unittest from beets import autotag from beets.autotag import match from beets.autotag.hooks import Distance, string_dist @@ -569,7 +572,7 @@ class AssignmentTest(unittest.TestCase): return Item( artist=u'ben harper', album=u'burn to shine', - title=u'ben harper - Burn to Shine ' + str(i), + title=u'ben harper - Burn to Shine {0}'.format(i), track=i, length=length, mb_trackid='', mb_albumid='', mb_artistid='', @@ -942,5 +945,5 @@ class EnumTest(_common.TestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_bucket.py b/test/test_bucket.py index 38846e324..d2c546998 100644 --- a/test/test_bucket.py +++ b/test/test_bucket.py @@ -15,11 +15,14 @@ """Tests for the 'bucket' plugin.""" -from _common import unittest +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test._common import unittest from beetsplug import bucket from beets import config, ui -from helper import TestHelper +from test.helper import TestHelper class BucketPluginTest(unittest.TestCase, TestHelper): @@ -151,5 +154,5 @@ class BucketPluginTest(unittest.TestCase, TestHelper): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_config_command.py b/test/test_config_command.py index b68b5bab9..d48afe2d9 100644 --- a/test/test_config_command.py +++ b/test/test_config_command.py @@ -1,3 +1,6 @@ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os import yaml from mock import patch @@ -7,9 +10,9 @@ from shutil import rmtree from beets import ui from beets import config -import _common -from _common import unittest -from helper import TestHelper, capture_stdout +from test import _common +from test._common import unittest +from test.helper import TestHelper, capture_stdout from beets.library import Library @@ -104,7 +107,7 @@ class ConfigCommandTest(unittest.TestCase, TestHelper): execlp.side_effect = OSError() self.run_command('config', '-e') self.assertIn('Could not edit configuration', - str(user_error.exception.args[0])) + unicode(user_error.exception.args[0])) def test_edit_invalid_config_file(self): self.lib = Library(':memory:') @@ -123,5 +126,5 @@ class ConfigCommandTest(unittest.TestCase, TestHelper): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_convert.py b/test/test_convert.py index f3f8cbeff..3fec8879e 100644 --- a/test/test_convert.py +++ b/test/test_convert.py @@ -12,12 +12,15 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import re import os.path -import _common -from _common import unittest -import helper -from helper import control_stdin +from test import _common +from test._common import unittest +from test import helper +from test.helper import control_stdin from beets.mediafile import MediaFile @@ -223,5 +226,5 @@ class NeverConvertLossyFilesTest(unittest.TestCase, TestHelper): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_datequery.py b/test/test_datequery.py index 06d857c0b..0533846bf 100644 --- a/test/test_datequery.py +++ b/test/test_datequery.py @@ -14,8 +14,11 @@ """Test for dbcore's date-based queries. """ -import _common -from _common import unittest +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test import _common +from test._common import unittest from datetime import datetime import time from beets.dbcore.query import _parse_periods, DateInterval, DateQuery @@ -124,5 +127,5 @@ def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_dbcore.py b/test/test_dbcore.py index 4882c4da5..dffe9ae75 100644 --- a/test/test_dbcore.py +++ b/test/test_dbcore.py @@ -14,10 +14,13 @@ """Tests for the DBCore database abstraction. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os import sqlite3 -from _common import unittest +from test._common import unittest from beets import dbcore from tempfile import mkstemp @@ -187,7 +190,7 @@ class ModelTest(unittest.TestCase): model.field_one = 123 model.store() row = self.db._connection().execute('select * from test').fetchone() - self.assertEqual(row['field_one'], 123) + self.assertEqual(row[b'field_one'], 123) def test_retrieve_by_id(self): model = TestModel1() @@ -537,5 +540,5 @@ class ResultsIteratorTest(unittest.TestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_echonest.py b/test/test_echonest.py index a92bd4086..6a1929b6f 100644 --- a/test/test_echonest.py +++ b/test/test_echonest.py @@ -13,11 +13,14 @@ # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os.path from mock import Mock, patch -from _common import unittest, RSRC -from helper import TestHelper +from test._common import unittest, RSRC +from test.helper import TestHelper from beets.library import Item @@ -180,5 +183,5 @@ class EchonestCliTest(unittest.TestCase, TestHelper): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_embedart.py b/test/test_embedart.py index 864d15629..6347c32d1 100644 --- a/test/test_embedart.py +++ b/test/test_embedart.py @@ -12,12 +12,15 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os.path from mock import Mock, patch -import _common -from _common import unittest -from helper import TestHelper +from test import _common +from test._common import unittest +from test.helper import TestHelper from beets.mediafile import MediaFile from beets import config, logging, ui @@ -147,5 +150,5 @@ class EmbedartTest(unittest.TestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_fetchart.py b/test/test_fetchart.py index e5a15f46c..b199e7139 100644 --- a/test/test_fetchart.py +++ b/test/test_fetchart.py @@ -12,9 +12,12 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os -from _common import unittest -from helper import TestHelper +from test._common import unittest +from test.helper import TestHelper class FetchartCliTest(unittest.TestCase, TestHelper): @@ -22,7 +25,7 @@ class FetchartCliTest(unittest.TestCase, TestHelper): def setUp(self): self.setup_beets() self.load_plugins('fetchart') - self.config['fetchart']['cover_names'] = 'c\xc3\xb6ver.jpg' + self.config['fetchart']['cover_names'] = b'c\xc3\xb6ver.jpg' self.config['art_filename'] = 'mycover' self.album = self.add_album() @@ -31,10 +34,10 @@ class FetchartCliTest(unittest.TestCase, TestHelper): self.teardown_beets() def test_set_art_from_folder(self): - self.touch('c\xc3\xb6ver.jpg', dir=self.album.path, content='IMAGE') + self.touch(b'c\xc3\xb6ver.jpg', dir=self.album.path, content='IMAGE') self.run_command('fetchart') - cover_path = os.path.join(self.album.path, 'mycover.jpg') + cover_path = os.path.join(self.album.path, b'mycover.jpg') self.album.load() self.assertEqual(self.album['artpath'], cover_path) @@ -42,7 +45,7 @@ class FetchartCliTest(unittest.TestCase, TestHelper): self.assertEqual(f.read(), 'IMAGE') def test_filesystem_does_not_pick_up_folder(self): - os.makedirs(os.path.join(self.album.path, 'mycover.jpg')) + os.makedirs(os.path.join(self.album.path, b'mycover.jpg')) self.run_command('fetchart') self.album.load() self.assertEqual(self.album['artpath'], None) @@ -51,5 +54,5 @@ class FetchartCliTest(unittest.TestCase, TestHelper): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_files.py b/test/test_files.py index 03daf92e3..126f0821e 100644 --- a/test/test_files.py +++ b/test/test_files.py @@ -14,14 +14,17 @@ """Test file manipulation functionality of Item. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import shutil import os import stat from os.path import join -import _common -from _common import unittest -from _common import item, touch +from test import _common +from test._common import unittest +from test._common import item, touch import beets.library from beets import util @@ -621,5 +624,5 @@ class MkDirAllTest(_common.TestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_ftintitle.py b/test/test_ftintitle.py index 6b2e43b33..aa0f4c6a7 100644 --- a/test/test_ftintitle.py +++ b/test/test_ftintitle.py @@ -14,7 +14,10 @@ """Tests for the 'ftintitle' plugin.""" -from _common import unittest +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test._common import unittest from beetsplug import ftintitle @@ -56,5 +59,5 @@ class FtInTitlePluginTest(unittest.TestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_ihate.py b/test/test_ihate.py index 030f5649e..eb14ccdc3 100644 --- a/test/test_ihate.py +++ b/test/test_ihate.py @@ -1,6 +1,9 @@ """Tests for the 'ihate' plugin""" -from _common import unittest +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test._common import unittest from beets import importer from beets.library import Item from beetsplug.ihate import IHatePlugin @@ -46,5 +49,5 @@ class IHatePluginTest(unittest.TestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_importadded.py b/test/test_importadded.py index 11c541ffa..f070b1e60 100644 --- a/test/test_importadded.py +++ b/test/test_importadded.py @@ -12,11 +12,14 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + """Tests for the `importadded` plugin.""" import os -from _common import unittest +from test._common import unittest from test.test_importer import ImportHelper, AutotagStub from beets import importer from beets import util @@ -168,5 +171,5 @@ class ImportAddedTest(unittest.TestCase, ImportHelper): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_importer.py b/test/test_importer.py index 90bc27bed..48a489258 100644 --- a/test/test_importer.py +++ b/test/test_importer.py @@ -13,6 +13,9 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + """Tests for the general importer functionality. """ import os @@ -24,10 +27,10 @@ from zipfile import ZipFile from tarfile import TarFile from mock import patch -import _common -from _common import unittest +from test import _common +from test._common import unittest from beets.util import displayable_path -from helper import TestImportSession, TestHelper, has_program, capture_log +from test.helper import TestImportSession, TestHelper, has_program, capture_log from beets import importer from beets.importer import albums_in_dir from beets.mediafile import MediaFile @@ -1617,5 +1620,5 @@ class ImportPretendTest(_common.TestCase, ImportHelper): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_importfeeds.py b/test/test_importfeeds.py index 464470ebd..061f7a7a4 100644 --- a/test/test_importfeeds.py +++ b/test/test_importfeeds.py @@ -1,9 +1,12 @@ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os import os.path import tempfile import shutil -from _common import unittest +from test._common import unittest from beets import config from beets.library import Item, Album, Library from beetsplug.importfeeds import ImportFeedsPlugin @@ -57,5 +60,5 @@ class ImportfeedsTestTest(unittest.TestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_info.py b/test/test_info.py index 15f5936ce..09668ceb2 100644 --- a/test/test_info.py +++ b/test/test_info.py @@ -12,8 +12,11 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -from _common import unittest -from helper import TestHelper +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test._common import unittest +from test.helper import TestHelper from beets.mediafile import MediaFile @@ -57,7 +60,7 @@ class InfoTest(unittest.TestCase, TestHelper): out = self.run_with_output('album:yyyy') self.assertIn(items[0].path, out) - self.assertIn('album: xxxx', out) + self.assertIn(b'album: xxxx', out) self.assertNotIn(items[1].path, out) @@ -68,7 +71,7 @@ class InfoTest(unittest.TestCase, TestHelper): out = self.run_with_output('--library', 'album:xxxx') self.assertIn(item.path, out) - self.assertIn('album: xxxx', out) + self.assertIn(b'album: xxxx', out) def test_collect_item_and_path(self): path = self.create_mediafile_fixture() @@ -93,5 +96,5 @@ class InfoTest(unittest.TestCase, TestHelper): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_keyfinder.py b/test/test_keyfinder.py index a8428cbb3..95c06754a 100644 --- a/test/test_keyfinder.py +++ b/test/test_keyfinder.py @@ -12,9 +12,12 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from mock import patch -from _common import unittest -from helper import TestHelper +from test._common import unittest +from test.helper import TestHelper from beets.library import Item @@ -78,5 +81,5 @@ class KeyFinderTest(unittest.TestCase, TestHelper): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_lastgenre.py b/test/test_lastgenre.py index 3ac96b279..4b1a50207 100644 --- a/test/test_lastgenre.py +++ b/test/test_lastgenre.py @@ -14,14 +14,17 @@ """Tests for the 'lastgenre' plugin.""" +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from mock import Mock -import _common -from _common import unittest +from test import _common +from test._common import unittest from beetsplug import lastgenre from beets import config -from helper import TestHelper +from test.helper import TestHelper class LastGenrePluginTest(unittest.TestCase, TestHelper): @@ -213,5 +216,5 @@ class LastGenrePluginTest(unittest.TestCase, TestHelper): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_library.py b/test/test_library.py index a83fae8a4..c34691acc 100644 --- a/test/test_library.py +++ b/test/test_library.py @@ -15,6 +15,9 @@ """Tests for non-query database functions of Item. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os import os.path import stat @@ -23,9 +26,9 @@ import re import unicodedata import sys -import _common -from _common import unittest -from _common import item +from test import _common +from test._common import unittest +from test._common import item import beets.library import beets.mediafile from beets import util @@ -57,7 +60,7 @@ class StoreTest(_common.LibTestCase): self.i.store() new_year = self.lib._connection().execute( 'select year from items where ' - 'title="the title"').fetchone()['year'] + 'title="the title"').fetchone()[b'year'] self.assertEqual(new_year, 1987) def test_store_only_writes_dirty_fields(self): @@ -66,7 +69,7 @@ class StoreTest(_common.LibTestCase): self.i.store() new_genre = self.lib._connection().execute( 'select genre from items where ' - 'title="the title"').fetchone()['genre'] + 'title="the title"').fetchone()[b'genre'] self.assertEqual(new_genre, original_genre) def test_store_clears_dirty_flags(self): @@ -85,7 +88,7 @@ class AddTest(_common.TestCase): self.lib.add(self.i) new_grouping = self.lib._connection().execute( 'select grouping from items ' - 'where composer="the composer"').fetchone()['grouping'] + 'where composer="the composer"').fetchone()[b'grouping'] self.assertEqual(new_grouping, self.i.grouping) def test_library_add_path_inserts_row(self): @@ -95,7 +98,7 @@ class AddTest(_common.TestCase): self.lib.add(i) new_grouping = self.lib._connection().execute( 'select grouping from items ' - 'where composer="the composer"').fetchone()['grouping'] + 'where composer="the composer"').fetchone()[b'grouping'] self.assertEqual(new_grouping, self.i.grouping) @@ -440,7 +443,7 @@ class DestinationTest(_common.TestCase): self.i.title = u'h\u0259d' self.lib.path_formats = [('default', '$title')] p = self.i.destination() - self.assertFalse('?' in p) + self.assertFalse(b'?' in p) # We use UTF-8 to encode Windows paths now. self.assertTrue(u'h\u0259d'.encode('utf8') in p) finally: @@ -922,25 +925,25 @@ class PathStringTest(_common.TestCase): self.i = item(self.lib) def test_item_path_is_bytestring(self): - self.assert_(isinstance(self.i.path, str)) + self.assert_(isinstance(self.i.path, bytes)) def test_fetched_item_path_is_bytestring(self): i = list(self.lib.items())[0] - self.assert_(isinstance(i.path, str)) + self.assert_(isinstance(i.path, bytes)) def test_unicode_path_becomes_bytestring(self): self.i.path = u'unicodepath' - self.assert_(isinstance(self.i.path, str)) + self.assert_(isinstance(self.i.path, bytes)) def test_unicode_in_database_becomes_bytestring(self): self.lib._connection().execute(""" update items set path=? where id=? """, (self.i.id, u'somepath')) i = list(self.lib.items())[0] - self.assert_(isinstance(i.path, str)) + self.assert_(isinstance(i.path, bytes)) def test_special_chars_preserved_in_database(self): - path = 'b\xe1r' + path = 'b\xe1r'.encode('utf8') self.i.path = path self.i.store() i = list(self.lib.items())[0] @@ -948,7 +951,7 @@ class PathStringTest(_common.TestCase): def test_special_char_path_added_to_database(self): self.i.remove() - path = 'b\xe1r' + path = 'b\xe1r'.encode('utf8') i = item() i.path = path self.lib.add(i) @@ -958,13 +961,13 @@ class PathStringTest(_common.TestCase): def test_destination_returns_bytestring(self): self.i.artist = u'b\xe1r' dest = self.i.destination() - self.assert_(isinstance(dest, str)) + self.assert_(isinstance(dest, bytes)) def test_art_destination_returns_bytestring(self): self.i.artist = u'b\xe1r' alb = self.lib.add_album([self.i]) dest = alb.art_destination(u'image.jpg') - self.assert_(isinstance(dest, str)) + self.assert_(isinstance(dest, bytes)) def test_artpath_stores_special_chars(self): path = b'b\xe1r' @@ -987,7 +990,7 @@ class PathStringTest(_common.TestCase): def test_unicode_artpath_becomes_bytestring(self): alb = self.lib.add_album([self.i]) alb.artpath = u'somep\xe1th' - self.assert_(isinstance(alb.artpath, str)) + self.assert_(isinstance(alb.artpath, bytes)) def test_unicode_artpath_in_database_decoded(self): alb = self.lib.add_album([self.i]) @@ -996,7 +999,7 @@ class PathStringTest(_common.TestCase): (u'somep\xe1th', alb.id) ) alb = self.lib.get_album(alb.id) - self.assert_(isinstance(alb.artpath, str)) + self.assert_(isinstance(alb.artpath, bytes)) class PathTruncationTest(_common.TestCase): @@ -1159,5 +1162,5 @@ def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_logging.py b/test/test_logging.py index 0d2eb7291..864fd021a 100644 --- a/test/test_logging.py +++ b/test/test_logging.py @@ -1,9 +1,12 @@ """Stupid tests that ensure logging works as expected""" +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import logging as log from StringIO import StringIO import beets.logging as blog -from _common import unittest, TestCase +from test._common import unittest, TestCase class LoggingTest(TestCase): @@ -38,5 +41,5 @@ def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_lyrics.py b/test/test_lyrics.py index 90e47aa89..960a94e33 100644 --- a/test/test_lyrics.py +++ b/test/test_lyrics.py @@ -14,14 +14,17 @@ """Tests for the 'lyrics' plugin.""" +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os -import _common +from test import _common import sys import re from mock import MagicMock -from _common import unittest +from test._common import unittest from beetsplug import lyrics from beets.library import Item from beets.util import confit @@ -376,5 +379,5 @@ class LyricsGooglePluginTest(unittest.TestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_mb.py b/test/test_mb.py index 2e5cd1aa3..3e5f721ff 100644 --- a/test/test_mb.py +++ b/test/test_mb.py @@ -14,8 +14,11 @@ """Tests for MusicBrainz API wrapper. """ -import _common -from _common import unittest +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test import _common +from test._common import unittest from beets.autotag import mb from beets import config import mock @@ -63,7 +66,7 @@ class MBAlbumInfoTest(_common.TestCase): for i, recording in enumerate(tracks): track = { 'recording': recording, - 'position': str(i + 1), + 'position': bytes(i + 1), } if track_length: # Track lengths are distinct from recording lengths. @@ -483,5 +486,5 @@ class MBLibraryTest(unittest.TestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_mbsync.py b/test/test_mbsync.py index a6248023c..fc37fe8c3 100644 --- a/test/test_mbsync.py +++ b/test/test_mbsync.py @@ -12,10 +12,13 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + from mock import patch -from _common import unittest -from helper import TestHelper,\ +from test._common import unittest +from test.helper import TestHelper,\ generate_album_info, \ generate_track_info, \ capture_log @@ -124,5 +127,5 @@ class MbsyncCliTest(unittest.TestCase, TestHelper): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_mediafile.py b/test/test_mediafile.py index 74f6fe8c5..8b23d1cee 100644 --- a/test/test_mediafile.py +++ b/test/test_mediafile.py @@ -15,14 +15,17 @@ """Automatically-generated blanket testing for the MediaFile metadata layer. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os import shutil import tempfile import datetime import time -import _common -from _common import unittest +from test import _common +from test._common import unittest from beets.mediafile import MediaFile, MediaField, Image, \ MP3DescStorageStyle, StorageStyle, MP4StorageStyle, \ ASFStorageStyle, ImageType @@ -283,10 +286,10 @@ class GenreListTestMixin(object): field_extension = MediaField( - MP3DescStorageStyle('customtag'), - MP4StorageStyle('----:com.apple.iTunes:customtag'), - StorageStyle('customtag'), - ASFStorageStyle('customtag'), + MP3DescStorageStyle(b'customtag'), + MP4StorageStyle(b'----:com.apple.iTunes:customtag'), + StorageStyle(b'customtag'), + ASFStorageStyle(b'customtag'), ) @@ -337,12 +340,14 @@ class ExtendedFieldTestMixin(object): def test_invalid_descriptor(self): with self.assertRaises(ValueError) as cm: MediaFile.add_field('somekey', True) - self.assertIn('must be an instance of MediaField', str(cm.exception)) + self.assertIn('must be an instance of MediaField', + unicode(cm.exception)) def test_overwrite_property(self): with self.assertRaises(ValueError) as cm: MediaFile.add_field('artist', MediaField()) - self.assertIn('property "artist" already exists', str(cm.exception)) + self.assertIn('property "artist" already exists', + unicode(cm.exception)) class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin, @@ -679,7 +684,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin, # ReplayGain is float tags[key] = 1.0 else: - tags[key] = 'value\u2010%s' % key + tags[key] = b'value\u2010%s' % key for key in ['disc', 'disctotal', 'track', 'tracktotal', 'bpm']: tags[key] = 1 @@ -698,6 +703,7 @@ class ReadWriteTestBase(ArtTestMixin, GenreListTestMixin, tags['original_year'] = original_date.year tags['original_month'] = original_date.month tags['original_day'] = original_date.day + return tags @@ -939,5 +945,5 @@ class MediaFieldTest(unittest.TestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_mediafile_edge.py b/test/test_mediafile_edge.py index fa12d1dda..3e828ac3e 100644 --- a/test/test_mediafile_edge.py +++ b/test/test_mediafile_edge.py @@ -14,12 +14,15 @@ """Specific, edge-case tests for the MediaFile metadata layer. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os import shutil -import _common -from _common import unittest -from helper import TestHelper +from test import _common +from test._common import unittest +from test.helper import TestHelper import beets.mediafile @@ -289,7 +292,7 @@ class ID3v23Test(unittest.TestCase, TestHelper): mf.year = 2013 mf.save() frame = mf.mgfile['TDRC'] - self.assertTrue('2013' in str(frame)) + self.assertTrue('2013' in unicode(frame)) self.assertTrue('TYER' not in mf.mgfile) finally: self._delete_test() @@ -300,7 +303,7 @@ class ID3v23Test(unittest.TestCase, TestHelper): mf.year = 2013 mf.save() frame = mf.mgfile['TYER'] - self.assertTrue('2013' in str(frame)) + self.assertTrue('2013' in unicode(frame)) self.assertTrue('TDRC' not in mf.mgfile) finally: self._delete_test() @@ -343,5 +346,5 @@ def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_permissions.py b/test/test_permissions.py index 1ba58312d..1e16a1c34 100644 --- a/test/test_permissions.py +++ b/test/test_permissions.py @@ -1,7 +1,10 @@ """Tests for the 'permissions' plugin. """ -from _common import unittest -from helper import TestHelper +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test._common import unittest +from test.helper import TestHelper from beetsplug.permissions import check_permissions, convert_perm @@ -43,5 +46,5 @@ def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_pipeline.py b/test/test_pipeline.py index 917983a91..aaa12d2a3 100644 --- a/test/test_pipeline.py +++ b/test/test_pipeline.py @@ -14,7 +14,10 @@ """Test the "pipeline.py" restricted parallel programming library. """ -from _common import unittest +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test._common import unittest from beets.util import pipeline @@ -237,5 +240,5 @@ class StageDecoratorTest(unittest.TestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_player.py b/test/test_player.py index 147026963..c1608e646 100644 --- a/test/test_player.py +++ b/test/test_player.py @@ -14,7 +14,10 @@ """Tests for BPD and music playing. """ -from _common import unittest +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test._common import unittest from beetsplug import bpd @@ -63,5 +66,5 @@ class CommandParseTest(unittest.TestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_plexupdate.py b/test/test_plexupdate.py index 9bdea386a..b173e23e4 100644 --- a/test/test_plexupdate.py +++ b/test/test_plexupdate.py @@ -1,5 +1,8 @@ -from _common import unittest -from helper import TestHelper +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test._common import unittest +from test.helper import TestHelper from beetsplug.plexupdate import get_music_section, update_plex import responses @@ -102,5 +105,5 @@ def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_plugins.py b/test/test_plugins.py index c4d8c4d9a..3b8d79cab 100644 --- a/test/test_plugins.py +++ b/test/test_plugins.py @@ -11,21 +11,23 @@ # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -import os +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +import os from mock import patch import shutil -from _common import unittest + from beets.importer import SingletonImportTask, SentinelImportTask, \ ArchiveImportTask -import helper - from beets import plugins, config from beets.library import Item from beets.dbcore import types from beets.mediafile import MediaFile -from test import _common from test.test_importer import ImportHelper +from test._common import unittest, RSRC +from test import helper class TestHelper(helper.TestHelper): @@ -171,7 +173,7 @@ class EventsTest(unittest.TestCase, ImportHelper, TestHelper): def __copy_file(self, dest_path, metadata): # Copy files - resource_path = os.path.join(_common.RSRC, 'full.mp3') + resource_path = os.path.join(RSRC, 'full.mp3') shutil.copy(resource_path, dest_path) medium = MediaFile(dest_path) # Set metadata @@ -295,5 +297,5 @@ class ListenersTest(unittest.TestCase, TestHelper): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_query.py b/test/test_query.py index cead73bfe..065b95623 100644 --- a/test/test_query.py +++ b/test/test_query.py @@ -14,9 +14,12 @@ """Various tests for querying the library database. """ -import _common -from _common import unittest -import helper +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test import _common +from test._common import unittest +from test import helper import beets.library from beets import dbcore @@ -278,12 +281,12 @@ class GetTest(DummyDataTestCase): def test_invalid_query(self): with self.assertRaises(InvalidQueryError) as raised: dbcore.query.NumericQuery('year', '199a') - self.assertIn('not an int', str(raised.exception)) + self.assertIn('not an int', unicode(raised.exception)) with self.assertRaises(InvalidQueryError) as raised: dbcore.query.RegexpQuery('year', '199(') - self.assertIn('not a regular expression', str(raised.exception)) - self.assertIn('unbalanced parenthesis', str(raised.exception)) + self.assertIn('not a regular expression', unicode(raised.exception)) + self.assertIn('unbalanced parenthesis', unicode(raised.exception)) class MatchTest(_common.TestCase): @@ -546,5 +549,5 @@ def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_replaygain.py b/test/test_replaygain.py index aa0d19b23..64d65b006 100644 --- a/test/test_replaygain.py +++ b/test/test_replaygain.py @@ -13,8 +13,11 @@ # included in all copies or substantial portions of the Software. -from _common import unittest -from helper import TestHelper, has_program +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test._common import unittest +from test.helper import TestHelper, has_program from beets.mediafile import MediaFile @@ -123,5 +126,5 @@ class ReplayGainCmdCliTest(ReplayGainCliTestBase, unittest.TestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_sort.py b/test/test_sort.py index f0b56d3ca..a047942cf 100644 --- a/test/test_sort.py +++ b/test/test_sort.py @@ -14,8 +14,11 @@ """Various tests for querying the library database. """ -import _common -from _common import unittest +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test import _common +from test._common import unittest import beets.library from beets import dbcore from beets import config @@ -359,5 +362,5 @@ def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_spotify.py b/test/test_spotify.py index 207d8723d..3d4d75bde 100644 --- a/test/test_spotify.py +++ b/test/test_spotify.py @@ -1,12 +1,15 @@ """Tests for the 'spotify' plugin""" -import _common +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test import _common import responses -from _common import unittest +from test._common import unittest from beets import config from beets.library import Item from beetsplug import spotify -from helper import TestHelper +from test.helper import TestHelper import urlparse @@ -45,7 +48,7 @@ class SpotifyPluginTest(_common.TestCase, TestHelper): @responses.activate def test_missing_request(self): - response_body = str( + response_body = bytes( '{' '"tracks" : {' '"href" : "https://api.spotify.com/v1/search?query=duifhjslkef' @@ -81,7 +84,7 @@ class SpotifyPluginTest(_common.TestCase, TestHelper): @responses.activate def test_track_request(self): - response_body = str( + response_body = bytes( '{' '"tracks" : {' '"href" : "https://api.spotify.com/v1/search?query=Happy+album%3A' @@ -204,5 +207,5 @@ class SpotifyPluginTest(_common.TestCase, TestHelper): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_template.py b/test/test_template.py index 20c0df8f4..1a0daa42e 100644 --- a/test/test_template.py +++ b/test/test_template.py @@ -15,9 +15,12 @@ """Tests for template engine. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import warnings -from _common import unittest +from test._common import unittest from beets.util import functemplate @@ -274,5 +277,5 @@ class EvalTest(unittest.TestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_the.py b/test/test_the.py index 1dba1d371..1b33c390e 100644 --- a/test/test_the.py +++ b/test/test_the.py @@ -1,7 +1,10 @@ """Tests for the 'the' plugin""" -from _common import unittest -import _common +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test._common import unittest +from test import _common from beets import config from beetsplug.the import ThePlugin, PATTERN_A, PATTERN_THE, FORMAT @@ -60,5 +63,5 @@ class ThePluginTest(_common.TestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_types_plugin.py b/test/test_types_plugin.py index 697cda702..7e0e6e082 100644 --- a/test/test_types_plugin.py +++ b/test/test_types_plugin.py @@ -12,11 +12,14 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import time from datetime import datetime -from _common import unittest -from helper import TestHelper +from test._common import unittest +from test.helper import TestHelper from beets.util.confit import ConfigValueError @@ -45,7 +48,7 @@ class TypesPluginTest(unittest.TestCase, TestHelper): # Match in range out = self.list('myint:1..3') - self.assertIn('aaa', out) + self.assertIn(b'aaa', out) def test_album_integer_modify_and_query(self): self.config['types'] = {'myint': 'int'} @@ -61,7 +64,7 @@ class TypesPluginTest(unittest.TestCase, TestHelper): # Match in range out = self.list_album('myint:1..3') - self.assertIn('aaa', out) + self.assertIn(b'aaa', out) def test_float_modify_and_query(self): self.config['types'] = {'myfloat': 'float'} @@ -73,7 +76,7 @@ class TypesPluginTest(unittest.TestCase, TestHelper): # Match in range out = self.list('myfloat:-10..0') - self.assertIn('aaa', out) + self.assertIn(b'aaa', out) def test_bool_modify_and_query(self): self.config['types'] = {'mybool': 'bool'} @@ -148,5 +151,5 @@ def mktime(*args): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_ui.py b/test/test_ui.py index 162694565..6b40d6cca 100644 --- a/test/test_ui.py +++ b/test/test_ui.py @@ -14,15 +14,18 @@ """Tests for the command-line interface. """ +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os import shutil import re import subprocess import platform -import _common -from _common import unittest -from helper import capture_stdout, has_program, TestHelper, control_stdin +from test import _common +from test._common import unittest +from test.helper import capture_stdout, has_program, TestHelper, control_stdin from beets import library from beets import ui @@ -177,12 +180,12 @@ class ModifyTest(unittest.TestCase, TestHelper): def test_move(self): self.modify("title=newTitle") item = self.lib.items().get() - self.assertIn('newTitle', item.path) + self.assertIn(b'newTitle', item.path) def test_not_move(self): self.modify("--nomove", "title=newTitle") item = self.lib.items().get() - self.assertNotIn('newTitle', item.path) + self.assertNotIn(b'newTitle', item.path) def test_update_mtime(self): item = self.item @@ -223,13 +226,13 @@ class ModifyTest(unittest.TestCase, TestHelper): self.modify("--album", "album=newAlbum") item = self.lib.items().get() item.read() - self.assertIn('newAlbum', item.path) + self.assertIn(b'newAlbum', item.path) def test_album_not_move(self): self.modify("--nomove", "--album", "album=newAlbum") item = self.lib.items().get() item.read() - self.assertNotIn('newAlbum', item.path) + self.assertNotIn(b'newAlbum', item.path) # Misc @@ -539,8 +542,8 @@ class InputTest(_common.TestCase): self.io.install() def test_manual_search_gets_unicode(self): - self.io.addinput('\xc3\x82me') - self.io.addinput('\xc3\x82me') + self.io.addinput(b'\xc3\x82me') + self.io.addinput(b'\xc3\x82me') artist, album = commands.manual_search(False) self.assertEqual(artist, u'\xc2me') self.assertEqual(album, u'\xc2me') @@ -647,7 +650,7 @@ class ConfigTest(unittest.TestCase, TestHelper): ui._raw_main(['test']) replacements = self.test_cmd.lib.replacements - self.assertEqual(replacements, [(re.compile(ur'[xy]'), u'z')]) + self.assertEqual(replacements, [(re.compile(r'[xy]'), b'z')]) def test_multiple_replacements_parsed(self): with self.write_config_file() as config: @@ -656,8 +659,8 @@ class ConfigTest(unittest.TestCase, TestHelper): ui._raw_main(['test']) replacements = self.test_cmd.lib.replacements self.assertEqual(replacements, [ - (re.compile(ur'[xy]'), u'z'), - (re.compile(ur'foo'), u'bar'), + (re.compile(r'[xy]'), 'z'), + (re.compile(r'foo'), 'bar'), ]) def test_cli_config_option(self): @@ -885,6 +888,7 @@ class ShowChangeTest(_common.TestCase): def _show_change(self, items=None, info=None, cur_artist=u'the artist', cur_album=u'the album', dist=0.1): + """Return an unicode string representing the changes""" items = items or self.items info = info or self.info mapping = dict(zip(items, info.tracks)) @@ -896,7 +900,8 @@ class ShowChangeTest(_common.TestCase): cur_album, autotag.AlbumMatch(album_dist, info, mapping, set(), set()), ) - return self.io.getoutput().lower() + # FIXME decoding shouldn't be done here + return self.io.getoutput().lower().decode('utf8') def test_null_change(self): msg = self._show_change() @@ -916,7 +921,7 @@ class ShowChangeTest(_common.TestCase): def test_item_data_change_with_unicode(self): self.items[0].title = u'caf\xe9' msg = self._show_change() - self.assertTrue(u'caf\xe9 -> the title' in msg.decode('utf8')) + self.assertTrue(u'caf\xe9 -> the title' in msg) def test_album_data_change_with_unicode(self): msg = self._show_change(cur_artist=u'caf\xe9', @@ -931,7 +936,7 @@ class ShowChangeTest(_common.TestCase): def test_item_data_change_title_missing_with_unicode_filename(self): self.items[0].title = u'' self.items[0].path = u'/path/to/caf\xe9.mp3'.encode('utf8') - msg = re.sub(r' +', ' ', self._show_change().decode('utf8')) + msg = re.sub(r' +', ' ', self._show_change()) self.assertTrue(u'caf\xe9.mp3 -> the title' in msg or u'caf.mp3 ->' in msg) @@ -1000,5 +1005,5 @@ class CompletionTest(_common.TestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_ui_importer.py b/test/test_ui_importer.py index 0e3599301..2c571925f 100644 --- a/test/test_ui_importer.py +++ b/test/test_ui_importer.py @@ -13,11 +13,15 @@ # included in all copies or substantial portions of the Software. """Tests the TerminalImportSession. The tests are the same as in the + test_importer module. But here the test importer inherits from ``TerminalImportSession``. So we test this class, too. """ -from _common import unittest, DummyIO +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test._common import unittest, DummyIO from test import test_importer from beets.ui.commands import TerminalImportSession from beets import importer @@ -65,7 +69,7 @@ class TestTerminalImportSession(TerminalImportSession): self.io.addinput('S') elif isinstance(choice, int): self.io.addinput('M') - self.io.addinput(str(choice)) + self.io.addinput(unicode(choice)) self._add_choice_input() else: raise Exception('Unknown choice %s' % choice) @@ -144,5 +148,5 @@ class GlobalGroupAlbumsImportTest(TerminalImportSessionSetup, def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_vfs.py b/test/test_vfs.py index ae8e7aef6..0d6b280e5 100644 --- a/test/test_vfs.py +++ b/test/test_vfs.py @@ -13,8 +13,11 @@ # included in all copies or substantial portions of the Software. """Tests for the virtual filesystem builder..""" -import _common -from _common import unittest +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test import _common +from test._common import unittest from beets import library from beets import vfs @@ -42,5 +45,5 @@ class VFSTest(_common.TestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_web.py b/test/test_web.py index 5aae76365..72d9e61d1 100644 --- a/test/test_web.py +++ b/test/test_web.py @@ -1,7 +1,10 @@ """Tests for the 'web' plugin""" -from _common import unittest -import _common +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test._common import unittest +from test import _common import json import beetsplug from beets.library import Item, Album @@ -104,5 +107,5 @@ class WebPluginTest(_common.LibTestCase): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/test_zero.py b/test/test_zero.py index a65276b8d..7274cc943 100644 --- a/test/test_zero.py +++ b/test/test_zero.py @@ -1,7 +1,10 @@ """Tests for the 'zero' plugin""" -from _common import unittest -from helper import TestHelper +from __future__ import (division, absolute_import, print_function, + unicode_literals) + +from test._common import unittest +from test.helper import TestHelper from beets.library import Item from beets import config @@ -103,5 +106,5 @@ class ZeroPluginTest(unittest.TestCase, TestHelper): def suite(): return unittest.TestLoader().loadTestsFromName(__name__) -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite') diff --git a/test/testall.py b/test/testall.py index 5fe8c5536..320e2c677 100755 --- a/test/testall.py +++ b/test/testall.py @@ -14,11 +14,14 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. +from __future__ import (division, absolute_import, print_function, + unicode_literals) + import os import re import sys -from _common import unittest +from test._common import unittest pkgpath = os.path.dirname(__file__) or '.' sys.path.append(pkgpath) @@ -43,5 +46,5 @@ def suite(): s.addTest(__import__(modname).suite()) return s -if __name__ == '__main__': +if __name__ == b'__main__': unittest.main(defaultTest='suite')