Stop shutil.make_archive adding archive to itself
Asked Answered
L

3

6

I have App dir inside Release dir

$ cd Release
$ tree
.
`-- App
    |-- App.exe
    ..........

and I am trying to create App-1.0.zip in the Release dir containg App with all its content. That is after unpacking App-1.0.zip I would get this App dir.

I tried shutil.make_archive but when I do this

import shutil

shutil.make_archive('App-1.0', 'zip', '.')

from Release dir, I get 48 byte App-1.0.zip inside App-1.0.zip besides the App dir. That is it adds this unfinished archive to itself.

Is there any way to avoid that except creating the archive in temp dir and moving?

I tried to set base_dir and use App as root_dir

shutil.make_archive('App-1.0', 'zip', 'App', 'App')

but I get error that App is not found when I set base_dir.

Traceback (most recent call last):
  File ".......archive.py", line 4, in <module>
    shutil.make_archive('App-1.0', 'zip', 'App', 'App')
  File "C:\Users\Alex\.virtualenvs\....-nAKWzegL\lib\shutil.py", line 800, in make_archive
    filename = func(base_name, base_dir, **kwargs)
  File "C:\Users\Alex\.virtualenvs\....-nAKWzegL\lib\shutil.py", line 686, in _make_zipfile
    zf.write(path, path)
  File "C:\Users\Alex\AppData\Local\Programs\Python\Python36-32\Lib\zipfile.py", line 1594, in write
    zinfo = ZipInfo.from_file(filename, arcname)
  File "C:\Users\Alex\AppData\Local\Programs\Python\Python36-32\Lib\zipfile.py", line 484, in from_file
    st = os.stat(filename)
FileNotFoundError: [WinError 2] The system cannot find the file specified: "'App'"

The same for '/App' and './App'. With full path it works, but I get all parent dirs, not just App.

Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:04:45) [MSC v.1900 32 bit (Intel)] on win32

Lander answered 24/3, 2018 at 17:41 Comment(5)
Read the docs: root_dir is a directory that will be the root directory of the archive; for example, we typically chdir into root_dir before creating the archive. You told it to create the archive in Release/App not Release.Armorer
@Armorer you mean my second attempt? Yes, I know, I was trying to add App/ prefix using base_dir.Lander
No, in that attempt, you were also setting root_dir to App. The doc tells you that it changes into root_dir and then creates the archive file from there. @ekhumoro 's first example works because it doesn't change root_dir.Armorer
@Armorer Yes, if I set root_dir to App and leave default base_dir (.) then it simply puts all files (*.exe etc.) to the root of the archive. But I thought that base_dir simply adds prefix (like App/) and this path doesn't have to exist.Lander
yeah, make_archive isn't very good. It uses chdir to go to the root directory, so it has to exist (and since chdir is global to the process, your whole program is no longer thread safe for file operations) and its documentation is quite confusing.Armorer
S
4

Here's a couple of solutions that worked or me:

# curdir: Release
shutil.make_archive('App-1.0', 'zip', '.', 'App')

# curdir: ../Release
shutil.make_archive('Release/App-1.0', 'zip', 'Release', 'App')
Sitter answered 24/3, 2018 at 18:6 Comment(2)
hm, looks like it works indeed. I guess I misunderstood something about that base_dir.Lander
See also my answer here: #46943514Binomial
F
2

What I learned while fighting with this is this:

shutil.make_archive('App-1.0', 'zip', '.', 'App')

While 'App-1.0' is technically a "filename", it must be represented as a path to the file without the extension ('c:\myfiles\myzipfile'). I have not played with all the variants for path names yet, so some of the shortcuts likely work (such as: 'Release/App-1.0').

Feinstein answered 6/5, 2020 at 5:18 Comment(0)
K
0

You can try creating the archive in a temporary directory and then moving it to the desired output directory. Following solution worked for me:

import os
import shutil
import tempfile

def create_zip(output_directory, zip_filename):
    try:
        # Create a temporary directory
        temp_dir = tempfile.mkdtemp()

        # Create the archive in the temporary directory
        temp_archive_path = os.path.join(temp_dir, f"{zip_filename}.zip")
        shutil.make_archive(temp_archive_path, 'zip', output_directory)

        # Move the archive from the temporary directory to the output directory
        final_archive_path = os.path.join(output_directory, f"{zip_filename}.zip")
        shutil.move(temp_archive_path, final_archive_path)

        # Clean up the temporary directory
        shutil.rmtree(temp_dir)

        return True
    except Exception as e:
        print(f"Error while creating zip file: {e}")
        return False
Kendry answered 4/1 at 9:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.