merge flexattr branch

This is a big change. Some things will probably break.
This commit is contained in:
Adrian Sampson 2013-08-27 17:20:37 -07:00
commit 247e3b9e01
31 changed files with 838 additions and 803 deletions

View file

@ -12,7 +12,7 @@
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
__version__ = '1.2.2'
__version__ = '1.3.0'
__author__ = 'Adrian Sampson <adrian@radbox.org>'
import beets.library

View file

@ -810,7 +810,7 @@ def plugin_stage(session, func):
# Stage may modify DB, so re-load cached item data.
for item in task.imported_items():
session.lib.load(item)
item.load()
def manipulate_files(session):
"""A coroutine (pipeline stage) that performs necessary file
@ -866,7 +866,7 @@ def manipulate_files(session):
# Save new paths.
with session.lib.transaction():
for item in items:
session.lib.store(item)
item.store()
# Plugin event.
plugins.send('import_task_files', session=session, task=task)

File diff suppressed because it is too large Load diff

View file

@ -905,7 +905,7 @@ def update_items(lib, query, album, move, pretend):
continue
# Read new data.
old_data = dict(item.record)
old_data = dict(item)
try:
item.read()
except Exception as exc:
@ -920,12 +920,12 @@ def update_items(lib, query, album, move, pretend):
old_data['albumartist'] == old_data['artist'] == \
item.artist:
item.albumartist = old_data['albumartist']
item.dirty['albumartist'] = False
item._dirty.remove('albumartist')
# Get and save metadata changes.
changes = {}
for key in library.ITEM_KEYS_META:
if item.dirty[key]:
if key in item._dirty:
changes[key] = old_data[key], getattr(item, key)
if changes:
# Something changed.
@ -941,14 +941,14 @@ def update_items(lib, query, album, move, pretend):
if move and lib.directory in ancestry(item.path):
lib.move(item)
lib.store(item)
item.store()
affected_albums.add(item.album_id)
elif not pretend:
# The file's mtime was different, but there were no changes
# to the metadata. Store the new mtime, which is set in the
# call to read(), so we don't check this again in the
# future.
lib.store(item)
item.store()
# Skip album changes while pretending.
if pretend:
@ -959,17 +959,18 @@ def update_items(lib, query, album, move, pretend):
if album_id is None: # Singletons.
continue
album = lib.get_album(album_id)
if not album: # Empty albums have already been removed.
if not album: # Empty albums have already been removed.
log.debug('emptied album %i' % album_id)
continue
al_items = list(album.items())
first_item = album.items().get()
# Update album structure to reflect an item in it.
for key in library.ALBUM_KEYS_ITEM:
setattr(album, key, getattr(al_items[0], key))
album[key] = first_item[key]
album.store()
# Move album art (and any inconsistent items).
if move and lib.directory in ancestry(al_items[0].path):
if move and lib.directory in ancestry(first_item.path):
log.debug('moving album %i' % album_id)
album.move()
@ -1099,6 +1100,8 @@ def _convert_type(key, value, album=False):
`album` indicates whether to use album or item field definitions.
"""
fields = library.ALBUM_FIELDS if album else library.ITEM_FIELDS
if key not in fields:
return value
typ = [f[1] for f in fields if f[0] == key][0]
if typ is bool:
@ -1120,15 +1123,9 @@ def _convert_type(key, value, album=False):
def modify_items(lib, mods, query, write, move, album, confirm):
"""Modifies matching items according to key=value assignments."""
# Parse key=value specifications into a dictionary.
if album:
allowed_keys = library.ALBUM_KEYS
else:
allowed_keys = library.ITEM_KEYS_WRITABLE + ['added']
fsets = {}
for mod in mods:
key, value = mod.split('=', 1)
if key not in allowed_keys:
raise ui.UserError('"%s" is not a valid field' % key)
fsets[key] = _convert_type(key, value, album)
# Get the items to modify.
@ -1143,8 +1140,7 @@ def modify_items(lib, mods, query, write, move, album, confirm):
# Show each change.
for field, value in fsets.iteritems():
curval = getattr(obj, field)
_showdiff(field, curval, value)
_showdiff(field, obj.get(field), value)
# Confirm.
if confirm:
@ -1156,7 +1152,7 @@ def modify_items(lib, mods, query, write, move, album, confirm):
with lib.transaction():
for obj in objs:
for field, value in fsets.iteritems():
setattr(obj, field, value)
obj[field] = value
if move:
cur_path = obj.item_dir() if album else obj.path
@ -1167,9 +1163,7 @@ def modify_items(lib, mods, query, write, move, album, confirm):
else:
lib.move(obj)
# When modifying items, we have to store them to the database.
if not album:
lib.store(obj)
obj.store()
# Apply tags if requested.
if write:
@ -1226,7 +1220,7 @@ def move_items(lib, dest, query, copy, album):
obj.move(copy, basedir=dest)
else:
lib.move(obj, copy, basedir=dest)
lib.store(obj)
obj.store()
move_cmd = ui.Subcommand('move',
help='move or copy items', aliases=('mv',))

View file

@ -29,7 +29,6 @@ import beets
from beets.plugins import BeetsPlugin
import beets.ui
from beets import vfs
from beets import config
from beets.util import bluelet
from beets.library import ITEM_KEYS_WRITABLE
@ -931,12 +930,12 @@ class Server(BaseServer):
def cmd_stats(self, conn):
"""Sends some statistics about the library."""
with self.lib.transaction() as tx:
songs, totaltime = beets.library.TrueQuery().count(tx)
statement = 'SELECT COUNT(DISTINCT artist), ' \
'COUNT(DISTINCT album) FROM items'
result = tx.query(statement)[0]
artists, albums = result[0], result[1]
'COUNT(DISTINCT album), ' \
'COUNT(id), ' \
'SUM(length) ' \
'FROM items'
artists, albums, songs, totaltime = tx.query(statement)[0]
yield (u'artists: ' + unicode(artists),
u'albums: ' + unicode(albums),
@ -1046,8 +1045,11 @@ class Server(BaseServer):
tag/value query.
"""
_, key = self._tagtype_lookup(tag)
query = beets.library.MatchQuery(key, value)
songs, playtime = query.count(self.lib)
songs = 0
playtime = 0.0
for item in self.lib.items(beets.library.MatchQuery(key, value)):
songs += 1
playtime += item.length
yield u'songs: ' + unicode(songs)
yield u'playtime: ' + unicode(int(playtime))

View file

@ -160,7 +160,7 @@ class AcoustidPlugin(plugins.BeetsPlugin):
help='generate fingerprints for items without them')
def fingerprint_cmd_func(lib, opts, args):
for item in lib.items(ui.decargs(args)):
fingerprint_item(item, lib=lib,
fingerprint_item(item,
write=config['import']['write'].get(bool))
fingerprint_cmd.func = fingerprint_cmd_func
@ -237,12 +237,12 @@ def submit_items(userkey, items, chunksize=64):
submit_chunk()
def fingerprint_item(item, lib=None, write=False):
def fingerprint_item(item, write=False):
"""Get the fingerprint for an Item. If the item already has a
fingerprint, it is not regenerated. If fingerprint generation fails,
return None. If `lib` is provided, then new fingerprints are saved
to the database. If `write` is set, then the new fingerprints are
also written to files' metadata.
return None. If the items are associated with a library, they are
saved to the database. If `write` is set, then the new fingerprints
are also written to files' metadata.
"""
# Get a fingerprint and length for this track.
if not item.length:
@ -271,8 +271,8 @@ def fingerprint_item(item, lib=None, write=False):
util.displayable_path(item.path)
))
item.write()
if lib:
lib.store(item)
if item._lib:
item.store()
return item.acoustid_fingerprint
except acoustid.FingerprintGenerationError as exc:
log.info(

View file

@ -120,7 +120,7 @@ def convert_item(lib, dest_dir, keep_new, path_formats):
# writing) to get new bitrate, duration, etc.
if keep_new:
item.read()
lib.store(item) # Store new path and audio data.
item.store() # Store new path and audio data.
if config['convert']['embed']:
album = lib.get_album(item)
@ -142,7 +142,7 @@ def convert_on_import(lib, item):
item.path = dest
item.write()
item.read() # Load new audio information data.
lib.store(item)
item.store()
def convert_func(lib, opts, args):
@ -168,7 +168,7 @@ def convert_func(lib, opts, args):
if opts.album:
items = (i for a in lib.albums(ui.decargs(args)) for i in a.items())
else:
items = lib.items(ui.decargs(args))
items = iter(lib.items(ui.decargs(args)))
convert = [convert_item(lib, dest, keep_new, path_formats) for i in range(threads)]
pipe = util.pipeline.Pipeline([items, convert])
pipe.run_parallel()

View file

@ -54,7 +54,7 @@ def fetch_item_tempo(lib, loglevel, item, write):
item.bpm = tempo
if write:
item.write()
lib.store(item)
item.store()
def get_tempo(artist, title):
"""Get the tempo for a song."""

View file

@ -216,6 +216,7 @@ def batch_fetch_art(lib, albums, force, maxwidth=None):
if path:
album.set_art(path, False)
album.store()
message = 'found album art'
else:
message = 'no art found'
@ -274,6 +275,7 @@ class FetchArtPlugin(BeetsPlugin):
src_removed = config['import']['delete'].get(bool) or \
config['import']['move'].get(bool)
album.set_art(path, not src_removed)
album.store()
if src_removed:
task.prune(path)

View file

@ -16,12 +16,12 @@
"""
from beets.plugins import BeetsPlugin
from beets.library import RegisteredFieldQuery
from beets.library import FieldQuery
import beets
import difflib
class FuzzyQuery(RegisteredFieldQuery):
class FuzzyQuery(FieldQuery):
@classmethod
def value_match(self, pattern, val):
# smartcase

View file

@ -70,12 +70,10 @@ def compile_inline(python_code, album):
is_expr = True
def _dict_for(obj):
out = dict(obj)
if album:
out = dict(obj._record)
out['items'] = list(obj.items())
return out
else:
return dict(obj.record)
return out
if is_expr:
# For expressions, just evaluate and return the result.

View file

@ -335,7 +335,7 @@ class LastGenrePlugin(plugins.BeetsPlugin):
# track on the album.
if 'track' in self.sources:
item.genre, src = self._get_genre(item)
lib.store(item)
item.store()
log.info(u'genre for track {0} - {1} ({2}): {3}'.format(
item.artist, item.title, src, item.genre
))
@ -353,17 +353,18 @@ class LastGenrePlugin(plugins.BeetsPlugin):
album.genre, src = self._get_genre(album)
log.debug(u'added last.fm album genre ({0}): {1}'.format(
src, album.genre))
album.store()
if 'track' in self.sources:
for item in album.items():
item.genre, src = self._get_genre(item)
log.debug(u'added last.fm item genre ({0}): {1}'.format(
src, item.genre))
session.lib.store(item)
item.store()
else:
item = task.item
item.genre, src = self._get_genre(item)
log.debug(u'added last.fm item genre ({0}): {1}'.format(
src, item.genre))
session.lib.store(item)
item.store()

View file

@ -444,7 +444,7 @@ class LyricsPlugin(BeetsPlugin):
if write:
item.write()
lib.store(item)
item.store()
def get_lyrics(self, artist, title):
"""Fetch lyrics, trying each source in turn. Return a string or

View file

@ -24,14 +24,14 @@ from beets import config
log = logging.getLogger('beets')
def _print_and_apply_changes(lib, item, move, pretend, write):
def _print_and_apply_changes(lib, item, old_data, move, pretend, write):
"""Apply changes to an Item and preview them in the console. Return
a boolean indicating whether any changes were made.
"""
changes = {}
for key in library.ITEM_KEYS_META:
if item.dirty[key]:
changes[key] = item.old_data[key], getattr(item, key)
if key in item._dirty:
changes[key] = old_data[key], getattr(item, key)
if not changes:
return False
@ -53,7 +53,7 @@ def _print_and_apply_changes(lib, item, move, pretend, write):
log.error(u'could not sync {0}: {1}'.format(
util.displayable_path(item.path), exc))
return False
lib.store(item)
item.store()
return True
@ -69,7 +69,7 @@ def mbsync_singletons(lib, query, move, pretend, write):
.format(s.title))
continue
s.old_data = dict(s.record)
old_data = dict(s)
# Get the MusicBrainz recording info.
track_info = hooks.track_for_mbid(s.mb_trackid)
@ -80,7 +80,7 @@ def mbsync_singletons(lib, query, move, pretend, write):
# Apply.
with lib.transaction():
autotag.apply_item_metadata(s, track_info)
_print_and_apply_changes(lib, s, move, pretend, write)
_print_and_apply_changes(lib, s, old_data, move, pretend, write)
def mbsync_albums(lib, query, move, pretend, write):
@ -93,8 +93,7 @@ def mbsync_albums(lib, query, move, pretend, write):
continue
items = list(a.items())
for item in items:
item.old_data = dict(item.record)
old_data = {item: dict(item) for item in items}
# Get the MusicBrainz album information.
album_info = hooks.album_for_mbid(a.mb_albumid)
@ -116,8 +115,8 @@ def mbsync_albums(lib, query, move, pretend, write):
autotag.apply_metadata(album_info, mapping)
changed = False
for item in items:
changed = _print_and_apply_changes(lib, item, move, pretend,
write) or changed
changed |= _print_and_apply_changes(lib, item, old_data[item],
move, pretend, write)
if not changed:
# No change to any item.
continue

View file

@ -61,39 +61,41 @@ def _item(track_info, album_info, album_id):
t = track_info
a = album_info
return Item({'album_id': album_id,
'album': a.album,
'albumartist': a.artist,
'albumartist_credit': a.artist_credit,
'albumartist_sort': a.artist_sort,
'albumdisambig': a.albumdisambig,
'albumstatus': a.albumstatus,
'albumtype': a.albumtype,
'artist': t.artist,
'artist_credit': t.artist_credit,
'artist_sort': t.artist_sort,
'asin': a.asin,
'catalognum': a.catalognum,
'comp': a.va,
'country': a.country,
'day': a.day,
'disc': t.medium,
'disctitle': t.disctitle,
'disctotal': a.mediums,
'label': a.label,
'language': a.language,
'length': t.length,
'mb_albumid': a.album_id,
'mb_artistid': t.artist_id,
'mb_releasegroupid': a.releasegroup_id,
'mb_trackid': t.track_id,
'media': a.media,
'month': a.month,
'script': a.script,
'title': t.title,
'track': t.index,
'tracktotal': len(a.tracks),
'year': a.year})
return Item(
album_id = album_id,
album = a.album,
albumartist = a.artist,
albumartist_credit = a.artist_credit,
albumartist_sort = a.artist_sort,
albumdisambig = a.albumdisambig,
albumstatus = a.albumstatus,
albumtype = a.albumtype,
artist = t.artist,
artist_credit = t.artist_credit,
artist_sort = t.artist_sort,
asin = a.asin,
catalognum = a.catalognum,
comp = a.va,
country = a.country,
day = a.day,
disc = t.medium,
disctitle = t.disctitle,
disctotal = a.mediums,
label = a.label,
language = a.language,
length = t.length,
mb_albumid = a.album_id,
mb_artistid = t.artist_id,
mb_releasegroupid = a.releasegroup_id,
mb_trackid = t.track_id,
media = a.media,
month = a.month,
script = a.script,
title = t.title,
track = t.index,
tracktotal = len(a.tracks),
year = a.year,
)
class MissingPlugin(BeetsPlugin):

View file

@ -225,7 +225,7 @@ class ReplayGainPlugin(BeetsPlugin):
for item, info in zip(items, rgain_infos):
item.rg_track_gain = info['gain']
item.rg_track_peak = info['peak']
lib.store(item)
item.store()
log.debug(u'replaygain: applied track gain {0}, peak {1}'.format(
item.rg_track_gain,
@ -241,3 +241,4 @@ class ReplayGainPlugin(BeetsPlugin):
album.rg_album_gain,
album.rg_album_peak
))
album.store()

View file

@ -30,7 +30,7 @@ def _rep(obj, expand=False):
included.
"""
if isinstance(obj, beets.library.Item):
out = dict(obj.record)
out = dict(obj)
del out['path']
# Get the size (in bytes) of the backing file. This is useful

View file

@ -1,6 +1,39 @@
Changelog
=========
1.3.0 (in development)
----------------------
Albums and items now have **flexible attributes**. This means that, when you
want to store information about your music in the beets database, you're no
longer constrained to the set of fields it supports out of the box (title,
artist, track, etc.). Instead, you can use any field name you can think of and
treat it just like the built-in fields.
For example, you can use the :ref:`modify-cmd` command to set a new field on a
track::
$ beet modify mood=sexy artist:miguel
and then query your music based on that field::
$ beet ls mood:sunny
or use templates to see the value of the field::
$ beet ls -f '$title: $mood'
While this feature is nifty when used directly with the usual command-line
suspects, it's especially useful for plugin authors and for future beets
features. Stay tuned for great things built on this flexible attribute
infrastructure.
One side effect of this change: queries that include unknown fields will now
match *nothing* instead of *everything*. So if you type ``beet ls
fieldThatDoesNotExist:foo``, beets will now return no results, whereas
previous versions would spit out a warning and then list your entire library.
1.2.2 (August 27, 2013)
-----------------------

View file

@ -12,8 +12,8 @@ master_doc = 'index'
project = u'beets'
copyright = u'2012, Adrian Sampson'
version = '1.2'
release = '1.2.2'
version = '1.3'
release = '1.3.0'
pygments_style = 'sphinx'

View file

@ -357,18 +357,18 @@ To do so, define a subclass of the ``Query`` type from the ``beets.library``
module. Then, in the ``queries`` method of your plugin class, return a
dictionary mapping prefix strings to query classes.
One simple kind of query you can extend is the ``RegisteredFieldQuery``, which
implements string comparisons. To use it, create a subclass inheriting from
that class and override the ``value_match`` class method. (Remember the
``@classmethod`` decorator!) The following example plugin declares a query
using the ``@`` prefix to delimit exact string matches. The plugin will be
used if we issue a command like ``beet ls @something`` or ``beet ls
artist:@something``::
One simple kind of query you can extend is the ``FieldQuery``, which
implements string comparisons on fields. To use it, create a subclass
inheriting from that class and override the ``value_match`` class method.
(Remember the ``@classmethod`` decorator!) The following example plugin
declares a query using the ``@`` prefix to delimit exact string matches. The
plugin will be used if we issue a command like ``beet ls @something`` or
``beet ls artist:@something``::
from beets.plugins import BeetsPlugin
from beets.library import PluginQuery
from beets.library import FieldQuery
class ExactMatchQuery(PluginQuery):
class ExactMatchQuery(FieldQuery):
@classmethod
def value_match(self, pattern, val):
return pattern == val

View file

@ -42,7 +42,7 @@ if 'sdist' in sys.argv:
shutil.copytree(os.path.join(docdir, '_build', 'man'), mandir)
setup(name='beets',
version='1.2.2',
version='1.3.0',
description='music tagger and library organizer',
author='Adrian Sampson',
author_email='adrian@radbox.org',

View file

@ -45,35 +45,35 @@ _item_ident = 0
def item():
global _item_ident
_item_ident += 1
return beets.library.Item({
'title': u'the title',
'artist': u'the artist',
'albumartist': u'the album artist',
'album': u'the album',
'genre': u'the genre',
'composer': u'the composer',
'grouping': u'the grouping',
'year': 1,
'month': 2,
'day': 3,
'track': 4,
'tracktotal': 5,
'disc': 6,
'disctotal': 7,
'lyrics': u'the lyrics',
'comments': u'the comments',
'bpm': 8,
'comp': True,
'path': 'somepath' + str(_item_ident),
'length': 60.0,
'bitrate': 128000,
'format': 'FLAC',
'mb_trackid': 'someID-1',
'mb_albumid': 'someID-2',
'mb_artistid': 'someID-3',
'mb_albumartistid': 'someID-4',
'album_id': None,
})
return beets.library.Item(
title = u'the title',
artist = u'the artist',
albumartist = u'the album artist',
album = u'the album',
genre = u'the genre',
composer = u'the composer',
grouping = u'the grouping',
year = 1,
month = 2,
day = 3,
track = 4,
tracktotal = 5,
disc = 6,
disctotal = 7,
lyrics = u'the lyrics',
comments = u'the comments',
bpm = 8,
comp = True,
path = 'somepath' + str(_item_ident),
length = 60.0,
bitrate = 128000,
format = 'FLAC',
mb_trackid = 'someID-1',
mb_albumid = 'someID-2',
mb_artistid = 'someID-3',
mb_albumartistid = 'someID-4',
album_id = None,
)
# Dummy import session.
def import_session(lib=None, logfile=None, paths=[], query=[], cli=False):

Binary file not shown.

View file

@ -53,30 +53,30 @@ class PluralityTest(_common.TestCase):
plurality([])
def test_current_metadata_finds_pluralities(self):
items = [Item({'artist': 'The Beetles', 'album': 'The White Album'}),
Item({'artist': 'The Beatles', 'album': 'The White Album'}),
Item({'artist': 'The Beatles', 'album': 'Teh White Album'})]
items = [Item(artist='The Beetles', album='The White Album'),
Item(artist='The Beatles', album='The White Album'),
Item(artist='The Beatles', album='Teh White Album')]
likelies, consensus = match.current_metadata(items)
self.assertEqual(likelies['artist'], 'The Beatles')
self.assertEqual(likelies['album'], 'The White Album')
self.assertFalse(consensus['artist'])
def test_current_metadata_artist_consensus(self):
items = [Item({'artist': 'The Beatles', 'album': 'The White Album'}),
Item({'artist': 'The Beatles', 'album': 'The White Album'}),
Item({'artist': 'The Beatles', 'album': 'Teh White Album'})]
items = [Item(artist='The Beatles', album='The White Album'),
Item(artist='The Beatles', album='The White Album'),
Item(artist='The Beatles', album='Teh White Album')]
likelies, consensus = match.current_metadata(items)
self.assertEqual(likelies['artist'], 'The Beatles')
self.assertEqual(likelies['album'], 'The White Album')
self.assertTrue(consensus['artist'])
def test_albumartist_consensus(self):
items = [Item({'artist': 'tartist1', 'album': 'album',
'albumartist': 'aartist'}),
Item({'artist': 'tartist2', 'album': 'album',
'albumartist': 'aartist'}),
Item({'artist': 'tartist3', 'album': 'album',
'albumartist': 'aartist'})]
items = [Item(artist='tartist1', album='album',
albumartist='aartist'),
Item(artist='tartist2', album='album',
albumartist='aartist'),
Item(artist='tartist3', album='album',
albumartist='aartist')]
likelies, consensus = match.current_metadata(items)
self.assertEqual(likelies['artist'], 'aartist')
self.assertFalse(consensus['artist'])
@ -85,19 +85,17 @@ class PluralityTest(_common.TestCase):
fields = ['artist', 'album', 'albumartist', 'year', 'disctotal',
'mb_albumid', 'label', 'catalognum', 'country', 'media',
'albumdisambig']
items = [Item(dict((f, '%s_%s' % (f, i or 1)) for f in fields))
items = [Item(**dict((f, '%s_%s' % (f, i or 1)) for f in fields))
for i in range(5)]
likelies, _ = match.current_metadata(items)
for f in fields:
self.assertEqual(likelies[f], '%s_1' % f)
def _make_item(title, track, artist=u'some artist'):
return Item({
'title': title, 'track': track,
'artist': artist, 'album': u'some album',
'length': 1,
'mb_trackid': '', 'mb_albumid': '', 'mb_artistid': '',
})
return Item(title=title, track=track,
artist=artist, album=u'some album',
length=1,
mb_trackid='', mb_albumid='', mb_artistid='')
def _make_trackinfo():
return [
@ -560,10 +558,10 @@ class MultiDiscAlbumsInDirTest(_common.TestCase):
class AssignmentTest(unittest.TestCase):
def item(self, title, track):
return Item({
'title': title, 'track': track,
'mb_trackid': '', 'mb_albumid': '', 'mb_artistid': '',
})
return Item(
title=title, track=track,
mb_trackid='', mb_albumid='', mb_artistid='',
)
def test_reorder_when_track_numbers_incorrect(self):
items = []
@ -640,14 +638,14 @@ class AssignmentTest(unittest.TestCase):
def test_order_works_when_track_names_are_entirely_wrong(self):
# A real-world test case contributed by a user.
def item(i, length):
return Item({
'artist': u'ben harper',
'album': u'burn to shine',
'title': u'ben harper - Burn to Shine ' + str(i),
'track': i,
'length': length,
'mb_trackid': '', 'mb_albumid': '', 'mb_artistid': '',
})
return Item(
artist=u'ben harper',
album=u'burn to shine',
title=u'ben harper - Burn to Shine ' + str(i),
track=i,
length=length,
mb_trackid='', mb_albumid='', mb_artistid='',
)
items = []
items.append(item(1, 241.37243007106997))
items.append(item(2, 342.27781704375036))

View file

@ -40,8 +40,8 @@ def remove_lib():
if os.path.exists(TEMP_LIB):
os.unlink(TEMP_LIB)
def boracay(l):
return beets.library.Item(
l._connection().execute('select * from items where id=3').fetchone()
return beets.library.Item(l,
**l._connection().execute('select * from items where id=3').fetchone()
)
np = util.normpath
@ -56,13 +56,13 @@ class LoadTest(unittest.TestCase):
def test_load_restores_data_from_db(self):
original_title = self.i.title
self.i.title = 'something'
self.lib.load(self.i)
self.i.load()
self.assertEqual(original_title, self.i.title)
def test_load_clears_dirty_flags(self):
self.i.artist = 'something'
self.lib.load(self.i)
self.assertTrue(not self.i.dirty['artist'])
self.i.load()
self.assertTrue('artist' not in self.i._dirty)
class StoreTest(unittest.TestCase):
def setUp(self):
@ -74,7 +74,7 @@ class StoreTest(unittest.TestCase):
def test_store_changes_database_value(self):
self.i.year = 1987
self.lib.store(self.i)
self.i.store()
new_year = self.lib._connection().execute(
'select year from items where '
'title="Boracay"').fetchone()['year']
@ -82,8 +82,8 @@ class StoreTest(unittest.TestCase):
def test_store_only_writes_dirty_fields(self):
original_genre = self.i.genre
self.i.record['genre'] = 'beatboxing' # change value w/o dirtying
self.lib.store(self.i)
self.i._values_fixed['genre'] = 'beatboxing' # change w/o dirtying
self.i.store()
new_genre = self.lib._connection().execute(
'select genre from items where '
'title="Boracay"').fetchone()['genre']
@ -91,8 +91,8 @@ class StoreTest(unittest.TestCase):
def test_store_clears_dirty_flags(self):
self.i.composer = 'tvp'
self.lib.store(self.i)
self.assertTrue(not self.i.dirty['composer'])
self.i.store()
self.assertTrue('composer' not in self.i._dirty)
class AddTest(unittest.TestCase):
def setUp(self):
@ -139,11 +139,11 @@ class GetSetTest(unittest.TestCase):
def test_set_sets_dirty_flag(self):
self.i.comp = not self.i.comp
self.assertTrue(self.i.dirty['comp'])
self.assertTrue('comp' in self.i._dirty)
def test_set_does_not_dirty_if_value_unchanged(self):
self.i.title = self.i.title
self.assertTrue(not self.i.dirty['title'])
self.assertTrue('title' not in self.i._dirty)
def test_invalid_field_raises_attributeerror(self):
self.assertRaises(AttributeError, getattr, self.i, 'xyzzy')
@ -533,21 +533,21 @@ class DisambiguationTest(unittest.TestCase, PathFormattingMixin):
def test_unique_with_default_arguments_uses_albumtype(self):
album2 = self.lib.get_album(self.i1)
album2.albumtype = 'bar'
self.lib._connection().commit()
album2.store()
self._setf(u'foo%aunique{}/$title')
self._assert_dest('/base/foo [bar]/the title', self.i1)
def test_unique_expands_to_nothing_for_distinct_albums(self):
album2 = self.lib.get_album(self.i2)
album2.album = 'different album'
self.lib._connection().commit()
album2.store()
self._assert_dest('/base/foo/the title', self.i1)
def test_use_fallback_numbers_when_identical(self):
album2 = self.lib.get_album(self.i2)
album2.year = 2001
self.lib._connection().commit()
album2.store()
self._assert_dest('/base/foo 1/the title', self.i1)
self._assert_dest('/base/foo 2/the title', self.i2)
@ -561,6 +561,8 @@ class DisambiguationTest(unittest.TestCase, PathFormattingMixin):
album2.year = 2001
album1 = self.lib.get_album(self.i1)
album1.albumtype = 'foo/bar'
album2.store()
album1.store()
self._setf(u'foo%aunique{albumartist album,albumtype}/$title')
self._assert_dest('/base/foo [foo_bar]/the title', self.i1)
@ -757,6 +759,7 @@ class AlbumInfoTest(unittest.TestCase):
def test_albuminfo_stores_art(self):
ai = self.lib.get_album(self.i)
ai.artpath = '/my/great/art'
ai.store()
new_ai = self.lib.get_album(self.i)
self.assertEqual(new_ai.artpath, '/my/great/art')
@ -795,20 +798,23 @@ class AlbumInfoTest(unittest.TestCase):
def test_albuminfo_changes_affect_items(self):
ai = self.lib.get_album(self.i)
ai.album = 'myNewAlbum'
i = self.lib.items().next()
ai.store()
i = self.lib.items()[0]
self.assertEqual(i.album, 'myNewAlbum')
def test_albuminfo_change_albumartist_changes_items(self):
ai = self.lib.get_album(self.i)
ai.albumartist = 'myNewArtist'
i = self.lib.items().next()
ai.store()
i = self.lib.items()[0]
self.assertEqual(i.albumartist, 'myNewArtist')
self.assertNotEqual(i.artist, 'myNewArtist')
def test_albuminfo_change_artist_does_not_change_items(self):
ai = self.lib.get_album(self.i)
ai.artist = 'myNewArtist'
i = self.lib.items().next()
ai.store()
i = self.lib.items()[0]
self.assertNotEqual(i.artist, 'myNewArtist')
def test_albuminfo_remove_removes_items(self):
@ -824,15 +830,6 @@ class AlbumInfoTest(unittest.TestCase):
self.lib.remove(self.i)
self.assertEqual(len(self.lib.albums()), 0)
class BaseAlbumTest(_common.TestCase):
def test_field_access(self):
album = beets.library.BaseAlbum(None, {'fld1':'foo'})
self.assertEqual(album.fld1, 'foo')
def test_field_access_unset_values(self):
album = beets.library.BaseAlbum(None, {})
self.assertRaises(AttributeError, getattr, album, 'field')
class ArtDestinationTest(_common.TestCase):
def setUp(self):
super(ArtDestinationTest, self).setUp()
@ -887,7 +884,7 @@ class PathStringTest(_common.TestCase):
def test_special_chars_preserved_in_database(self):
path = 'b\xe1r'
self.i.path = path
self.lib.store(self.i)
self.i.store()
i = list(self.lib.items())[0]
self.assertEqual(i.path, path)
@ -912,9 +909,10 @@ class PathStringTest(_common.TestCase):
self.assert_(isinstance(dest, str))
def test_artpath_stores_special_chars(self):
path = 'b\xe1r'
path = b'b\xe1r'
alb = self.lib.add_album([self.i])
alb.artpath = path
alb.store()
alb = self.lib.get_album(self.i)
self.assertEqual(path, alb.artpath)

View file

@ -170,7 +170,8 @@ class AlbumFileTest(_common.TestCase):
def test_albuminfo_move_changes_paths(self):
self.ai.album = 'newAlbumName'
self.ai.move()
self.lib.load(self.i)
self.ai.store()
self.i.load()
self.assert_('newAlbumName' in self.i.path)
@ -178,7 +179,8 @@ class AlbumFileTest(_common.TestCase):
oldpath = self.i.path
self.ai.album = 'newAlbumName'
self.ai.move()
self.lib.load(self.i)
self.ai.store()
self.i.load()
self.assertFalse(os.path.exists(oldpath))
self.assertTrue(os.path.exists(self.i.path))
@ -187,14 +189,16 @@ class AlbumFileTest(_common.TestCase):
oldpath = self.i.path
self.ai.album = 'newAlbumName'
self.ai.move(True)
self.lib.load(self.i)
self.ai.store()
self.i.load()
self.assertTrue(os.path.exists(oldpath))
self.assertTrue(os.path.exists(self.i.path))
def test_albuminfo_move_to_custom_dir(self):
self.ai.move(basedir=self.otherdir)
self.lib.load(self.i)
self.i.load()
self.ai.store()
self.assertTrue('testotherdir' in self.i.path)
class ArtFileTest(_common.TestCase):
@ -216,6 +220,7 @@ class ArtFileTest(_common.TestCase):
self.art = self.lib.get_album(self.i).art_destination('something.jpg')
touch(self.art)
self.ai.artpath = self.art
self.ai.store()
# Alternate destination dir.
self.otherdir = os.path.join(self.temp_dir, 'testotherdir')
@ -229,7 +234,7 @@ class ArtFileTest(_common.TestCase):
oldpath = self.i.path
self.ai.album = 'newAlbum'
self.ai.move()
self.lib.load(self.i)
self.i.load()
self.assertNotEqual(self.i.path, oldpath)
self.assertFalse(os.path.exists(self.art))
@ -239,7 +244,8 @@ class ArtFileTest(_common.TestCase):
def test_art_moves_with_album_to_custom_dir(self):
# Move the album to another directory.
self.ai.move(basedir=self.otherdir)
self.lib.load(self.i)
self.ai.store()
self.i.load()
# Art should be in new directory.
self.assertNotExists(self.art)
@ -344,7 +350,8 @@ class ArtFileTest(_common.TestCase):
self.assertExists(oldartpath)
self.ai.album = 'different_album'
self.lib.move(self.i)
self.ai.store()
self.lib.move(self.ai.items()[0])
artpath = self.lib.albums()[0].artpath
self.assertTrue('different_album' in artpath)
@ -421,6 +428,7 @@ class RemoveTest(_common.TestCase):
artfile = os.path.join(self.temp_dir, 'testart.jpg')
touch(artfile)
self.ai.set_art(artfile)
self.ai.store()
parent = os.path.dirname(self.i.path)
self.lib.remove(self.i, True)

View file

@ -16,7 +16,7 @@ class IHatePluginTest(unittest.TestCase):
task = ImportTask()
task.cur_artist = u'Test Artist'
task.cur_album = u'Test Album'
task.items = [Item({'genre': 'Test Genre'})]
task.items = [Item(genre='Test Genre')]
self.assertFalse(IHatePlugin.do_i_hate_this(task, genre_p, artist_p,
album_p, white_p))
genre_p = 'some_genre test\sgenre'.split()

View file

@ -193,6 +193,7 @@ class ImportApplyTest(_common.TestCase):
shutil.copy(os.path.join(_common.RSRC, 'full.mp3'), self.srcpath)
self.i = library.Item.from_path(self.srcpath)
self.i.comp = False
self.lib.add(self.i)
trackinfo = TrackInfo('one', 'trackid', 'some artist',
'artistid', 1)
@ -406,7 +407,7 @@ class ApplyExistingItemsTest(_common.TestCase):
self._apply_asis([self.i])
# Get the item's path and import it again.
item = self.lib.items().next()
item = self.lib.items().get()
new_item = library.Item.from_path(item.path)
self._apply_asis([new_item])
@ -416,7 +417,7 @@ class ApplyExistingItemsTest(_common.TestCase):
def test_apply_existing_album_does_not_duplicate_album(self):
# As above.
self._apply_asis([self.i])
item = self.lib.items().next()
item = self.lib.items().get()
new_item = library.Item.from_path(item.path)
self._apply_asis([new_item])
@ -425,7 +426,7 @@ class ApplyExistingItemsTest(_common.TestCase):
def test_apply_existing_singleton_does_not_duplicate_album(self):
self._apply_asis([self.i])
item = self.lib.items().next()
item = self.lib.items().get()
new_item = library.Item.from_path(item.path)
self._apply_asis([new_item], False)
@ -440,7 +441,7 @@ class ApplyExistingItemsTest(_common.TestCase):
self._apply_asis([self.i])
# Import again with new metadata.
item = self.lib.items().next()
item = self.lib.items().get()
new_item = library.Item.from_path(item.path)
new_item.title = 'differentTitle'
self._apply_asis([new_item])
@ -454,12 +455,12 @@ class ApplyExistingItemsTest(_common.TestCase):
config['import']['copy'] = True
self._apply_asis([self.i])
item = self.lib.items().next()
item = self.lib.items().get()
new_item = library.Item.from_path(item.path)
new_item.title = 'differentTitle'
self._apply_asis([new_item])
item = self.lib.items().next()
item = self.lib.items().get()
self.assertTrue('differentTitle' in item.path)
self.assertExists(item.path)
@ -468,12 +469,12 @@ class ApplyExistingItemsTest(_common.TestCase):
config['import']['copy'] = False
self._apply_asis([self.i])
item = self.lib.items().next()
item = self.lib.items().get()
new_item = library.Item.from_path(item.path)
new_item.title = 'differentTitle'
self._apply_asis([new_item])
item = self.lib.items().next()
item = self.lib.items().get()
self.assertFalse('differentTitle' in item.path)
self.assertExists(item.path)
@ -481,13 +482,13 @@ class ApplyExistingItemsTest(_common.TestCase):
config['import']['copy'] = True
self._apply_asis([self.i])
item = self.lib.items().next()
item = self.lib.items().get()
oldpath = item.path
new_item = library.Item.from_path(item.path)
new_item.title = 'differentTitle'
self._apply_asis([new_item])
item = self.lib.items().next()
item = self.lib.items().get()
self.assertNotExists(oldpath)
def test_apply_existing_item_new_metadata_delete_enabled(self):
@ -497,13 +498,13 @@ class ApplyExistingItemsTest(_common.TestCase):
config['import']['delete'] = True # !
self._apply_asis([self.i])
item = self.lib.items().next()
item = self.lib.items().get()
oldpath = item.path
new_item = library.Item.from_path(item.path)
new_item.title = 'differentTitle'
self._apply_asis([new_item])
item = self.lib.items().next()
item = self.lib.items().get()
self.assertNotExists(oldpath)
self.assertTrue('differentTitle' in item.path)
self.assertExists(item.path)
@ -513,13 +514,13 @@ class ApplyExistingItemsTest(_common.TestCase):
config['import']['copy'] = True
self._apply_asis([self.i])
item = self.lib.items().next()
item = self.lib.items().get()
oldpath = item.path
new_item = library.Item.from_path(item.path)
self._apply_asis([new_item])
self.assertEqual(len(list(self.lib.items())), 1)
item = self.lib.items().next()
item = self.lib.items().get()
self.assertEqual(oldpath, item.path)
self.assertExists(oldpath)
@ -528,19 +529,19 @@ class ApplyExistingItemsTest(_common.TestCase):
config['import']['delete'] = True # !
self._apply_asis([self.i])
item = self.lib.items().next()
item = self.lib.items().get()
new_item = library.Item.from_path(item.path)
self._apply_asis([new_item])
self.assertEqual(len(list(self.lib.items())), 1)
item = self.lib.items().next()
item = self.lib.items().get()
self.assertExists(item.path)
def test_same_album_does_not_duplicate(self):
# With the -L flag, exactly the same item (with the same ID)
# is re-imported. This test simulates that situation.
self._apply_asis([self.i])
item = self.lib.items().next()
item = self.lib.items().get()
self._apply_asis([item])
# Should not be duplicated.
@ -704,6 +705,7 @@ class DuplicateCheckTest(_common.TestCase):
def test_duplicate_va_album(self):
self.album.albumartist = 'an album artist'
self.album.store()
res = importer._duplicate_check(self.lib,
self._album_task(False, 'an album artist'))
self.assertTrue(res)

View file

@ -78,30 +78,30 @@ class AnyFieldQueryTest(unittest.TestCase):
def test_no_restriction(self):
q = beets.library.AnyFieldQuery('title', beets.library.ITEM_KEYS,
beets.library.SubstringQuery)
self.assertEqual(self.lib.items(q).next().title, 'the title')
self.assertEqual(self.lib.items(q).get().title, 'the title')
def test_restriction_completeness(self):
q = beets.library.AnyFieldQuery('title', ['title'],
beets.library.SubstringQuery)
self.assertEqual(self.lib.items(q).next().title, 'the title')
self.assertEqual(self.lib.items(q).get().title, 'the title')
def test_restriction_soundness(self):
q = beets.library.AnyFieldQuery('title', ['artist'],
beets.library.SubstringQuery)
self.assertRaises(StopIteration, self.lib.items(q).next)
self.assertEqual(self.lib.items(q).get(), None)
# Convenient asserts for matching items.
class AssertsMixin(object):
def assert_matched(self, result_iterator, title):
self.assertEqual(result_iterator.next().title, title)
def assert_done(self, result_iterator):
self.assertRaises(StopIteration, result_iterator.next)
def assert_matched_all(self, result_iterator):
self.assert_matched(result_iterator, 'Littlest Things')
self.assert_matched(result_iterator, 'Take Pills')
self.assert_matched(result_iterator, 'Lovers Who Uncover')
self.assert_matched(result_iterator, 'Boracay')
self.assert_done(result_iterator)
def assert_matched(self, results, titles):
self.assertEqual([i.title for i in results], titles)
def assert_matched_all(self, results):
self.assert_matched(results, [
'Littlest Things',
'Take Pills',
'Lovers Who Uncover',
'Boracay',
])
class GetTest(unittest.TestCase, AssertsMixin):
def setUp(self):
@ -122,127 +122,126 @@ class GetTest(unittest.TestCase, AssertsMixin):
def test_get_one_keyed_term(self):
q = 'artist:Lil'
results = self.lib.items(q)
self.assert_matched(results, 'Littlest Things')
self.assert_done(results)
self.assert_matched(results, ['Littlest Things'])
def test_get_one_keyed_regexp(self):
q = r'artist::L.+y'
results = self.lib.items(q)
self.assert_matched(results, 'Littlest Things')
self.assert_done(results)
self.assert_matched(results, ['Littlest Things'])
def test_get_one_unkeyed_term(self):
q = 'Terry'
results = self.lib.items(q)
self.assert_matched(results, 'Boracay')
self.assert_done(results)
self.assert_matched(results, ['Boracay'])
def test_get_one_unkeyed_regexp(self):
q = r':y$'
results = self.lib.items(q)
self.assert_matched(results, 'Boracay')
self.assert_done(results)
self.assert_matched(results, ['Boracay'])
def test_get_no_matches(self):
q = 'popebear'
results = self.lib.items(q)
self.assert_done(results)
self.assert_matched(results, [])
def test_invalid_key(self):
q = 'pope:bear'
results = self.lib.items(q)
self.assert_matched_all(results)
# Matches nothing since the flexattr is not present on the
# objects.
self.assert_matched(results, [])
def test_term_case_insensitive(self):
q = 'UNCoVER'
results = self.lib.items(q)
self.assert_matched(results, 'Lovers Who Uncover')
self.assert_done(results)
self.assert_matched(results, ['Lovers Who Uncover'])
def test_regexp_case_sensitive(self):
q = r':UNCoVER'
results = self.lib.items(q)
self.assert_done(results)
self.assert_matched(results, [])
q = r':Uncover'
results = self.lib.items(q)
self.assert_matched(results, 'Lovers Who Uncover')
self.assert_done(results)
self.assert_matched(results, ['Lovers Who Uncover'])
def test_term_case_insensitive_with_key(self):
q = 'album:stiLL'
results = self.lib.items(q)
self.assert_matched(results, 'Littlest Things')
self.assert_done(results)
self.assert_matched(results, ['Littlest Things'])
def test_key_case_insensitive(self):
q = 'ArTiST:Allen'
results = self.lib.items(q)
self.assert_matched(results, 'Littlest Things')
self.assert_done(results)
self.assert_matched(results, ['Littlest Things'])
def test_unkeyed_term_matches_multiple_columns(self):
q = 'little'
results = self.lib.items(q)
self.assert_matched(results, 'Littlest Things')
self.assert_matched(results, 'Lovers Who Uncover')
self.assert_matched(results, 'Boracay')
self.assert_done(results)
self.assert_matched(results, [
'Littlest Things',
'Lovers Who Uncover',
'Boracay',
])
def test_unkeyed_regexp_matches_multiple_columns(self):
q = r':^T'
results = self.lib.items(q)
self.assert_matched(results, 'Take Pills')
self.assert_matched(results, 'Lovers Who Uncover')
self.assert_matched(results, 'Boracay')
self.assert_done(results)
self.assert_matched(results, [
'Take Pills',
'Lovers Who Uncover',
'Boracay',
])
def test_keyed_term_matches_only_one_column(self):
q = 'artist:little'
results = self.lib.items(q)
self.assert_matched(results, 'Lovers Who Uncover')
self.assert_matched(results, 'Boracay')
self.assert_done(results)
self.assert_matched(results, [
'Lovers Who Uncover',
'Boracay',
])
def test_keyed_regexp_matches_only_one_column(self):
q = r'album::\sS'
results = self.lib.items(q)
self.assert_matched(results, 'Littlest Things')
self.assert_matched(results, 'Lovers Who Uncover')
self.assert_done(results)
self.assert_matched(results, [
'Littlest Things',
'Lovers Who Uncover',
])
def test_multiple_terms_narrow_search(self):
q = 'little ones'
results = self.lib.items(q)
self.assert_matched(results, 'Lovers Who Uncover')
self.assert_matched(results, 'Boracay')
self.assert_done(results)
self.assert_matched(results, [
'Lovers Who Uncover',
'Boracay',
])
def test_multiple_regexps_narrow_search(self):
q = r':\sS :^T'
results = self.lib.items(q)
self.assert_matched(results, 'Lovers Who Uncover')
self.assert_done(results)
self.assert_matched(results, ['Lovers Who Uncover'])
def test_mixed_terms_regexps_narrow_search(self):
q = r':\sS lily'
results = self.lib.items(q)
self.assert_matched(results, 'Littlest Things')
self.assert_done(results)
self.assert_matched(results, ['Littlest Things'])
def test_single_year(self):
q = 'year:2006'
results = self.lib.items(q)
self.assert_matched(results, 'Littlest Things')
self.assert_matched(results, 'Lovers Who Uncover')
self.assert_done(results)
self.assert_matched(results, [
'Littlest Things',
'Lovers Who Uncover',
])
def test_year_range(self):
q = 'year:2000..2010'
results = self.lib.items(q)
self.assert_matched(results, 'Littlest Things')
self.assert_matched(results, 'Take Pills')
self.assert_matched(results, 'Lovers Who Uncover')
self.assert_done(results)
self.assert_matched(results, [
'Littlest Things',
'Take Pills',
'Lovers Who Uncover',
])
def test_bad_year(self):
q = 'year:delete from items'
@ -263,55 +262,48 @@ class MemoryGetTest(unittest.TestCase, AssertsMixin):
def test_singleton_true(self):
q = 'singleton:true'
results = self.lib.items(q)
self.assert_matched(results, 'singleton item')
self.assert_done(results)
self.assert_matched(results, ['singleton item'])
def test_singleton_false(self):
q = 'singleton:false'
results = self.lib.items(q)
self.assert_matched(results, 'album item')
self.assert_done(results)
self.assert_matched(results, ['album item'])
def test_compilation_true(self):
q = 'comp:true'
results = self.lib.items(q)
self.assert_matched(results, 'album item')
self.assert_done(results)
self.assert_matched(results, ['album item'])
def test_compilation_false(self):
q = 'comp:false'
results = self.lib.items(q)
self.assert_matched(results, 'singleton item')
self.assert_done(results)
self.assert_matched(results, ['singleton item'])
def test_unknown_field_name_ignored(self):
def test_unknown_field_name_no_results(self):
q = 'xyzzy:nonsense'
results = self.lib.items(q)
titles = [i.title for i in results]
self.assertTrue('singleton item' in titles)
self.assertTrue('album item' in titles)
self.assertEqual(len(titles), 2)
self.assertEqual(titles, [])
def test_unknown_field_name_ignored_in_album_query(self):
def test_unknown_field_name_no_results_in_album_query(self):
q = 'xyzzy:nonsense'
results = self.lib.albums(q)
names = [a.album for a in results]
self.assertEqual(names, ['the album'])
self.assertEqual(names, [])
def test_item_field_name_ignored_in_album_query(self):
def test_item_field_name_matches_nothing_in_album_query(self):
q = 'format:nonsense'
results = self.lib.albums(q)
names = [a.album for a in results]
self.assertEqual(names, ['the album'])
self.assertEqual(names, [])
def test_unicode_query(self):
self.single_item.title = u'caf\xe9'
self.lib.store(self.single_item)
self.single_item.store()
q = u'title:caf\xe9'
results = self.lib.items(q)
self.assert_matched(results, u'caf\xe9')
self.assert_done(results)
self.assert_matched(results, [u'caf\xe9'])
class MatchTest(unittest.TestCase):
def setUp(self):
@ -369,58 +361,52 @@ class PathQueryTest(unittest.TestCase, AssertsMixin):
def test_path_exact_match(self):
q = 'path:/a/b/c.mp3'
results = self.lib.items(q)
self.assert_matched(results, 'path item')
self.assert_done(results)
self.assert_matched(results, ['path item'])
def test_parent_directory_no_slash(self):
q = 'path:/a'
results = self.lib.items(q)
self.assert_matched(results, 'path item')
self.assert_done(results)
self.assert_matched(results, ['path item'])
def test_parent_directory_with_slash(self):
q = 'path:/a/'
results = self.lib.items(q)
self.assert_matched(results, 'path item')
self.assert_done(results)
self.assert_matched(results, ['path item'])
def test_no_match(self):
q = 'path:/xyzzy/'
results = self.lib.items(q)
self.assert_done(results)
self.assert_matched(results, [])
def test_fragment_no_match(self):
q = 'path:/b/'
results = self.lib.items(q)
self.assert_done(results)
self.assert_matched(results, [])
def test_nonnorm_path(self):
q = 'path:/x/../a/b'
results = self.lib.items(q)
self.assert_matched(results, 'path item')
self.assert_done(results)
self.assert_matched(results, ['path item'])
def test_slashed_query_matches_path(self):
q = '/a/b'
results = self.lib.items(q)
self.assert_matched(results, 'path item')
self.assert_done(results)
self.assert_matched(results, ['path item'])
def test_non_slashed_does_not_match_path(self):
q = 'c.mp3'
results = self.lib.items(q)
self.assert_done(results)
self.assert_matched(results, [])
def test_slashes_in_explicit_field_does_not_match_path(self):
q = 'title:/a/b'
results = self.lib.items(q)
self.assert_done(results)
self.assert_matched(results, [])
def test_path_regex(self):
q = 'path::\\.mp3$'
results = self.lib.items(q)
self.assert_matched(results, 'path item')
self.assert_done(results)
self.assert_matched(results, ['path item'])
class BrowseTest(unittest.TestCase, AssertsMixin):
def setUp(self):
@ -438,11 +424,12 @@ class BrowseTest(unittest.TestCase, AssertsMixin):
def test_item_list(self):
items = self.lib.items()
self.assert_matched(items, 'Littlest Things')
self.assert_matched(items, 'Take Pills')
self.assert_matched(items, 'Lovers Who Uncover')
self.assert_matched(items, 'Boracay')
self.assert_done(items)
self.assert_matched(items, [
'Littlest Things',
'Take Pills',
'Lovers Who Uncover',
'Boracay',
])
def test_albums_matches_album(self):
albums = list(self.lib.albums('person'))
@ -454,31 +441,11 @@ class BrowseTest(unittest.TestCase, AssertsMixin):
def test_items_matches_title(self):
items = self.lib.items('boracay')
self.assert_matched(items, 'Boracay')
self.assert_done(items)
self.assert_matched(items, ['Boracay'])
def test_items_does_not_match_year(self):
items = self.lib.items('2007')
self.assert_done(items)
class CountTest(unittest.TestCase):
def setUp(self):
self.lib = beets.library.Library(':memory:')
self.item = some_item
self.lib.add(self.item)
def test_count_gets_single_item(self):
with self.lib.transaction() as tx:
songs, totaltime = beets.library.TrueQuery().count(tx)
self.assertEqual(songs, 1)
self.assertEqual(totaltime, self.item.length)
def test_count_works_for_empty_library(self):
self.lib.remove(self.item)
with self.lib.transaction() as tx:
songs, totaltime = beets.library.TrueQuery().count(tx)
self.assertEqual(songs, 0)
self.assertEqual(totaltime, 0.0)
self.assert_matched(items, [])
class StringParseTest(unittest.TestCase):
def test_single_field_query(self):

View file

@ -17,7 +17,6 @@
import os
import shutil
import textwrap
import logging
import re
import yaml
@ -55,7 +54,7 @@ class ListTest(_common.TestCase):
def test_list_unicode_query(self):
self.item.title = u'na\xefve'
self.lib.store(self.item)
self.item.store()
self.lib._connection().commit()
self._run_list([u'na\xefve'])
@ -162,7 +161,7 @@ class ModifyTest(_common.TestCase):
def test_modify_item_dbdata(self):
self._modify(["title=newTitle"])
item = self.lib.items().next()
item = self.lib.items().get()
self.assertEqual(item.title, 'newTitle')
def test_modify_album_dbdata(self):
@ -172,47 +171,47 @@ class ModifyTest(_common.TestCase):
def test_modify_item_tag_unmodified(self):
self._modify(["title=newTitle"], write=False)
item = self.lib.items().next()
item = self.lib.items().get()
item.read()
self.assertEqual(item.title, 'full')
def test_modify_album_tag_unmodified(self):
self._modify(["album=newAlbum"], write=False, album=True)
item = self.lib.items().next()
item = self.lib.items().get()
item.read()
self.assertEqual(item.album, 'the album')
def test_modify_item_tag(self):
self._modify(["title=newTitle"], write=True)
item = self.lib.items().next()
item = self.lib.items().get()
item.read()
self.assertEqual(item.title, 'newTitle')
def test_modify_album_tag(self):
self._modify(["album=newAlbum"], write=True, album=True)
item = self.lib.items().next()
item = self.lib.items().get()
item.read()
self.assertEqual(item.album, 'newAlbum')
def test_item_move(self):
self._modify(["title=newTitle"], move=True)
item = self.lib.items().next()
item = self.lib.items().get()
self.assertTrue('newTitle' in item.path)
def test_album_move(self):
self._modify(["album=newAlbum"], move=True, album=True)
item = self.lib.items().next()
item = self.lib.items().get()
item.read()
self.assertTrue('newAlbum' in item.path)
def test_item_not_move(self):
self._modify(["title=newTitle"], move=False)
item = self.lib.items().next()
item = self.lib.items().get()
self.assertFalse('newTitle' in item.path)
def test_album_not_move(self):
self._modify(["album=newAlbum"], move=False, album=True)
item = self.lib.items().next()
item = self.lib.items().get()
item.read()
self.assertFalse('newAlbum' in item.path)
@ -242,42 +241,42 @@ class MoveTest(_common.TestCase):
def test_move_item(self):
self._move()
self.lib.load(self.i)
self.i.load()
self.assertTrue('testlibdir' in self.i.path)
self.assertExists(self.i.path)
self.assertNotExists(self.itempath)
def test_copy_item(self):
self._move(copy=True)
self.lib.load(self.i)
self.i.load()
self.assertTrue('testlibdir' in self.i.path)
self.assertExists(self.i.path)
self.assertExists(self.itempath)
def test_move_album(self):
self._move(album=True)
self.lib.load(self.i)
self.i.load()
self.assertTrue('testlibdir' in self.i.path)
self.assertExists(self.i.path)
self.assertNotExists(self.itempath)
def test_copy_album(self):
self._move(copy=True, album=True)
self.lib.load(self.i)
self.i.load()
self.assertTrue('testlibdir' in self.i.path)
self.assertExists(self.i.path)
self.assertExists(self.itempath)
def test_move_item_custom_dir(self):
self._move(dest=self.otherdir)
self.lib.load(self.i)
self.i.load()
self.assertTrue('testotherdir' in self.i.path)
self.assertExists(self.i.path)
self.assertNotExists(self.itempath)
def test_move_album_custom_dir(self):
self._move(dest=self.otherdir, album=True)
self.lib.load(self.i)
self.i.load()
self.assertTrue('testotherdir' in self.i.path)
self.assertExists(self.i.path)
self.assertNotExists(self.itempath)
@ -300,13 +299,14 @@ class UpdateTest(_common.TestCase):
artfile = os.path.join(_common.RSRC, 'testart.jpg')
_common.touch(artfile)
self.album.set_art(artfile)
self.album.store()
os.remove(artfile)
def _update(self, query=(), album=False, move=False, reset_mtime=True):
self.io.addinput('y')
if reset_mtime:
self.i.mtime = 0
self.lib.store(self.i)
self.i.store()
commands.update_items(self.lib, query, album, move, False)
def test_delete_removes_item(self):
@ -333,7 +333,7 @@ class UpdateTest(_common.TestCase):
mf.title = 'differentTitle'
mf.save()
self._update()
item = self.lib.items().next()
item = self.lib.items().get()
self.assertEqual(item.title, 'differentTitle')
def test_modified_metadata_moved(self):
@ -341,7 +341,7 @@ class UpdateTest(_common.TestCase):
mf.title = 'differentTitle'
mf.save()
self._update(move=True)
item = self.lib.items().next()
item = self.lib.items().get()
self.assertTrue('differentTitle' in item.path)
def test_modified_metadata_not_moved(self):
@ -349,7 +349,7 @@ class UpdateTest(_common.TestCase):
mf.title = 'differentTitle'
mf.save()
self._update(move=False)
item = self.lib.items().next()
item = self.lib.items().get()
self.assertTrue('differentTitle' not in item.path)
def test_modified_album_metadata_moved(self):
@ -357,7 +357,7 @@ class UpdateTest(_common.TestCase):
mf.album = 'differentAlbum'
mf.save()
self._update(move=True)
item = self.lib.items().next()
item = self.lib.items().get()
self.assertTrue('differentAlbum' in item.path)
def test_modified_album_metadata_art_moved(self):
@ -376,10 +376,10 @@ class UpdateTest(_common.TestCase):
# Make in-memory mtime match on-disk mtime.
self.i.mtime = os.path.getmtime(self.i.path)
self.lib.store(self.i)
self.i.store()
self._update(reset_mtime=False)
item = self.lib.items().next()
item = self.lib.items().get()
self.assertEqual(item.title, 'full')
class PrintTest(_common.TestCase):

View file

@ -7,11 +7,12 @@ from beetsplug.zero import ZeroPlugin
class ZeroPluginTest(unittest.TestCase):
def test_no_patterns(self):
v = {'comments' : 'test comment',
'day' : 13,
'month' : 3,
'year' : 2012}
i=Item(v)
i = Item(
comments='test comment',
day=13,
month=3,
year=2012,
)
z = ZeroPlugin()
z.debug = False
z.fields = ['comments', 'month', 'day']
@ -25,9 +26,10 @@ class ZeroPluginTest(unittest.TestCase):
self.assertEqual(i.year, 2012)
def test_patterns(self):
v = {'comments' : 'from lame collection, ripped by eac',
'year' : 2012}
i=Item(v)
i = Item(
comments='from lame collection, ripped by eac',
year=2012,
)
z = ZeroPlugin()
z.debug = False
z.fields = ['comments', 'year']