(root)/
Python-3.11.7/
Lib/
distutils/
dir_util.py
       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