# Created By: Virgil Dupras # Created On: 2008-01-08 # Copyright 2013 Hardcoded Software (http://www.hardcoded.net) # This software is licensed under the "BSD" License as described in the "LICENSE" file, # which should be included with this package. The terms are also available at # http://www.hardcoded.net/licenses/bsd_license import re import os import shutil from .path import Path, pathify #This matches [123], but not [12] (3 digits being the minimum). #It also matches [1234] [12345] etc.. #And only at the start of the string re_conflict = re.compile(r'^\[\d{3}\d*\] ') def get_conflicted_name(other_names, name): """Returns name with a [000] number in front of it. The number between brackets depends on how many conlicted filenames there already are in other_names. """ name = get_unconflicted_name(name) if name not in other_names: return name i = 0 while True: newname = '[%03d] %s' % (i, name) if newname not in other_names: return newname i += 1 def get_unconflicted_name(name): return re_conflict.sub('',name,1) def is_conflicted(name): return re_conflict.match(name) is not None @pathify def _smart_move_or_copy(operation, source_path: Path, dest_path: Path): ''' Use move() or copy() to move and copy file with the conflict management, but without the slowness of the fs system. ''' if dest_path.isdir() and not source_path.isdir(): dest_path = dest_path[source_path.name] if dest_path.exists(): filename = dest_path.name dest_dir_path = dest_path.parent() newname = get_conflicted_name(os.listdir(str(dest_dir_path)), filename) dest_path = dest_dir_path[newname] operation(str(source_path), str(dest_path)) def smart_move(source_path, dest_path): _smart_move_or_copy(shutil.move, source_path, dest_path) def smart_copy(source_path, dest_path): try: _smart_move_or_copy(shutil.copy, source_path, dest_path) except IOError as e: if e.errno in {21, 13}: # it's a directory, code is 21 on OS X / Linux and 13 on Windows _smart_move_or_copy(shutil.copytree, source_path, dest_path) else: raise