python (3.11.7)

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