PageRenderTime 285ms CodeModel.GetById 131ms app.highlight 18ms RepoModel.GetById 132ms app.codeStats 1ms

/Lib/distutils/dir_util.py

http://unladen-swallow.googlecode.com/
Python | 227 lines | 206 code | 8 blank | 13 comment | 6 complexity | a3c2e8d47a8f7de5ed6d8fd1910bf7ca MD5 | raw file
  1"""distutils.dir_util
  2
  3Utility functions for manipulating directories and directory trees."""
  4
  5# This module should be kept compatible with Python 2.1.
  6
  7__revision__ = "$Id: dir_util.py 60923 2008-02-21 18:18:37Z guido.van.rossum $"
  8
  9import os, sys
 10from types import *
 11from distutils.errors import DistutilsFileError, DistutilsInternalError
 12from distutils import log
 13
 14# cache for by mkpath() -- in addition to cheapening redundant calls,
 15# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode
 16_path_created = {}
 17
 18# I don't use os.makedirs because a) it's new to Python 1.5.2, and
 19# b) it blows up if the directory already exists (I want to silently
 20# succeed in that case).
 21def mkpath (name, mode=0777, verbose=0, dry_run=0):
 22    """Create a directory and any missing ancestor directories.  If the
 23       directory already exists (or if 'name' is the empty string, which
 24       means the current directory, which of course exists), then do
 25       nothing.  Raise DistutilsFileError if unable to create some
 26       directory along the way (eg. some sub-path exists, but is a file
 27       rather than a directory).  If 'verbose' is true, print a one-line
 28       summary of each mkdir to stdout.  Return the list of directories
 29       actually created."""
 30
 31    global _path_created
 32
 33    # Detect a common bug -- name is None
 34    if not isinstance(name, StringTypes):
 35        raise DistutilsInternalError, \
 36              "mkpath: 'name' must be a string (got %r)" % (name,)
 37
 38    # XXX what's the better way to handle verbosity? print as we create
 39    # each directory in the path (the current behaviour), or only announce
 40    # the creation of the whole path? (quite easy to do the latter since
 41    # we're not using a recursive algorithm)
 42
 43    name = os.path.normpath(name)
 44    created_dirs = []
 45    if os.path.isdir(name) or name == '':
 46        return created_dirs
 47    if _path_created.get(os.path.abspath(name)):
 48        return created_dirs
 49
 50    (head, tail) = os.path.split(name)
 51    tails = [tail]                      # stack of lone dirs to create
 52
 53    while head and tail and not os.path.isdir(head):
 54        #print "splitting '%s': " % head,
 55        (head, tail) = os.path.split(head)
 56        #print "to ('%s','%s')" % (head, tail)
 57        tails.insert(0, tail)          # push next higher dir onto stack
 58
 59    #print "stack of tails:", tails
 60
 61    # now 'head' contains the deepest directory that already exists
 62    # (that is, the child of 'head' in 'name' is the highest directory
 63    # that does *not* exist)
 64    for d in tails:
 65        #print "head = %s, d = %s: " % (head, d),
 66        head = os.path.join(head, d)
 67        abs_head = os.path.abspath(head)
 68
 69        if _path_created.get(abs_head):
 70            continue
 71
 72        log.info("creating %s", head)
 73
 74        if not dry_run:
 75            try:
 76                os.mkdir(head)
 77                created_dirs.append(head)
 78            except OSError, exc:
 79                raise DistutilsFileError, \
 80                      "could not create '%s': %s" % (head, exc[-1])
 81
 82        _path_created[abs_head] = 1
 83    return created_dirs
 84
 85# mkpath ()
 86
 87
 88def create_tree (base_dir, files, mode=0777, verbose=0, dry_run=0):
 89
 90    """Create all the empty directories under 'base_dir' needed to
 91       put 'files' there.  'base_dir' is just the a name of a directory
 92       which doesn't necessarily exist yet; 'files' is a list of filenames
 93       to be interpreted relative to 'base_dir'.  'base_dir' + the
 94       directory portion of every file in 'files' will be created if it
 95       doesn't already exist.  'mode', 'verbose' and 'dry_run' flags are as
 96       for 'mkpath()'."""
 97
 98    # First get the list of directories to create
 99    need_dir = {}
100    for file in files:
101        need_dir[os.path.join(base_dir, os.path.dirname(file))] = 1
102    need_dirs = need_dir.keys()
103    need_dirs.sort()
104
105    # Now create them
106    for dir in need_dirs:
107        mkpath(dir, mode, dry_run=dry_run)
108
109# create_tree ()
110
111
112def copy_tree (src, dst,
113               preserve_mode=1,
114               preserve_times=1,
115               preserve_symlinks=0,
116               update=0,
117               verbose=0,
118               dry_run=0):
119
120    """Copy an entire directory tree 'src' to a new location 'dst'.  Both
121       'src' and 'dst' must be directory names.  If 'src' is not a
122       directory, raise DistutilsFileError.  If 'dst' does not exist, it is
123       created with 'mkpath()'.  The end result of the copy is that every
124       file in 'src' is copied to 'dst', and directories under 'src' are
125       recursively copied to 'dst'.  Return the list of files that were
126       copied or might have been copied, using their output name.  The
127       return value is unaffected by 'update' or 'dry_run': it is simply
128       the list of all files under 'src', with the names changed to be
129       under 'dst'.
130
131       'preserve_mode' and 'preserve_times' are the same as for
132       'copy_file'; note that they only apply to regular files, not to
133       directories.  If 'preserve_symlinks' is true, symlinks will be
134       copied as symlinks (on platforms that support them!); otherwise
135       (the default), the destination of the symlink will be copied.
136       'update' and 'verbose' are the same as for 'copy_file'."""
137
138    from distutils.file_util import copy_file
139
140    if not dry_run and not os.path.isdir(src):
141        raise DistutilsFileError, \
142              "cannot copy tree '%s': not a directory" % src
143    try:
144        names = os.listdir(src)
145    except os.error, (errno, errstr):
146        if dry_run:
147            names = []
148        else:
149            raise DistutilsFileError, \
150                  "error listing files in '%s': %s" % (src, errstr)
151
152    if not dry_run:
153        mkpath(dst)
154
155    outputs = []
156
157    for n in names:
158        src_name = os.path.join(src, n)
159        dst_name = os.path.join(dst, n)
160
161        if preserve_symlinks and os.path.islink(src_name):
162            link_dest = os.readlink(src_name)
163            log.info("linking %s -> %s", dst_name, link_dest)
164            if not dry_run:
165                os.symlink(link_dest, dst_name)
166            outputs.append(dst_name)
167
168        elif os.path.isdir(src_name):
169            outputs.extend(
170                copy_tree(src_name, dst_name, preserve_mode,
171                          preserve_times, preserve_symlinks, update,
172                          dry_run=dry_run))
173        else:
174            copy_file(src_name, dst_name, preserve_mode,
175                      preserve_times, update, dry_run=dry_run)
176            outputs.append(dst_name)
177
178    return outputs
179
180# copy_tree ()
181
182# Helper for remove_tree()
183def _build_cmdtuple(path, cmdtuples):
184    for f in os.listdir(path):
185        real_f = os.path.join(path,f)
186        if os.path.isdir(real_f) and not os.path.islink(real_f):
187            _build_cmdtuple(real_f, cmdtuples)
188        else:
189            cmdtuples.append((os.remove, real_f))
190    cmdtuples.append((os.rmdir, path))
191
192
193def remove_tree (directory, verbose=0, dry_run=0):
194    """Recursively remove an entire directory tree.  Any errors are ignored
195    (apart from being reported to stdout if 'verbose' is true).
196    """
197    from distutils.util import grok_environment_error
198    global _path_created
199
200    log.info("removing '%s' (and everything under it)", directory)
201    if dry_run:
202        return
203    cmdtuples = []
204    _build_cmdtuple(directory, cmdtuples)
205    for cmd in cmdtuples:
206        try:
207            apply(cmd[0], (cmd[1],))
208            # remove dir from cache if it's already there
209            abspath = os.path.abspath(cmd[1])
210            if abspath in _path_created:
211                del _path_created[abspath]
212        except (IOError, OSError), exc:
213            log.warn(grok_environment_error(
214                    exc, "error removing %s: " % directory))
215
216
217def ensure_relative (path):
218    """Take the full path 'path', and make it a relative path so
219    it can be the second argument to os.path.join().
220    """
221    drive, path = os.path.splitdrive(path)
222    if sys.platform == 'mac':
223        return os.sep + path
224    else:
225        if path[0:1] == os.sep:
226            path = drive + path[1:]
227        return path