Форум сайта python.su
Доброго всем утра, вечера, дня и ночи. Начал изучать Python и сразу же столкнулся с задачей копирования директории с условием, чтоб пользователю предлагался выбор - заменить существующий файл или нет. Покопавшись в модулях os, os.path, shutil я такой функции не нашел, сразу скажу в shutil есть функция copytree(), но как я понял она генерирует исключение, а исключения область в которой я еще не твердо стою на ногах, а скорее вообще не стою. Подумав решил написать свою, взяв за основу код той же copytree(), заодно и в Python поупражняться. Собственно возникло пару вопросов:
1) Может я что-то пропустил в os и shutil, с английским у меня не очень, поэтому большая просьба опытным людям - ткните меня носом, если я что то пропустил в упомянутых модулях.
2) Можно ли добиться необходимого результата обработкой исключений, не создавая новую функцию копирования
3)Решил выложить написанную мной функцию здесь, хотелось бы услышать конструктивную критику, замечания, советы по поводу ее реализации.
Использую Python 3.0.1 (в PortablePython)
# -*- coding: cp866 -*-
import sys, subprocess, os, tempfile, code, time, shutil, stat
def dircpy(src, dst, ignore=None, symlinks=False, ForceReplace=False, logfile=None):
names = os.listdir(src)
AskStr = '\nФайл уже существует. Заменить?'
ChoiceNoReplace = ' н(n)= Не заменять (Don\'t replace)'
ChoiceReplace = ' д(y)= Да, заменить(Yes, replace)'
ChoiceAllReplace= ' в(a)= Заменить все файлы в данной директории(Replace all files in this dir)'
ChoiceForceAllReplace = ' ф(f)= Вообще все, и не спрашивайте меня больше (Force replace All, don\'t ask me never)'
ReplaceAll=False
ForceAll=ForceReplace
if ForceAll:
ReplaceAll=True
if ignore is not None:
ignored_names = ignore(src, names)
else:
ignored_names = set()
if not os.path.exists(dst):
os.makedirs(dst)
errors = []
for name in names:
if name in ignored_names:
continue
srcname = os.path.join(src, name)
dstname = os.path.join(dst, name)
try:
if symlinks and os.path.islink(srcname):
linkto = os.readlink(srcname)
os.symlink(linkto, dstname)
elif os.path.isdir(srcname):
ForceAll = dircpy(srcname, dstname, ignore, symlinks, ForceAll, logfile)
if ForceAll:
ReplaceAll=True
else:
print(dstname)
if logfile:
logfile.write('\n')
logfile.writelines(dstname)
if os.path.exists(dstname):
if ReplaceAll == True:
os.remove(dstname)
shutil.copy2(srcname, dstname)
else:
correctChoice = False
while not correctChoice:
print (AskStr)
print (ChoiceNoReplace)
print (ChoiceReplace)
print (ChoiceAllReplace)
print (ChoiceForceAllReplace)
if logfile:
logfile.writelines(AskStr)
Choice = code.InteractiveConsole.raw_input(': ')
if ('y' in Choice) or ('д' in Choice):
correctChoice = True
print ('Вы выбрали: ',ChoiceReplace)
if logfile:
logfile.writelines('\nВы выбрали: \n')
logfile.writelines(ChoiceReplace)
logfile.writelines('\n')
os.remove(dstname)
shutil.copy2(srcname, dstname)
elif ('в' in Choice) or ('a' in Choice):
correctChoice = True
print ('Вы выбрали: ',ChoiceAllReplace)
if logfile:
logfile.writelines('\nВы выбрали: \n')
logfile.writelines(ChoiceAllReplace)
logfile.writelines('\n')
os.remove(dstname)
shutil.copy2(srcname, dstname)
ReplaceAll=True
elif ('ф' in Choice) or ('f' in Choice):
correctChoice = True
print ('Вы выбрали: ',ChoiceForceAllReplace)
if logfile:
logfile.writelines('\nВы выбрали: \n')
logfile.writelines(ChoiceForceAllReplace)
logfile.writelines('\n')
os.remove(dstname)
shutil.copy2(srcname, dstname)
ReplaceAll=True
ForceAll=True
elif ('н' in Choice) or ('n' in Choice):
correctChoice = True
print ('Вы выбрали: ',ChoiceNoReplace)
if logfile:
logfile.writelines('\nВы выбрали: \n')
logfile.writelines(ChoiceNoReplace)
logfile.writelines('\n')
else:
print('Некоректный выбор\n')
if logfile:
logfile.writelines('Некоректный выбор-> ')
logfile.writelines(Choice)
logfile.writelines('\n')
else:
shutil.copy2(srcname, dstname)
# XXX What about devices, sockets etc.?
except (IOError, os.error) as why:
errors.append((srcname, dstname, str(why)))
# catch the Error from the recursive copytree so that we can
# continue with other files
except shutil.Error as err:
errors.extend(err.args[0])
try:
shutil.copystat(src, dst)
except OSError as why:
if WindowsError is not None and isinstance(why, WindowsError):
# Copying file access times may fail on Windows
pass
else:
errors.extend((src, dst, str(why)))
if errors:
raise shutil.Error(errors)
return ForceAll
Офлайн
1. Нет, стандартные высокоуровневые функции такое не поддерживают - очень неопределен “ответ пользователя”.
2. Вместо рекурсии посмотрите на os.walk
3. Вынесите “функцию-вопрошалку” за пределы вашего копировщика. Передавайте ей исходный путь и назначение. В ответ получайте константы типа “да, делать”, “только в текущей директории”, “все запрещаю и не беспокойте меня” и т.д.
4. Исключения - они несколько не для того. А именно исключения бросаются в случае ошибки, который внешний код не может игнорировать.
Например - open(filename)
если open будет возвращать код ошибки - его обязательно программист обязательно проигнорирует как правило. Особенно если этот open будет вызываться из функции, которая была вызвана уровнем выше и т.д. Да и неудобно так писать. Зато если выбросите OSError типа “файл не существует” - о вас уже не забудут.
По поводу исключений и их правильного применения можно писать долго - так что спрашивайте.
Офлайн
Андрей, спасибо за ответ.
Сделал функцию-вопрошалку отдельно. Но вот по варианту с os.wolk столкнулся с трудностью. При рекурсивном обходе директорий вот таким кодом:
for root, dirs, files in os.walk(src):
for name in files:
fullname = os.path.join(root, name)
print (fullname)
# src - копируемая директория.
# dst - директория, куда копируются файлы
Офлайн
romanc
Можно ли это сделать каким-либо простым способом (встроенная функция)
destination = os.path.join(dst, os.path.relpath(fullname, src))
Офлайн
pyuser, благодарю, я пропустил функцию os.path.relpath при просмотре справки по модулю.
Постарался воспользоваться советами Андрея Светлова в меру своих знаний и понимания,
вот результат:
# Функция взаимодействия с пользователем.
# -*- coding: cp1251 -*-
import sys, subprocess, os, tempfile, code, time, shutil, stat, re
# Функция взаимодействия с пользователем.
def AskForReplace (dst, logfile=None):
AskStr = 'Файл уже существует. Заменить?'
ChoiceNoReplace = ' 1 = Не заменять (Don\'t replace)'
ChoiceCancelAll = ' 2 = Не заменять файлы в данной директории (Don\'t replace All in this dir)'
ChoiceForceCancelAll = ' 3 = Не заменять файлы, и не спрашивать больше (Don\'t replace All, don\'t ask me never)'
ChoiceReplace = ' 4 = Да, заменить(Yes, replace)'
ChoiceAllReplace= ' 5 = Заменить все файлы в данной директории(Replace all files in this dir)'
ChoiceForceAllReplace = ' 6 = Заменить все, и не спрашивайте меня больше (Force replace All, don\'t ask me never)'
text = ''
correctChoice = False
while not correctChoice:
print (AskStr)
print (ChoiceNoReplace)
print(ChoiceCancelAll)
print(ChoiceForceCancelAll)
print (ChoiceReplace)
print (ChoiceAllReplace)
print (ChoiceForceAllReplace)
if logfile:
logfile.writelines(AskStr)
Choice = code.InteractiveConsole.raw_input(': ')
if ('1' == Choice):
correctChoice = True
text = ChoiceNoReplace
elif ('2' == Choice):
correctChoice = True
text = ChoiceCancelAll
elif ('3' == Choice):
correctChoice = True
text = ChoiceForceCancelAll
elif ('4' == Choice):
correctChoice = True
text = ChoiceReplace
elif ('5' == Choice):
correctChoice = True
text = ChoiceAllReplace
elif ('6' == Choice):
correctChoice = True
text = ChoiceForceAllReplace
else:
print('Некоректный выбор\n')
if logfile:
logfile.writelines('Некоректный выбор-> ')
logfile.writelines(Choice)
logfile.writelines('\n')
print ('Вы выбрали: ',text)
print('')
if logfile:
logfile.writelines('\nВы выбрали: \n')
logfile.writelines(text)
logfile.writelines('\n\n')
return Choice
# Функция копирования.
# src - Папка, содержимое которой копируется
# dst - Папка, куда копируем
# symlink - Копировать ли файлы на которые указывает линк (ярлык?)
# logfile - лог файл
def dircpy(src, dst, ignore=None, symlinks=False, logfile=None):
names = os.listdir(src)
ReplaceAll=False
CancelAll = False
ForceAll=False
ForceCancelAll = False
if ignore is not None:
ignored_names = ignore(src, names)
else:
ignored_names = set()
errors = []
# Обход всех файлов с помощью функции os.walk
for root, dirs, files in os.walk(src):
# Проверяем флаг ForceAll замены всех файлов без запроса, если ForceAll == True, то
# заменяем все файлы во всех папках без запроса пользователю,
# если ForceAll != True, то задаем вопрос пользователю при каждом совпадении имен файлов
if ForceAll == True:
ReplaceAll=True
else:
ReplaceAll=False
# Проверяем флаг ForceCancelAll запрета замены файлов, если ForceCancelAll == True, то
# отменяем замену файлов во всех папках без запроса пользователю,
# если ForceCancelAll != True, то задаем вопрос пользователю при каждом совпадении имен файлов
if ForceCancelAll == True:
CancelAll = True
else:
CancelAll = False
# Обходим все файлы в поддиректориях
for name in files:
if name in ignored_names:
continue
srcname = os.path.join(root, name) # путь, имя копируемого файла
dstpath=os.path.join(dst, os.path.relpath(root, src)) # путь - место назначения, куда копируем
# проверяем существует ли папка назначения, если нет, то создаем ее
if not os.path.exists(dstpath):
os.makedirs(dstpath)
# Получаем полное имя файла, куда будем копировать исходный файл
dstname = os.path.join(dstpath, name)
try:
if symlinks and os.path.islink(srcname):
linkto = os.readlink(srcname)
os.symlink(linkto, dstname)
else:
print(dstname)
if logfile:
logfile.writelines(dstname)
logfile.write('\n')
# Проверяем существует ли файл
if os.path.exists(dstname):
# Проверяем установлен ли флаг ReplaceAll замены файлов только в данной директории, если да, то
# заменяем все файлы без запроса пользователю
if ReplaceAll == True:
os.remove(dstname)
shutil.copy2(srcname, dstname)
elif CancelAll == True: # иначе проверяем установлен ли флаг CancelAll запрета замены файлов
pass # только для данной директории, если да, то пропускаем
# копирование уже существующих файлов. Замена не происходит
else: # Иначе (если не установлены флаг замены или запрета замены, взаимоддействуем
# с пользователем)
Choice = AskForReplace(dstname, logfile) # Вызов функции интерактивного запроса
# Действуем согласно ответу пользователя
if '1' in Choice:
pass
elif '2' in Choice:
CancelAll = True
elif '3' in Choice:
CancelAll = True
ForceCancelAll = True
elif '4' in Choice:
os.remove(dstname)
shutil.copy2(srcname, dstname)
elif '5' in Choice:
os.remove(dstname)
shutil.copy2(srcname, dstname)
ReplaceAll=True
elif '6' in Choice:
os.remove(dstname)
shutil.copy2(srcname, dstname)
ReplaceAll=True
ForceAll=True
else:
shutil.copy2(srcname, dstname)
#XXX What about devices, sockets etc.?
except (IOError, os.error) as why:
errors.append((srcname, dstname, str(why)))
# catch the Error from the recursive copytree so that we can
# continue with other files
except shutil.Error as err:
errors.extend(err.args[0])
try:
shutil.copystat(src, dst)
except OSError as why:
if WindowsError is not None and isinstance(why, WindowsError):
# Copying file access times may fail on Windows
pass
else:
errors.extend((src, dst, str(why)))
if errors:
raise shutil.Error(errors)
Офлайн
romancэтот код целесообразнее делать перед цикломdstpath=os.path.join(dst, os.path.relpath(root, src)) # путь - место назначения, куда копируем
# проверяем существует ли папка назначения, если нет, то создаем ее
if not os.path.exists(dstpath): os.makedirs(dstpath)
for name in files:
if нечто == True:
if нечто:
Отредактировано (Окт. 20, 2009 03:07:18)
Офлайн
Да, действительно, так будет лучше:
...
dstpath=os.path.join(dst, os.path.relpath(root, src)) # путь - место назначения, куда копируем
if not os.path.exists(dstpath):
os.makedirs(dstpath)
# Обходим все файлы в поддиректориях
for name in files:
if name in ignored_names:
continue
...
if нечто == True:
Офлайн
Хм… не пойму чем вам рекурсия не нравится. Она хоть и ест память, но если вложенность не превышает приделы разумного, то в ней нет ничего страшного. Я для копирования дерева каталогов когда то написал такую функцию:
def cp(N, K):
if os.path.isdir(N):
if not os.path.exists(K): os.mkdir(K)
for x in os.listdir(N):
if os.path.isdir(os.path.join(N,x)):
if not os.path.exists(os.path.join(K,x)): os.mkdir(os.path.join(K,x))
cp(os.path.join(N,x),os.path.join(K,x))
else: cp(os.path.join(N,x),os.path.join(K,x))
else:
file = open(N,"rb"); data = file.read(); file.close()
file = open(K,"wb"); file.write(data); file.close()
Отредактировано (Окт. 20, 2009 13:40:46)
Офлайн
дело не в памяти. Генератор более уместен.
Офлайн
Почему?
Офлайн