From 39809a8a440583ad06c85557bc97137da2204c61 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Tue, 7 Jul 2015 18:17:12 -0700 Subject: [PATCH] Expand a little on the docs for #1361 Fix #496, at long last. --- beets/library.py | 8 +++++--- beets/util/__init__.py | 41 ++++++++++++++++++++++++++++------------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/beets/library.py b/beets/library.py index 10e54b2a1..9d11b166c 100644 --- a/beets/library.py +++ b/beets/library.py @@ -799,10 +799,12 @@ class Item(LibModel): subpath, self._db.replacements, maxlen, os.path.splitext(self.path)[1], fragment ) - - # Print an error message if legalize fell back to default replacements if fellback: - log.warning(u'fell back to default replacements when naming file') + # Print an error message if legalization fell back to + # default replacements because of the maximum length. + log.warning('Fell back to default replacements when naming ' + 'file {}. Configure replacements to avoid lengthening ' + 'the filename.', subpath) if fragment: return subpath diff --git a/beets/util/__init__.py b/beets/util/__init__.py index 42971fdf6..30a1fcba9 100644 --- a/beets/util/__init__.py +++ b/beets/util/__init__.py @@ -546,9 +546,11 @@ def truncate_path(path, length=MAX_FILENAME_LENGTH): def _legalize_stage(path, replacements, length, extension, fragment): - """ Performs sanitization of the path, then encodes for the filesystem, - appends the extension and truncates. Returns the path (unicode if fragment - is set, otherwise bytes), and whether truncation was required. + """Perform a single round of path legalization steps + (sanitation/replacement, encoding from Unicode to bytes, + extension-appending, and truncation). Return the path (Unicode if + `fragment` is set, `bytes` otherwise) and whether truncation was + required. """ # Perform an initial sanitization including user replacements. path = sanitize_path(path, replacements) @@ -568,11 +570,25 @@ def _legalize_stage(path, replacements, length, extension, fragment): def legalize_path(path, replacements, length, extension, fragment): - """ Perform up to three calls to _legalize_stage, to generate a stable - output path, taking user replacements into consideration if possible. The - limited number of iterations avoids the possibility of an infinite loop of - sanitization and truncation operations, which could be caused by some user - replacements. + """Given a path-like Unicode string, produce a legal path. Return + the path and a flag indicating whether some replacements had to be + ignored (see below). + + The legalization process (see `_legalize_stage`) consists of + applying the sanitation rules in `replacements`, encoding the string + to bytes (unless `fragment` is set), truncating components to + `length`, appending the `extension`. + + This function performs up to three calls to `_legalize_stage` in + case truncation conflicts with replacements (as can happen when + truncation creates whitespace at the end of the string, for + example). The limited number of iterations iterations avoids the + possibility of an infinite loop of sanitation and truncation + operations, which could be caused by replacement rules that make the + string longer. The flag returned from this function indicates that + the path has to be truncated twice (indicating that replacements + made the string longer again after it was truncated); the + application should probably log some sort of warning. """ if fragment: @@ -584,22 +600,21 @@ def legalize_path(path, replacements, length, extension, fragment): ) # Convert back to Unicode with extension removed. - first_stage_path = os.path.splitext(displayable_path(first_stage_path))[0] + first_stage_path, _ = os.path.splitext(displayable_path(first_stage_path)) # Re-sanitize following truncation (including user replacements). second_stage_path, retruncated = _legalize_stage( first_stage_path, replacements, length, extension, fragment ) - # If the path was once again truncated, discard user replacements. + # If the path was once again truncated, discard user replacements + # and run through one last legalization stage. if retruncated: second_stage_path, _ = _legalize_stage( first_stage_path, None, length, extension, fragment ) - return second_stage_path, True - else: - return second_stage_path, False + return second_stage_path, retruncated def str2bool(value):