diff --git a/beets/dbcore/db.py b/beets/dbcore/db.py index 878004976..54fb10f47 100644 --- a/beets/dbcore/db.py +++ b/beets/dbcore/db.py @@ -346,17 +346,13 @@ class Model(object): # Formatting and templating. - def _get_formatted(self, key, for_path=False): - """Get a field value formatted as a string (`unicode` object) - for display to the user. If `for_path` is true, then the value - will be sanitized for inclusion in a pathname (i.e., path - separators will be removed from the value). + @classmethod + def _format(cls, key, value, for_path=False): + """Format a value as the given field for this model. """ - value = self.get(key) - # Format the value as a string according to its type, if any. - if key in self._fields: - value = self._fields[key].format(value) + if key in cls._fields: + value = cls._fields[key].format(value) # Formatting must result in a string. To deal with # Python2isms, implicitly convert ASCII strings. assert isinstance(value, basestring), \ @@ -382,6 +378,14 @@ class Model(object): return value + def _get_formatted(self, key, for_path=False): + """Get a field value formatted as a string (`unicode` object) + for display to the user. If `for_path` is true, then the value + will be sanitized for inclusion in a pathname (i.e., path + separators will be removed from the value). + """ + return self._format(key, self.get(key), for_path) + def _formatted_mapping(self, for_path=False): """Get a mapping containing all values on this object formatted as human-readable strings. @@ -410,6 +414,23 @@ class Model(object): return template.substitute(mapping, funcs) + # Parsing. + + @classmethod + def _parse(cls, key, string): + """Parse a string as a value for the given key. + """ + if not isinstance(string, basestring): + raise TypeError("_parse() argument must be a string") + + typ = cls._fields.get(key) + if typ: + return typ.parse(string) + else: + # Fall back to unparsed string. + return string + + # Database controller and supporting interfaces. diff --git a/beets/library.py b/beets/library.py index d5b07c3e7..2295861fe 100644 --- a/beets/library.py +++ b/beets/library.py @@ -86,11 +86,17 @@ class DateType(types.Type): time.localtime(value or 0)) def parse(self, string): - # FIXME Real date parsing. try: - return float(string) + # Try a formatted date string. + return time.mktime( + time.strptime(string, beets.config['time_format'].get(unicode)) + ) except ValueError: - return 0.0 + # Fall back to a plain timestamp number. + try: + return float(string) + except ValueError: + return 0.0 class PathType(types.Type): diff --git a/beets/ui/commands.py b/beets/ui/commands.py index b3366d944..f57d2edb0 100644 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -1113,10 +1113,7 @@ def modify_items(lib, mods, query, write, move, album, confirm): fsets = {} for mod in mods: key, value = mod.split('=', 1) - typ = model_cls._fields.get(key) - if typ: - value = typ.parse(value) - fsets[key] = value + fsets[key] = model_cls._parse(key, value) # Get the items to modify. items, albums = _do_query(lib, query, album, False) diff --git a/test/test_dbcore.py b/test/test_dbcore.py index 8dd554e06..85fb94f87 100644 --- a/test/test_dbcore.py +++ b/test/test_dbcore.py @@ -246,6 +246,16 @@ class FormatTest(_common.TestCase): self.assertEqual(value, u'') +class ParseTest(_common.TestCase): + def test_parse_fixed_field(self): + value = TestModel1._parse('field_one', u'2') + self.assertEqual(value, 2) + + def test_parse_untyped_field(self): + value = TestModel1._parse('field_nine', u'2') + self.assertEqual(value, u'2') + + def suite(): return unittest.TestLoader().loadTestsFromName(__name__)