From 52d0c19fb779da1b0ba83fdbbebec7dd1ce0a59f Mon Sep 17 00:00:00 2001 From: Mook Date: Sun, 13 Apr 2014 19:41:22 -0700 Subject: [PATCH 1/2] `beet modify`: Treat arguments with : before = as queries, not modifications This can be useful if the value being queried contains an equal sign. --- beets/ui/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beets/ui/commands.py b/beets/ui/commands.py index cc5b14cfe..fd91c0dcd 100644 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -1170,7 +1170,7 @@ def modify_func(lib, opts, args): for arg in args: if arg.endswith('!') and '=' not in arg and ':' not in arg: dels.append(arg[:-1]) # Strip trailing !. - elif '=' in arg: + elif '=' in arg and ':' not in arg.split('=', 1)[0]: mods.append(arg) else: query.append(arg) From 12a5215afa0a0fe3d0aeb38d00a5547366258eae Mon Sep 17 00:00:00 2001 From: Mook Date: Sun, 13 Apr 2014 21:24:45 -0700 Subject: [PATCH 2/2] `beet modify`: Add simple argument parsing tests. --- beets/ui/commands.py | 31 +++++++++++++++++++------------ test/test_ui.py | 24 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/beets/ui/commands.py b/beets/ui/commands.py index fd91c0dcd..a4cd77763 100644 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -1145,6 +1145,24 @@ def modify_items(lib, mods, dels, query, write, move, album, confirm): for item in changed_items: item.try_write() + +def modify_parse_args(args): + """Split the arguments for the modify subcommand into query parts, + assignments (field=value), and deletions (field!). Returns the result as + a three-tuple in that order. + """ + mods = [] + dels = [] + query = [] + for arg in args: + if arg.endswith('!') and '=' not in arg and ':' not in arg: + dels.append(arg[:-1]) # Strip trailing !. + elif '=' in arg and ':' not in arg.split('=', 1)[0]: + mods.append(arg) + else: + query.append(arg) + return (query, mods, dels) + modify_cmd = ui.Subcommand('modify', help='change metadata fields', aliases=('mod',)) modify_cmd.parser.add_option('-M', '--nomove', action='store_false', @@ -1162,18 +1180,7 @@ modify_cmd.parser.add_option('-f', '--format', action='store', def modify_func(lib, opts, args): args = decargs(args) - # Split the arguments into query parts, assignments (field=value), - # and deletions (field!). - mods = [] - dels = [] - query = [] - for arg in args: - if arg.endswith('!') and '=' not in arg and ':' not in arg: - dels.append(arg[:-1]) # Strip trailing !. - elif '=' in arg and ':' not in arg.split('=', 1)[0]: - mods.append(arg) - else: - query.append(arg) + (query, mods, dels) = modify_parse_args(args) if not mods and not dels: raise ui.UserError('no modifications specified') diff --git a/test/test_ui.py b/test/test_ui.py index 34d7104b4..d25c94e1f 100644 --- a/test/test_ui.py +++ b/test/test_ui.py @@ -238,6 +238,30 @@ class ModifyTest(_common.TestCase): mediafile = MediaFile(item.path) self.assertIsNone(mediafile.initial_key) + def test_arg_parsing_colon_query(self): + (query, mods, dels) = commands.modify_parse_args(["title:oldTitle", + "title=newTitle"]) + self.assertEqual(query, ["title:oldTitle"]) + self.assertEqual(mods, ["title=newTitle"]) + + def test_arg_parsing_delete(self): + (query, mods, dels) = commands.modify_parse_args(["title:oldTitle", + "title!"]) + self.assertEqual(query, ["title:oldTitle"]) + self.assertEqual(dels, ["title"]) + + def test_arg_parsing_query_with_exclaimation(self): + (query, mods, dels) = commands.modify_parse_args(["title:oldTitle!", + "title=newTitle!"]) + self.assertEqual(query, ["title:oldTitle!"]) + self.assertEqual(mods, ["title=newTitle!"]) + + def test_arg_parsing_equals_in_value(self): + (query, mods, dels) = commands.modify_parse_args(["title:foo=bar", + "title=newTitle"]) + self.assertEqual(query, ["title:foo=bar"]) + self.assertEqual(mods, ["title=newTitle"]) + class MoveTest(_common.TestCase): def setUp(self):