Почему не создаётся архив, если на входе более одного пути?

Я написал код который делает резервную копию выбранной папки. Этот код принимает путь к каталогу, который нужно заархивировать в выбранном месте.

Если программа принимает только один путь, то она работает успешно, создаётся архив с папкой, в папке все файлы, но если программа будет принимать более 1 пути, то создаётся пустой архив без папок и файлов.

Как решить эту проблему?

import os
import time
import zipfile

# 1. Файлы и каталоги, которые необходимо скопировать, собираются в список.
source = ['C:\\Code']
# Заметьте, что для имён, содержащих пробелы, необходимо использовать
# двойные кавычки внутри строки.

# 2. Резервные копии должны храниться в основном каталоге резерва.
target_dir = 'C:\\Sasha'  # Подставьте ваш путь.

# 3. Файлы помещаются в zip-архив.
# 4. Текущая дата служит именем подкаталога в основном каталоге
today = target_dir + os.sep + time.strftime('%Y%m%d')
# Текущее время служит именем zip-архива
now = time.strftime('%H%M%S')

# Запрашиваем комментарий пользователя для имени файла
comment = input('Введите комментарий --> ')

if len(comment) == 0:  # проверяем, введён ли комментарий
    target = today + os.sep + now + '.zip'
else:
    target = today + os.sep + now + '_' + \
        comment.replace(' ', '_') + '.zip'

# Создаём каталог, если его ещё нет
if not os.path.exists(today):
    os.mkdir(today)  # создание каталога
print('Каталог успешно создан', today)

# 5. Используем команду "zip" для помещения файлов в zip-архив
zip_file = zipfile.ZipFile(target, 'w')

for root, dirs, files in os.walk(''.join(source)):
    for file in files:
        zip_file.write(os.path.join(root, file))

# Закрываем архив
zip_file.close()


Ответы (2 шт):

Автор решения: Andy Pavlov

Не нужно делать join(), Вы в одну строку все соедините, которую потом os.walk не поймет. Для этого можно воспользоваться решением через стандартную библиотеку itertools: itertools.chain.from_iterable(os.walk(s) for s in source).

Еще такой момент. Создавать zip-архив лучше через with. Тогда не придется отдельно его закрывать. Ну и код будет чище.

Ну и последнее) Собирать путь удобнее через os.path.join().

import itertools
import os
import time
import zipfile

# 1. Файлы и каталоги, которые необходимо скопировать, собираются в список.
source = ['C:\\Code']
# Заметьте, что для имён, содержащих пробелы, необходимо использовать
# двойные кавычки внутри строки.

# 2. Резервные копии должны храниться в основном каталоге резерва.
target_dir = 'C:\\Sasha'  # Подставьте ваш путь.

# 3. Файлы помещаются в zip-архив.
# 4. Текущая дата служит именем подкаталога в основном каталоге
today = os.path.join(target_dir, time.strftime('%Y%m%d'))
# Текущее время служит именем zip-архива
now = time.strftime('%H%M%S')

# Запрашиваем комментарий пользователя для имени файла
comment = input('Введите комментарий --> ')

if len(comment) == 0:  # проверяем, введён ли комментарий
    target = today + os.sep + now + '.zip'
else:
    target = today + os.sep + now + '_' + \
        comment.replace(' ', '_') + '.zip'

# Создаём каталог, если его ещё нет
if not os.path.exists(today):
    os.mkdir(today)  # создание каталога
print('Каталог успешно создан', today)

# 5. Используем команду "zip" для помещения файлов в zip-архив
with zipfile.ZipFile(target, 'w') as zip_file:
    for root, dirs, files in itertools.chain.from_iterable(os.walk(s) for s in source):
        for file in files:
            zip_file.write(os.path.join(root, file))
→ Ссылка
Автор решения: MaxU

Вам уже объяснили в чем причина ошибки в комментарии под вопросом и в соседнем ответе.

Я бы посоветовал оформлять операции в функции или в классы. И постарайтесь выносить запрос пользователя на ввод за пределы функции. Кроме того, в современных версиях Python при работе с путями и именами файлов предпочтение отдается встроенному модулю - pathlib.

Вот пример функции для создания резервных копий указанных директорий:

import time
from pathlib import Path
from typing import Union, List

path_type = Union[Path, str]
target_dir = Path("C:\\Sasha")

def bkp_dirs(
        source_dirs: Union[List[path_type], path_type],
        target_filename: path_type
) -> None:
    if not isinstance(source_dirs, list):
        source_dirs = [source_dirs]
    target_filename = Path(target_filename)
    target_filename.parent.mkdir(parents=True, exist_ok=True)
    zip_file = zipfile.ZipFile(target_filename, 'w')
    for d in source_dirs:
        for f in Path(d).glob("**/*"):
            zip_file.write(f)
    zip_file.close()

пример использования:

comment = input('Введите комментарий --> ')
target = target_dir \
         / time.strftime('%Y%m%d') \
         / f"{time.strftime('%H%M%S')}{comment.replace(' ', '_')}.zip"

source_dirs = ['C:\\Code']

bkp_dirs(source_dirs, target)
→ Ссылка