Home | History | Annotate | Download | only in distutils
      1 """distutils.dir_util
      2 
      3 Utility functions for manipulating directories and directory trees."""
      4 
      5 __revision__ = "$Id$"
      6 
      7 import os
      8 import errno
      9 from distutils.errors import DistutilsFileError, DistutilsInternalError
     10 from distutils import log
     11 
     12 # cache for by mkpath() -- in addition to cheapening redundant calls,

     13 # eliminates redundant "creating /foo/bar/baz" messages in dry-run mode

     14 _path_created = {}
     15 
     16 # I don't use os.makedirs because a) it's new to Python 1.5.2, and

     17 # b) it blows up if the directory already exists (I want to silently

     18 # succeed in that case).

     19 def mkpath(name, mode=0777, verbose=1, dry_run=0):
     20     """Create a directory and any missing ancestor directories.
     21 
     22     If the directory already exists (or if 'name' is the empty string, which
     23     means the current directory, which of course exists), then do nothing.
     24     Raise DistutilsFileError if unable to create some directory along the way
     25     (eg. some sub-path exists, but is a file rather than a directory).
     26     If 'verbose' is true, print a one-line summary of each mkdir to stdout.
     27     Return the list of directories actually created.
     28     """
     29 
     30     global _path_created
     31 
     32     # Detect a common bug -- name is None

     33     if not isinstance(name, basestring):
     34         raise DistutilsInternalError, \
     35               "mkpath: 'name' must be a string (got %r)" % (name,)
     36 
     37     # XXX what's the better way to handle verbosity? print as we create

     38     # each directory in the path (the current behaviour), or only announce

     39     # the creation of the whole path? (quite easy to do the latter since

     40     # we're not using a recursive algorithm)

     41 
     42     name = os.path.normpath(name)
     43     created_dirs = []
     44     if os.path.isdir(name) or name == '':
     45         return created_dirs
     46     if _path_created.get(os.path.abspath(name)):
     47         return created_dirs
     48 
     49     (head, tail) = os.path.split(name)
     50     tails = [tail]                      # stack of lone dirs to create

     51 
     52     while head and tail and not os.path.isdir(head):
     53         (head, tail) = os.path.split(head)
     54         tails.insert(0, tail)          # push next higher dir onto stack

     55 
     56     # now 'head' contains the deepest directory that already exists

     57     # (that is, the child of 'head' in 'name' is the highest directory

     58     # that does *not* exist)

     59     for d in tails:
     60         #print "head = %s, d = %s: " % (head, d),

     61         head = os.path.join(head, d)
     62         abs_head = os.path.abspath(head)
     63 
     64         if _path_created.get(abs_head):
     65             continue
     66 
     67         if verbose >= 1:
     68             log.info("creating %s", head)
     69 
     70         if not dry_run:
     71             try:
     72                 os.mkdir(head, mode)
     73             except OSError, exc:
     74                 if not (exc.errno == errno.EEXIST and os.path.isdir(head)):
     75                     raise DistutilsFileError(
     76                           "could not create '%s': %s" % (head, exc.args[-1]))
     77             created_dirs.append(head)
     78 
     79         _path_created[abs_head] = 1
     80     return created_dirs
     81 
     82 def create_tree(base_dir, files, mode=0777, verbose=1, dry_run=0):
     83     """Create all the empty directories under 'base_dir' needed to put 'files'
     84     there.
     85 
     86     'base_dir' is just the a name of a directory which doesn't necessarily
     87     exist yet; 'files' is a list of filenames to be interpreted relative to
     88     'base_dir'.  'base_dir' + the directory portion of every file in 'files'
     89     will be created if it doesn't already exist.  'mode', 'verbose' and
     90     'dry_run' flags are as for 'mkpath()'.
     91     """
     92     # First get the list of directories to create

     93     need_dir = {}
     94     for file in files:
     95         need_dir[os.path.join(base_dir, os.path.dirname(file))] = 1
     96     need_dirs = need_dir.keys()
     97     need_dirs.sort()
     98 
     99     # Now create them

    100     for dir in need_dirs:
    101         mkpath(dir, mode, verbose=verbose, dry_run=dry_run)
    102 
    103 def copy_tree(src, dst, preserve_mode=1, preserve_times=1,
    104               preserve_symlinks=0, update=0, verbose=1, dry_run=0):
    105     """Copy an entire directory tree 'src' to a new location 'dst'.
    106 
    107     Both 'src' and 'dst' must be directory names.  If 'src' is not a
    108     directory, raise DistutilsFileError.  If 'dst' does not exist, it is
    109     created with 'mkpath()'.  The end result of the copy is that every
    110     file in 'src' is copied to 'dst', and directories under 'src' are
    111     recursively copied to 'dst'.  Return the list of files that were
    112     copied or might have been copied, using their output name.  The
    113     return value is unaffected by 'update' or 'dry_run': it is simply
    114     the list of all files under 'src', with the names changed to be
    115     under 'dst'.
    116 
    117     'preserve_mode' and 'preserve_times' are the same as for
    118     'copy_file'; note that they only apply to regular files, not to
    119     directories.  If 'preserve_symlinks' is true, symlinks will be
    120     copied as symlinks (on platforms that support them!); otherwise
    121     (the default), the destination of the symlink will be copied.
    122     'update' and 'verbose' are the same as for 'copy_file'.
    123     """
    124     from distutils.file_util import copy_file
    125 
    126     if not dry_run and not os.path.isdir(src):
    127         raise DistutilsFileError, \
    128               "cannot copy tree '%s': not a directory" % src
    129     try:
    130         names = os.listdir(src)
    131     except os.error, (errno, errstr):
    132         if dry_run:
    133             names = []
    134         else:
    135             raise DistutilsFileError, \
    136                   "error listing files in '%s': %s" % (src, errstr)
    137 
    138     if not dry_run:
    139         mkpath(dst, verbose=verbose)
    140 
    141     outputs = []
    142 
    143     for n in names:
    144         src_name = os.path.join(src, n)
    145         dst_name = os.path.join(dst, n)
    146 
    147         if preserve_symlinks and os.path.islink(src_name):
    148             link_dest = os.readlink(src_name)
    149             if verbose >= 1:
    150                 log.info("linking %s -> %s", dst_name, link_dest)
    151             if not dry_run:
    152                 os.symlink(link_dest, dst_name)
    153             outputs.append(dst_name)
    154 
    155         elif os.path.isdir(src_name):
    156             outputs.extend(
    157                 copy_tree(src_name, dst_name, preserve_mode,
    158                           preserve_times, preserve_symlinks, update,
    159                           verbose=verbose, dry_run=dry_run))
    160         else:
    161             copy_file(src_name, dst_name, preserve_mode,
    162                       preserve_times, update, verbose=verbose,
    163                       dry_run=dry_run)
    164             outputs.append(dst_name)
    165 
    166     return outputs
    167 
    168 def _build_cmdtuple(path, cmdtuples):
    169     """Helper for remove_tree()."""
    170     for f in os.listdir(path):
    171         real_f = os.path.join(path,f)
    172         if os.path.isdir(real_f) and not os.path.islink(real_f):
    173             _build_cmdtuple(real_f, cmdtuples)
    174         else:
    175             cmdtuples.append((os.remove, real_f))
    176     cmdtuples.append((os.rmdir, path))
    177 
    178 def remove_tree(directory, verbose=1, dry_run=0):
    179     """Recursively remove an entire directory tree.
    180 
    181     Any errors are ignored (apart from being reported to stdout if 'verbose'
    182     is true).
    183     """
    184     from distutils.util import grok_environment_error
    185     global _path_created
    186 
    187     if verbose >= 1:
    188         log.info("removing '%s' (and everything under it)", directory)
    189     if dry_run:
    190         return
    191     cmdtuples = []
    192     _build_cmdtuple(directory, cmdtuples)
    193     for cmd in cmdtuples:
    194         try:
    195             cmd[0](cmd[1])
    196             # remove dir from cache if it's already there

    197             abspath = os.path.abspath(cmd[1])
    198             if abspath in _path_created:
    199                 del _path_created[abspath]
    200         except (IOError, OSError), exc:
    201             log.warn(grok_environment_error(
    202                     exc, "error removing %s: " % directory))
    203 
    204 def ensure_relative(path):
    205     """Take the full path 'path', and make it a relative path.
    206 
    207     This is useful to make 'path' the second argument to os.path.join().
    208     """
    209     drive, path = os.path.splitdrive(path)
    210     if path[0:1] == os.sep:
    211         path = drive + path[1:]
    212     return path
    213