truncation incorporates extension length (GC-461)

This commit is contained in:
Adrian Sampson 2012-11-27 16:54:50 -08:00
parent 953291f736
commit eef87c2189
4 changed files with 30 additions and 5 deletions

View file

@ -1152,7 +1152,6 @@ class Library(BaseLibrary):
# Encode for the filesystem.
if not fragment:
subpath = bytestring_path(subpath)
subpath = util.truncate_path(subpath, pathmod)
# Preserve extension.
_, extension = pathmod.splitext(item.path)
@ -1161,6 +1160,9 @@ class Library(BaseLibrary):
extension = extension.decode('utf8', 'ignore')
subpath += extension.lower()
# Truncate too-long components.
subpath = util.truncate_path(subpath, pathmod)
if fragment:
return subpath
else:

View file

@ -457,13 +457,22 @@ def sanitize_path(path, pathmod=None, replacements=None):
comps[i] = comp
return pathmod.join(*comps)
def truncate_path(path, pathmod=None):
def truncate_path(path, pathmod=None, length=MAX_FILENAME_LENGTH):
"""Given a bytestring path or a Unicode path fragment, truncate the
components to a legal length.
components to a legal length. In the last component, the extension
is preserved.
"""
pathmod = pathmod or os.path
comps = [c[:MAX_FILENAME_LENGTH] for c in components(path, pathmod)]
return pathmod.join(*comps)
comps = components(path, pathmod)
out = [c[:length] for c in comps]
base, ext = pathmod.splitext(comps[-1])
if ext:
# Last component has an extension.
base = base[:length - len(ext)]
out[-1] = base + ext
return pathmod.join(*out)
def sanitize_for_path(value, pathmod, key=None):
"""Sanitize the value for inclusion in a path: replace separators

View file

@ -60,6 +60,7 @@ Changelog
question is now logged (thanks to Mike Kazantsev).
* Truncate long filenames based on their *bytes* rather than their Unicode
*characters*, fixing situations where encoded names could be too long.
* Filename truncation now incorporates the length of the extension.
* Fix an assertion failure when the MusicBrainz main database and search server
disagree.
* Fix a bug that caused the :doc:`/plugins/lastgenre` and other plugins not to

View file

@ -918,6 +918,19 @@ class PathStringTest(unittest.TestCase):
alb = self.lib.get_album(alb.id)
self.assert_(isinstance(alb.artpath, str))
class PathTruncationTest(unittest.TestCase):
def test_truncate_bytestring(self):
p = util.truncate_path('abcde/fgh', posixpath, 4)
self.assertEqual(p, 'abcd/fgh')
def test_truncate_unicode(self):
p = util.truncate_path(u'abcde/fgh', posixpath, 4)
self.assertEqual(p, u'abcd/fgh')
def test_truncate_preserves_extension(self):
p = util.truncate_path(u'abcde/fgh.ext', posixpath, 5)
self.assertEqual(p, u'abcde/f.ext')
class MtimeTest(unittest.TestCase):
def setUp(self):
self.ipath = os.path.join(_common.RSRC, 'testfile.mp3')