mirror of
https://github.com/beetbox/beets.git
synced 2025-12-29 20:12:33 +01:00
safely interpret integers in packed values
This commit is contained in:
parent
633b97b302
commit
c3988f7300
3 changed files with 74 additions and 31 deletions
1
NEWS
1
NEWS
|
|
@ -45,6 +45,7 @@
|
|||
Windows users can now just type "beet" at the prompt to run beets.
|
||||
* Fixed an occasional bug where Mutagen would complain that a tag was
|
||||
already present.
|
||||
* Fixed a bug with reading invalid integers from ID3 tags.
|
||||
|
||||
1.0b3
|
||||
-----
|
||||
|
|
|
|||
|
|
@ -62,6 +62,50 @@ TYPES = {
|
|||
}
|
||||
|
||||
|
||||
# Utility.
|
||||
|
||||
def _safe_cast(out_type, val):
|
||||
"""Tries to covert val to out_type but will never raise an
|
||||
exception. If the value can't be converted, then a sensible
|
||||
default value is returned. out_type should be bool, int, or
|
||||
unicode; otherwise, the value is just passed through.
|
||||
"""
|
||||
if out_type == int:
|
||||
if val is None:
|
||||
return 0
|
||||
elif isinstance(val, int) or isinstance(val, float):
|
||||
# Just a number.
|
||||
return int(val)
|
||||
else:
|
||||
# Process any other type as a string.
|
||||
if not isinstance(val, basestring):
|
||||
val = unicode(val)
|
||||
# Get a number from the front of the string.
|
||||
val = re.match('[0-9]*', val.strip()).group(0)
|
||||
if not val:
|
||||
return 0
|
||||
else:
|
||||
return int(val)
|
||||
|
||||
elif out_type == bool:
|
||||
if val is None:
|
||||
return False
|
||||
else:
|
||||
try:
|
||||
# Should work for strings, bools, ints:
|
||||
return bool(int(val))
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
elif out_type == unicode:
|
||||
if val is None:
|
||||
return u''
|
||||
else:
|
||||
return unicode(val)
|
||||
|
||||
else:
|
||||
return val
|
||||
|
||||
# Flags for encoding field behavior.
|
||||
|
||||
class Enumeration(object):
|
||||
|
|
@ -146,9 +190,9 @@ class Packed(object):
|
|||
out = None
|
||||
|
||||
if out is None or out == self.none_val or out == '':
|
||||
return self.out_type(self.none_val)
|
||||
return _safe_cast(self.out_type, self.none_val)
|
||||
else:
|
||||
return self.out_type(out)
|
||||
return _safe_cast(self.out_type, out)
|
||||
|
||||
def __setitem__(self, index, value):
|
||||
|
||||
|
|
@ -336,35 +380,7 @@ class MediaField(object):
|
|||
if style.packing:
|
||||
out = Packed(out, style.packing)[style.pack_pos]
|
||||
|
||||
# return the appropriate type
|
||||
if self.out_type == int:
|
||||
if out is None:
|
||||
return 0
|
||||
elif isinstance(out, int) or isinstance(out, float):
|
||||
# Just a number.
|
||||
return int(out)
|
||||
else:
|
||||
# Process any other type as a string.
|
||||
if not isinstance(out, basestring):
|
||||
out = unicode(out)
|
||||
# Get a number from the front of the string.
|
||||
out = re.match('[0-9]*', out.strip()).group(0)
|
||||
if not out:
|
||||
return 0
|
||||
else:
|
||||
return int(out)
|
||||
elif self.out_type == bool:
|
||||
if out is None:
|
||||
return False
|
||||
else:
|
||||
return bool(int(out)) # should work for strings, bools, ints
|
||||
elif self.out_type == unicode:
|
||||
if out is None:
|
||||
return u''
|
||||
else:
|
||||
return unicode(out)
|
||||
else:
|
||||
return out
|
||||
return _safe_cast(self.out_type, out)
|
||||
|
||||
def __set__(self, obj, val):
|
||||
"""Set the value of this metadata field.
|
||||
|
|
|
|||
|
|
@ -61,6 +61,32 @@ class EdgeTest(unittest.TestCase):
|
|||
self.assertEqual(f.disc, 4)
|
||||
self.assertEqual(f.disctotal, 5)
|
||||
|
||||
_sc = beets.mediafile._safe_cast
|
||||
class InvalidValueToleranceTest(unittest.TestCase):
|
||||
def test_packed_integer_with_extra_chars(self):
|
||||
pack = beets.mediafile.Packed("06a", beets.mediafile.packing.SLASHED)
|
||||
self.assertEqual(pack[0], 6)
|
||||
|
||||
def test_packed_integer_invalid(self):
|
||||
pack = beets.mediafile.Packed("blah", beets.mediafile.packing.SLASHED)
|
||||
self.assertEqual(pack[0], 0)
|
||||
|
||||
def test_packed_index_out_of_range(self):
|
||||
pack = beets.mediafile.Packed("06", beets.mediafile.packing.SLASHED)
|
||||
self.assertEqual(pack[1], 0)
|
||||
|
||||
def test_safe_cast_string_to_int(self):
|
||||
self.assertEqual(_sc(int, 'something'), 0)
|
||||
|
||||
def test_safe_cast_int_string_to_int(self):
|
||||
self.assertEqual(_sc(int, '20'), 20)
|
||||
|
||||
def test_safe_cast_string_to_bool(self):
|
||||
self.assertEqual(_sc(bool, 'whatever'), False)
|
||||
|
||||
def test_safe_cast_intstring_to_bool(self):
|
||||
self.assertEqual(_sc(bool, '5'), True)
|
||||
|
||||
class SafetyTest(unittest.TestCase):
|
||||
def _exccheck(self, fn, exc):
|
||||
fn = os.path.join('rsrc', fn)
|
||||
|
|
|
|||
Loading…
Reference in a new issue