mirror of https://github.com/python/cpython
Added 'create_tree()'.
Changes to 'copy_file()': * added support for making hard links and symlinks * noted that it silently clobbers existing files when copying, but blows up if destination exists when linking -- hmmm... * error message tweak Added 'base_name' parameter to 'make_tarball()' and 'make_zipfile()'. Added 'make_archive()' -- wrapper around 'make_tarball()' or 'make_zipfile()' to take care of the archive "root directory".
This commit is contained in:
parent
318a9d7aa2
commit
32ce329ce4
|
@ -19,6 +19,11 @@ from distutils.spawn import spawn
|
|||
# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode
|
||||
PATH_CREATED = {}
|
||||
|
||||
# for generating verbose output in 'copy_file()'
|
||||
_copy_action = { None: 'copying',
|
||||
'hard': 'hard linking',
|
||||
'sym': 'symbolically linking' }
|
||||
|
||||
# I don't use os.makedirs because a) it's new to Python 1.5.2, and
|
||||
# b) it blows up if the directory already exists (I want to silently
|
||||
# succeed in that case).
|
||||
|
@ -83,6 +88,30 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0):
|
|||
# mkpath ()
|
||||
|
||||
|
||||
def create_tree (base_dir, files, mode=0777, verbose=0, dry_run=0):
|
||||
|
||||
"""Create all the empty directories under 'base_dir' needed to
|
||||
put 'files' there. 'base_dir' is just the a name of a directory
|
||||
which doesn't necessarily exist yet; 'files' is a list of filenames
|
||||
to be interpreted relative to 'base_dir'. 'base_dir' + the
|
||||
directory portion of every file in 'files' will be created if it
|
||||
doesn't already exist. 'mode', 'verbose' and 'dry_run' flags are as
|
||||
for 'mkpath()'."""
|
||||
|
||||
# First get the list of directories to create
|
||||
need_dir = {}
|
||||
for file in files:
|
||||
need_dir[os.path.join (base_dir, os.path.dirname (file))] = 1
|
||||
need_dirs = need_dir.keys()
|
||||
need_dirs.sort()
|
||||
|
||||
# Now create them
|
||||
for dir in need_dirs:
|
||||
mkpath (dir, mode, verbose, dry_run)
|
||||
|
||||
# create_tree ()
|
||||
|
||||
|
||||
def newer (source, target):
|
||||
"""Return true if 'source' exists and is more recently modified than
|
||||
'target', or if 'source' exists and 'target' doesn't. Return
|
||||
|
@ -241,6 +270,7 @@ def copy_file (src, dst,
|
|||
preserve_mode=1,
|
||||
preserve_times=1,
|
||||
update=0,
|
||||
link=None,
|
||||
verbose=0,
|
||||
dry_run=0):
|
||||
|
||||
|
@ -256,17 +286,32 @@ def copy_file (src, dst,
|
|||
'verbose' is true, then a one-line summary of the copy will be
|
||||
printed to stdout.
|
||||
|
||||
'link' allows you to make hard links (os.link) or symbolic links
|
||||
(os.symlink) instead of copying: set it to "hard" or "sym"; if it
|
||||
is None (the default), files are copied. Don't set 'link' on
|
||||
systems that don't support it: 'copy_file()' doesn't check if
|
||||
hard or symbolic linking is availalble.
|
||||
|
||||
Under Mac OS, uses the native file copy function in macostools;
|
||||
on other systems, uses '_copy_file_contents()' to copy file
|
||||
contents.
|
||||
|
||||
Return true if the file was copied (or would have been copied),
|
||||
false otherwise (ie. 'update' was true and the destination is
|
||||
up-to-date)."""
|
||||
|
||||
# XXX doesn't copy Mac-specific metadata
|
||||
|
||||
# XXX if the destination file already exists, we clobber it if
|
||||
# copying, but blow up if linking. Hmmm. And I don't know what
|
||||
# macostools.copyfile() does. Should definitely be consistent, and
|
||||
# should probably blow up if destination exists and we would be
|
||||
# changing it (ie. it's not already a hard/soft link to src OR
|
||||
# (not update) and (src newer than dst).
|
||||
|
||||
from stat import *
|
||||
|
||||
if not os.path.isfile (src):
|
||||
raise DistutilsFileError, \
|
||||
"can't copy '%s': not a regular file" % src
|
||||
"can't copy '%s': doesn't exist or not a regular file" % src
|
||||
|
||||
if os.path.isdir (dst):
|
||||
dir = dst
|
||||
|
@ -279,8 +324,13 @@ def copy_file (src, dst,
|
|||
print "not copying %s (output up-to-date)" % src
|
||||
return 0
|
||||
|
||||
try:
|
||||
action = _copy_action[link]
|
||||
except KeyError:
|
||||
raise ValueError, \
|
||||
"invalid value '%s' for 'link' argument" % link
|
||||
if verbose:
|
||||
print "copying %s -> %s" % (src, dir)
|
||||
print "%s %s -> %s" % (action, src, dir)
|
||||
|
||||
if dry_run:
|
||||
return 1
|
||||
|
@ -293,19 +343,29 @@ def copy_file (src, dst,
|
|||
except OSError, exc:
|
||||
raise DistutilsFileError, \
|
||||
"could not copy '%s' to '%s': %s" % (src, dst, exc[-1])
|
||||
return 1
|
||||
|
||||
# Otherwise use custom routine
|
||||
_copy_file_contents (src, dst)
|
||||
if preserve_mode or preserve_times:
|
||||
st = os.stat (src)
|
||||
# If linking (hard or symbolic), use the appropriate system call
|
||||
# (Unix only, of course, but that's the caller's responsibility)
|
||||
elif link == 'hard':
|
||||
if not (os.path.exists (dst) and os.path.samefile (src, dst)):
|
||||
os.link (src, dst)
|
||||
elif link == 'sym':
|
||||
if not (os.path.exists (dst) and os.path.samefile (src, dst)):
|
||||
os.symlink (src, dst)
|
||||
|
||||
# According to David Ascher <da@ski.org>, utime() should be done
|
||||
# before chmod() (at least under NT).
|
||||
if preserve_times:
|
||||
os.utime (dst, (st[ST_ATIME], st[ST_MTIME]))
|
||||
if preserve_mode:
|
||||
os.chmod (dst, S_IMODE (st[ST_MODE]))
|
||||
# Otherwise (non-Mac, not linking), copy the file contents and
|
||||
# (optionally) copy the times and mode.
|
||||
else:
|
||||
_copy_file_contents (src, dst)
|
||||
if preserve_mode or preserve_times:
|
||||
st = os.stat (src)
|
||||
|
||||
# According to David Ascher <da@ski.org>, utime() should be done
|
||||
# before chmod() (at least under NT).
|
||||
if preserve_times:
|
||||
os.utime (dst, (st[ST_ATIME], st[ST_MTIME]))
|
||||
if preserve_mode:
|
||||
os.chmod (dst, S_IMODE (st[ST_MODE]))
|
||||
|
||||
return 1
|
||||
|
||||
|
@ -375,7 +435,7 @@ def copy_tree (src, dst,
|
|||
else:
|
||||
copy_file (src_name, dst_name,
|
||||
preserve_mode, preserve_times,
|
||||
update, verbose, dry_run)
|
||||
update, None, verbose, dry_run)
|
||||
outputs.append (dst_name)
|
||||
|
||||
return outputs
|
||||
|
@ -562,7 +622,8 @@ def subst_vars (str, local_vars):
|
|||
# subst_vars ()
|
||||
|
||||
|
||||
def make_tarball (base_dir, compress="gzip", verbose=0, dry_run=0):
|
||||
def make_tarball (base_name, base_dir, compress="gzip",
|
||||
verbose=0, dry_run=0):
|
||||
"""Create a (possibly compressed) tar file from all the files under
|
||||
'base_dir'. 'compress' must be "gzip" (the default), "compress", or
|
||||
None. Both "tar" and the compression utility named by 'compress'
|
||||
|
@ -584,7 +645,7 @@ def make_tarball (base_dir, compress="gzip", verbose=0, dry_run=0):
|
|||
raise ValueError, \
|
||||
"bad value for 'compress': must be None, 'gzip', or 'compress'"
|
||||
|
||||
archive_name = base_dir + ".tar"
|
||||
archive_name = base_name + ".tar"
|
||||
cmd = ["tar", "-cf", archive_name, base_dir]
|
||||
spawn (cmd, verbose=verbose, dry_run=dry_run)
|
||||
|
||||
|
@ -597,21 +658,21 @@ def make_tarball (base_dir, compress="gzip", verbose=0, dry_run=0):
|
|||
# make_tarball ()
|
||||
|
||||
|
||||
def make_zipfile (base_dir, verbose=0, dry_run=0):
|
||||
"""Create a ZIP file from all the files under 'base_dir'. The
|
||||
output ZIP file will be named 'base_dir' + ".zip". Uses either the
|
||||
def make_zipfile (base_name, base_dir, verbose=0, dry_run=0):
|
||||
"""Create a zip file from all the files under 'base_dir'. The
|
||||
output zip file will be named 'base_dir' + ".zip". Uses either the
|
||||
InfoZIP "zip" utility (if installed and found on the default search
|
||||
path) or the "zipfile" Python module (if available). If neither
|
||||
tool is available, raises DistutilsExecError. Returns the name
|
||||
of the output ZIP file."""
|
||||
of the output zip file."""
|
||||
|
||||
# This initially assumed the Unix 'zip' utility -- but
|
||||
# apparently InfoZIP's zip.exe works the same under Windows, so
|
||||
# no changes needed!
|
||||
|
||||
zip_filename = base_dir + ".zip"
|
||||
zip_filename = base_name + ".zip"
|
||||
try:
|
||||
spawn (["zip", "-r", zip_filename, base_dir],
|
||||
spawn (["zip", "-rq", zip_filename, base_dir],
|
||||
verbose=verbose, dry_run=dry_run)
|
||||
except DistutilsExecError:
|
||||
|
||||
|
@ -649,3 +710,52 @@ def make_zipfile (base_dir, verbose=0, dry_run=0):
|
|||
return zip_filename
|
||||
|
||||
# make_zipfile ()
|
||||
|
||||
|
||||
def make_archive (base_name, format,
|
||||
root_dir=None, base_dir=None,
|
||||
verbose=0, dry_run=0):
|
||||
|
||||
"""Create an archive file (eg. zip or tar). 'base_name' is the name
|
||||
of the file to create, minus any format-specific extension; 'format'
|
||||
is the archive format: one of "zip", "tar", "ztar", or "gztar".
|
||||
'root_dir' is a directory that will be the root directory of the
|
||||
archive; ie. we typically chdir into 'root_dir' before creating the
|
||||
archive. 'base_dir' is the directory where we start archiving from;
|
||||
ie. 'base_dir' will be the common prefix of all files and
|
||||
directories in the archive. 'root_dir' and 'base_dir' both default
|
||||
to the current directory."""
|
||||
|
||||
save_cwd = os.getcwd()
|
||||
if root_dir is not None:
|
||||
if verbose:
|
||||
print "changing into '%s'" % root_dir
|
||||
base_name = os.path.abspath (base_name)
|
||||
if not dry_run:
|
||||
os.chdir (root_dir)
|
||||
|
||||
if base_dir is None:
|
||||
base_dir = os.curdir
|
||||
|
||||
kwargs = { 'verbose': verbose,
|
||||
'dry_run': dry_run }
|
||||
|
||||
if format == 'gztar':
|
||||
func = make_tarball
|
||||
kwargs['compress'] = 'gzip'
|
||||
elif format == 'ztar':
|
||||
func = make_tarball
|
||||
kwargs['compress'] = 'compress'
|
||||
elif format == 'tar':
|
||||
func = make_tarball
|
||||
elif format == 'zip':
|
||||
func = make_zipfile
|
||||
|
||||
apply (func, (base_name, base_dir), kwargs)
|
||||
|
||||
if root_dir is not None:
|
||||
if verbose:
|
||||
print "changing back to '%s'" % save_cwd
|
||||
os.chdir (save_cwd)
|
||||
|
||||
# make_archive ()
|
||||
|
|
Loading…
Reference in New Issue