1 """distutils.archive_util
3 Utility functions for creating archive files (tarballs, zip files,
4 that sort of thing)."""
7 from warnings
import warn
16 from .errors
import DistutilsExecError
17 from .spawn
import spawn
18 from .dir_util
import mkpath
22 from pwd
import getpwnam
27 from grp
import getgrnam
33 """Returns a gid, given a group name."""
34 if getgrnam
is None or name
is None:
37 result
= getgrnam(name
)
40 if result
is not None:
46 """Returns an uid, given a user name."""
47 if getpwnam
is None or name
is None:
50 result
= getpwnam(name
)
53 if result
is not None:
59 base_name
, base_dir
, compress
="gzip", verbose
=0, dry_run
=0, owner
=None, group
=None
61 """Create a (possibly compressed) tar file from all the files under
64 'compress' must be "gzip" (the default), "bzip2", "xz", "compress", or
65 None. ("compress" will be deprecated in Python 3.2)
67 'owner' and 'group' can be used to define an owner and a group for the
68 archive that is being built. If not provided, the current owner and group
71 The output tar file will be named 'base_dir' + ".tar", possibly plus
72 the appropriate compression extension (".gz", ".bz2", ".xz" or ".Z").
74 Returns the output filename.
83 compress_ext
= {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz', 'compress': '.Z'}
85 # flags for compression program, each element of list will be an argument
86 if compress
is not None and compress
not in compress_ext
.keys():
88 "bad value for 'compress': must be None, 'gzip', 'bzip2', "
92 archive_name
= base_name
+ '.tar'
93 if compress
!= 'compress':
94 archive_name
+= compress_ext
.get(compress
, '')
96 mkpath(os
.path
.dirname(archive_name
), dry_run
=dry_run
)
98 # creating the tarball
99 import tarfile
# late import so Python build itself doesn't break
101 log
.info('Creating tar archive')
103 uid
= _get_uid(owner
)
104 gid
= _get_gid(group
)
106 def _set_uid_gid(tarinfo
):
109 tarinfo
.gname
= group
112 tarinfo
.uname
= owner
116 tar
= tarfile
.open(archive_name
, 'w|%s' % tar_compression
[compress
])
118 tar
.add(base_dir
, filter=_set_uid_gid
)
122 # compression using `compress`
123 if compress
== 'compress':
124 warn("'compress' is deprecated.", DeprecationWarning)
125 # the option varies depending on the platform
126 compressed_name
= archive_name
+ compress_ext
[compress
]
127 if sys
.platform
== 'win32':
128 cmd
= [compress
, archive_name
, compressed_name
]
130 cmd
= [compress
, '-f', archive_name
]
131 spawn(cmd
, dry_run
=dry_run
)
132 return compressed_name
137 def make_zipfile(base_name
, base_dir
, verbose
=0, dry_run
=0): # noqa: C901
138 """Create a zip file from all the files under 'base_dir'.
140 The output zip file will be named 'base_name' + ".zip". Uses either the
141 "zipfile" Python module (if available) or the InfoZIP "zip" utility
142 (if installed and found on the default search path). If neither tool is
143 available, raises DistutilsExecError. Returns the name of the output zip
146 zip_filename
= base_name
+ ".zip"
147 mkpath(os
.path
.dirname(zip_filename
), dry_run
=dry_run
)
149 # If zipfile module is not available, try spawning an external
158 spawn(["zip", zipoptions
, zip_filename
, base_dir
], dry_run
=dry_run
)
159 except DistutilsExecError
:
160 # XXX really should distinguish between "couldn't find
161 # external 'zip' command" and "zip failed".
162 raise DistutilsExecError(
164 "unable to create zip file '%s': "
165 "could neither import the 'zipfile' module nor "
166 "find a standalone zip utility"
172 log
.info("creating '%s' and adding '%s' to it", zip_filename
, base_dir
)
176 zip = zipfile
.ZipFile(
177 zip_filename
, "w", compression
=zipfile
.ZIP_DEFLATED
180 zip = zipfile
.ZipFile(zip_filename
, "w", compression
=zipfile
.ZIP_STORED
)
183 if base_dir
!= os
.curdir
:
184 path
= os
.path
.normpath(os
.path
.join(base_dir
, ''))
185 zip.write(path
, path
)
186 log
.info("adding '%s'", path
)
187 for dirpath
, dirnames
, filenames
in os
.walk(base_dir
):
188 for name
in dirnames
:
189 path
= os
.path
.normpath(os
.path
.join(dirpath
, name
, ''))
190 zip.write(path
, path
)
191 log
.info("adding '%s'", path
)
192 for name
in filenames
:
193 path
= os
.path
.normpath(os
.path
.join(dirpath
, name
))
194 if os
.path
.isfile(path
):
195 zip.write(path
, path
)
196 log
.info("adding '%s'", path
)
202 'gztar': (make_tarball
, [('compress', 'gzip')], "gzip'ed tar-file"),
203 'bztar': (make_tarball
, [('compress', 'bzip2')], "bzip2'ed tar-file"),
204 'xztar': (make_tarball
, [('compress', 'xz')], "xz'ed tar-file"),
205 'ztar': (make_tarball
, [('compress', 'compress')], "compressed tar file"),
206 'tar': (make_tarball
, [('compress', None)], "uncompressed tar file"),
207 'zip': (make_zipfile
, [], "ZIP file"),
211 def check_archive_formats(formats
):
212 """Returns the first format from the 'format' list that is unknown.
214 If all formats are known, returns None
216 for format
in formats
:
217 if format
not in ARCHIVE_FORMATS
:
232 """Create an archive file (eg. zip or tar).
234 'base_name' is the name of the file to create, minus any format-specific
235 extension; 'format' is the archive format: one of "zip", "tar", "gztar",
236 "bztar", "xztar", or "ztar".
238 'root_dir' is a directory that will be the root directory of the
239 archive; ie. we typically chdir into 'root_dir' before creating the
240 archive. 'base_dir' is the directory where we start archiving from;
241 ie. 'base_dir' will be the common prefix of all files and
242 directories in the archive. 'root_dir' and 'base_dir' both default
243 to the current directory. Returns the name of the archive file.
245 'owner' and 'group' are used when creating a tar archive. By default,
246 uses the current owner and group.
248 save_cwd
= os
.getcwd()
249 if root_dir
is not None:
250 log
.debug("changing into '%s'", root_dir
)
251 base_name
= os
.path
.abspath(base_name
)
258 kwargs
= {'dry_run': dry_run}
261 format_info
= ARCHIVE_FORMATS
[format
]
263 raise ValueError("unknown archive format '%s'" % format
)
265 func
= format_info
[0]
266 for arg
, val
in format_info
[1]:
270 kwargs
['owner'] = owner
271 kwargs
['group'] = group
274 filename
= func(base_name
, base_dir
, **kwargs
)
276 if root_dir
is not None:
277 log
.debug("changing back to '%s'", save_cwd
)