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 n.startswith('.nfs'):
    148             # skip NFS rename files
    149             continue
    150 
    151         if preserve_symlinks and os.path.islink(src_name):
    152             link_dest = os.readlink(src_name)
    153             if verbose >= 1:
    154                 log.info("linking %s -> %s", dst_name, link_dest)
    155             if not dry_run:
    156                 os.symlink(link_dest, dst_name)
    157             outputs.append(dst_name)
    158 
    159         elif os.path.isdir(src_name):
    160             outputs.extend(
    161                 copy_tree(src_name, dst_name, preserve_mode,
    162                           preserve_times, preserve_symlinks, update,
    163                           verbose=verbose, dry_run=dry_run))
    164         else:
    165             copy_file(src_name, dst_name, preserve_mode,
    166                       preserve_times, update, verbose=verbose,
    167                       dry_run=dry_run)
    168             outputs.append(dst_name)
    169 
    170     return outputs
    171 
    172 def _build_cmdtuple(path, cmdtuples):
    173     """Helper for remove_tree()."""
    174     for f in os.listdir(path):
    175         real_f = os.path.join(path,f)
    176         if os.path.isdir(real_f) and not os.path.islink(real_f):
    177             _build_cmdtuple(real_f, cmdtuples)
    178         else:
    179             cmdtuples.append((os.remove, real_f))
    180     cmdtuples.append((os.rmdir, path))
    181 
    182 def remove_tree(directory, verbose=1, dry_run=0):
    183     """Recursively remove an entire directory tree.
    184 
    185     Any errors are ignored (apart from being reported to stdout if 'verbose'
    186     is true).
    187     """
    188     from distutils.util import grok_environment_error
    189     global _path_created
    190 
    191     if verbose >= 1:
    192         log.info("removing '%s' (and everything under it)", directory)
    193     if dry_run:
    194         return
    195     cmdtuples = []
    196     _build_cmdtuple(directory, cmdtuples)
    197     for cmd in cmdtuples:
    198         try:
    199             cmd[0](cmd[1])
    200             # remove dir from cache if it's already there
    201             abspath = os.path.abspath(cmd[1])
    202             if abspath in _path_created:
    203                 del _path_created[abspath]
    204         except (IOError, OSError), exc:
    205             log.warn(grok_environment_error(
    206                     exc, "error removing %s: " % directory))
    207 
    208 def ensure_relative(path):
    209     """Take the full path 'path', and make it a relative path.
    210 
    211     This is useful to make 'path' the second argument to os.path.join().
    212     """
    213     drive, path = os.path.splitdrive(path)
    214     if path[0:1] == os.sep:
    215         path = drive + path[1:]
    216     return path
    217