Home | History | Annotate | Download | only in distutils
      1 """distutils.dir_util
      2 
      3 Utility functions for manipulating directories and directory trees."""
      4 
      5 import os
      6 import errno
      7 from distutils.errors import DistutilsFileError, DistutilsInternalError
      8 from distutils import log
      9 
     10 # cache for by mkpath() -- in addition to cheapening redundant calls,
     11 # eliminates redundant "creating /foo/bar/baz" messages in dry-run mode
     12 _path_created = {}
     13 
     14 # I don't use os.makedirs because a) it's new to Python 1.5.2, and
     15 # b) it blows up if the directory already exists (I want to silently
     16 # succeed in that case).
     17 def mkpath(name, mode=0o777, verbose=1, dry_run=0):
     18     """Create a directory and any missing ancestor directories.
     19 
     20     If the directory already exists (or if 'name' is the empty string, which
     21     means the current directory, which of course exists), then do nothing.
     22     Raise DistutilsFileError if unable to create some directory along the way
     23     (eg. some sub-path exists, but is a file rather than a directory).
     24     If 'verbose' is true, print a one-line summary of each mkdir to stdout.
     25     Return the list of directories actually created.
     26     """
     27 
     28     global _path_created
     29 
     30     # Detect a common bug -- name is None
     31     if not isinstance(name, str):
     32         raise DistutilsInternalError(
     33               "mkpath: 'name' must be a string (got %r)" % (name,))
     34 
     35     # XXX what's the better way to handle verbosity? print as we create
     36     # each directory in the path (the current behaviour), or only announce
     37     # the creation of the whole path? (quite easy to do the latter since
     38     # we're not using a recursive algorithm)
     39 
     40     name = os.path.normpath(name)
     41     created_dirs = []
     42     if os.path.isdir(name) or name == '':
     43         return created_dirs
     44     if _path_created.get(os.path.abspath(name)):
     45         return created_dirs
     46 
     47     (head, tail) = os.path.split(name)
     48     tails = [tail]                      # stack of lone dirs to create
     49 
     50     while head and tail and not os.path.isdir(head):
     51         (head, tail) = os.path.split(head)
     52         tails.insert(0, tail)          # push next higher dir onto stack
     53 
     54     # now 'head' contains the deepest directory that already exists
     55     # (that is, the child of 'head' in 'name' is the highest directory
     56     # that does *not* exist)
     57     for d in tails:
     58         #print "head = %s, d = %s: " % (head, d),
     59         head = os.path.join(head, d)
     60         abs_head = os.path.abspath(head)
     61 
     62         if _path_created.get(abs_head):
     63             continue
     64 
     65         if verbose >= 1:
     66             log.info("creating %s", head)
     67 
     68         if not dry_run:
     69             try:
     70                 os.mkdir(head, mode)
     71             except OSError as exc:
     72                 if not (exc.errno == errno.EEXIST and os.path.isdir(head)):
     73                     raise DistutilsFileError(
     74                           "could not create '%s': %s" % (head, exc.args[-1]))
     75             created_dirs.append(head)
     76 
     77         _path_created[abs_head] = 1
     78     return created_dirs
     79 
     80 def create_tree(base_dir, files, mode=0o777, verbose=1, dry_run=0):
     81     """Create all the empty directories under 'base_dir' needed to put 'files'
     82     there.
     83 
     84     'base_dir' is just the name of a directory which doesn't necessarily
     85     exist yet; 'files' is a list of filenames to be interpreted relative to
     86     'base_dir'.  'base_dir' + the directory portion of every file in 'files'
     87     will be created if it doesn't already exist.  'mode', 'verbose' and
     88     'dry_run' flags are as for 'mkpath()'.
     89     """
     90     # First get the list of directories to create
     91     need_dir = set()
     92     for file in files:
     93         need_dir.add(os.path.join(base_dir, os.path.dirname(file)))
     94 
     95     # Now create them
     96     for dir in sorted(need_dir):
     97         mkpath(dir, mode, verbose=verbose, dry_run=dry_run)
     98 
     99 def copy_tree(src, dst, preserve_mode=1, preserve_times=1,
    100               preserve_symlinks=0, update=0, verbose=1, dry_run=0):
    101     """Copy an entire directory tree 'src' to a new location 'dst'.
    102 
    103     Both 'src' and 'dst' must be directory names.  If 'src' is not a
    104     directory, raise DistutilsFileError.  If 'dst' does not exist, it is
    105     created with 'mkpath()'.  The end result of the copy is that every
    106     file in 'src' is copied to 'dst', and directories under 'src' are
    107     recursively copied to 'dst'.  Return the list of files that were
    108     copied or might have been copied, using their output name.  The
    109     return value is unaffected by 'update' or 'dry_run': it is simply
    110     the list of all files under 'src', with the names changed to be
    111     under 'dst'.
    112 
    113     'preserve_mode' and 'preserve_times' are the same as for
    114     'copy_file'; note that they only apply to regular files, not to
    115     directories.  If 'preserve_symlinks' is true, symlinks will be
    116     copied as symlinks (on platforms that support them!); otherwise
    117     (the default), the destination of the symlink will be copied.
    118     'update' and 'verbose' are the same as for 'copy_file'.
    119     """
    120     from distutils.file_util import copy_file
    121 
    122     if not dry_run and not os.path.isdir(src):
    123         raise DistutilsFileError(
    124               "cannot copy tree '%s': not a directory" % src)
    125     try:
    126         names = os.listdir(src)
    127     except OSError as e:
    128         if dry_run:
    129             names = []
    130         else:
    131             raise DistutilsFileError(
    132                   "error listing files in '%s': %s" % (src, e.strerror))
    133 
    134     if not dry_run:
    135         mkpath(dst, verbose=verbose)
    136 
    137     outputs = []
    138 
    139     for n in names:
    140         src_name = os.path.join(src, n)
    141         dst_name = os.path.join(dst, n)
    142 
    143         if n.startswith('.nfs'):
    144             # skip NFS rename files
    145             continue
    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     global _path_created
    185 
    186     if verbose >= 1:
    187         log.info("removing '%s' (and everything under it)", directory)
    188     if dry_run:
    189         return
    190     cmdtuples = []
    191     _build_cmdtuple(directory, cmdtuples)
    192     for cmd in cmdtuples:
    193         try:
    194             cmd[0](cmd[1])
    195             # remove dir from cache if it's already there
    196             abspath = os.path.abspath(cmd[1])
    197             if abspath in _path_created:
    198                 del _path_created[abspath]
    199         except OSError as exc:
    200             log.warn("error removing %s: %s", directory, exc)
    201 
    202 def ensure_relative(path):
    203     """Take the full path 'path', and make it a relative path.
    204 
    205     This is useful to make 'path' the second argument to os.path.join().
    206     """
    207     drive, path = os.path.splitdrive(path)
    208     if path[0:1] == os.sep:
    209         path = drive + path[1:]
    210     return path
    211