0001 """distutils.dir_util 0002 0003 Utility functions for manipulating directories and directory trees.""" 0004 0005 # This module should be kept compatible with Python 1.5.2. 0006 0007 __revision__ = "$Id: dir_util.py,v 1.14 2004/07/18 06:14:42 tim_one Exp $" 0008 0009 import os, sys 0010 from types import * 0011 from distutils.errors import DistutilsFileError, DistutilsInternalError 0012 from distutils import log 0013 0014 # cache for by mkpath() -- in addition to cheapening redundant calls, 0015 # eliminates redundant "creating /foo/bar/baz" messages in dry-run mode 0016 _path_created = {} 0017 0018 # I don't use os.makedirs because a) it's new to Python 1.5.2, and 0019 # b) it blows up if the directory already exists (I want to silently 0020 # succeed in that case). 0021 def mkpath (name, mode=0777, verbose=0, dry_run=0): 0022 """Create a directory and any missing ancestor directories. If the 0023 directory already exists (or if 'name' is the empty string, which 0024 means the current directory, which of course exists), then do 0025 nothing. Raise DistutilsFileError if unable to create some 0026 directory along the way (eg. some sub-path exists, but is a file 0027 rather than a directory). If 'verbose' is true, print a one-line 0028 summary of each mkdir to stdout. Return the list of directories 0029 actually created.""" 0030 0031 global _path_created 0032 0033 # Detect a common bug -- name is None 0034 if type(name) is not StringType: 0035 raise DistutilsInternalError, \ 0036 "mkpath: 'name' must be a string (got %r)" % (name,) 0037 0038 # XXX what's the better way to handle verbosity? print as we create 0039 # each directory in the path (the current behaviour), or only announce 0040 # the creation of the whole path? (quite easy to do the latter since 0041 # we're not using a recursive algorithm) 0042 0043 name = os.path.normpath(name) 0044 created_dirs = [] 0045 if os.path.isdir(name) or name == '': 0046 return created_dirs 0047 if _path_created.get(os.path.abspath(name)): 0048 return created_dirs 0049 0050 (head, tail) = os.path.split(name) 0051 tails = [tail] # stack of lone dirs to create 0052 0053 while head and tail and not os.path.isdir(head): 0054 #print "splitting '%s': " % head, 0055 (head, tail) = os.path.split(head) 0056 #print "to ('%s','%s')" % (head, tail) 0057 tails.insert(0, tail) # push next higher dir onto stack 0058 0059 #print "stack of tails:", tails 0060 0061 # now 'head' contains the deepest directory that already exists 0062 # (that is, the child of 'head' in 'name' is the highest directory 0063 # that does *not* exist) 0064 for d in tails: 0065 #print "head = %s, d = %s: " % (head, d), 0066 head = os.path.join(head, d) 0067 abs_head = os.path.abspath(head) 0068 0069 if _path_created.get(abs_head): 0070 continue 0071 0072 log.info("creating %s", head) 0073 0074 if not dry_run: 0075 try: 0076 os.mkdir(head) 0077 created_dirs.append(head) 0078 except OSError, exc: 0079 raise DistutilsFileError, \ 0080 "could not create '%s': %s" % (head, exc[-1]) 0081 0082 _path_created[abs_head] = 1 0083 return created_dirs 0084 0085 # mkpath () 0086 0087 0088 def create_tree (base_dir, files, mode=0777, verbose=0, dry_run=0): 0089 0090 """Create all the empty directories under 'base_dir' needed to 0091 put 'files' there. 'base_dir' is just the a name of a directory 0092 which doesn't necessarily exist yet; 'files' is a list of filenames 0093 to be interpreted relative to 'base_dir'. 'base_dir' + the 0094 directory portion of every file in 'files' will be created if it 0095 doesn't already exist. 'mode', 'verbose' and 'dry_run' flags are as 0096 for 'mkpath()'.""" 0097 0098 # First get the list of directories to create 0099 need_dir = {} 0100 for file in files: 0101 need_dir[os.path.join(base_dir, os.path.dirname(file))] = 1 0102 need_dirs = need_dir.keys() 0103 need_dirs.sort() 0104 0105 # Now create them 0106 for dir in need_dirs: 0107 mkpath(dir, mode, dry_run=dry_run) 0108 0109 # create_tree () 0110 0111 0112 def copy_tree (src, dst, 0113 preserve_mode=1, 0114 preserve_times=1, 0115 preserve_symlinks=0, 0116 update=0, 0117 verbose=0, 0118 dry_run=0): 0119 0120 """Copy an entire directory tree 'src' to a new location 'dst'. Both 0121 'src' and 'dst' must be directory names. If 'src' is not a 0122 directory, raise DistutilsFileError. If 'dst' does not exist, it is 0123 created with 'mkpath()'. The end result of the copy is that every 0124 file in 'src' is copied to 'dst', and directories under 'src' are 0125 recursively copied to 'dst'. Return the list of files that were 0126 copied or might have been copied, using their output name. The 0127 return value is unaffected by 'update' or 'dry_run': it is simply 0128 the list of all files under 'src', with the names changed to be 0129 under 'dst'. 0130 0131 'preserve_mode' and 'preserve_times' are the same as for 0132 'copy_file'; note that they only apply to regular files, not to 0133 directories. If 'preserve_symlinks' is true, symlinks will be 0134 copied as symlinks (on platforms that support them!); otherwise 0135 (the default), the destination of the symlink will be copied. 0136 'update' and 'verbose' are the same as for 'copy_file'.""" 0137 0138 from distutils.file_util import copy_file 0139 0140 if not dry_run and not os.path.isdir(src): 0141 raise DistutilsFileError, \ 0142 "cannot copy tree '%s': not a directory" % src 0143 try: 0144 names = os.listdir(src) 0145 except os.error, (errno, errstr): 0146 if dry_run: 0147 names = [] 0148 else: 0149 raise DistutilsFileError, \ 0150 "error listing files in '%s': %s" % (src, errstr) 0151 0152 if not dry_run: 0153 mkpath(dst) 0154 0155 outputs = [] 0156 0157 for n in names: 0158 src_name = os.path.join(src, n) 0159 dst_name = os.path.join(dst, n) 0160 0161 if preserve_symlinks and os.path.islink(src_name): 0162 link_dest = os.readlink(src_name) 0163 log.info("linking %s -> %s", dst_name, link_dest) 0164 if not dry_run: 0165 os.symlink(link_dest, dst_name) 0166 outputs.append(dst_name) 0167 0168 elif os.path.isdir(src_name): 0169 outputs.extend( 0170 copy_tree(src_name, dst_name, preserve_mode, 0171 preserve_times, preserve_symlinks, update, 0172 dry_run=dry_run)) 0173 else: 0174 copy_file(src_name, dst_name, preserve_mode, 0175 preserve_times, update, dry_run=dry_run) 0176 outputs.append(dst_name) 0177 0178 return outputs 0179 0180 # copy_tree () 0181 0182 # Helper for remove_tree() 0183 def _build_cmdtuple(path, cmdtuples): 0184 for f in os.listdir(path): 0185 real_f = os.path.join(path,f) 0186 if os.path.isdir(real_f) and not os.path.islink(real_f): 0187 _build_cmdtuple(real_f, cmdtuples) 0188 else: 0189 cmdtuples.append((os.remove, real_f)) 0190 cmdtuples.append((os.rmdir, path)) 0191 0192 0193 def remove_tree (directory, verbose=0, dry_run=0): 0194 """Recursively remove an entire directory tree. Any errors are ignored 0195 (apart from being reported to stdout if 'verbose' is true). 0196 """ 0197 from distutils.util import grok_environment_error 0198 global _path_created 0199 0200 log.info("removing '%s' (and everything under it)", directory) 0201 if dry_run: 0202 return 0203 cmdtuples = [] 0204 _build_cmdtuple(directory, cmdtuples) 0205 for cmd in cmdtuples: 0206 try: 0207 apply(cmd[0], (cmd[1],)) 0208 # remove dir from cache if it's already there 0209 abspath = os.path.abspath(cmd[1]) 0210 if _path_created.has_key(abspath): 0211 del _path_created[abspath] 0212 except (IOError, OSError), exc: 0213 log.warn(grok_environment_error( 0214 exc, "error removing %s: " % directory)) 0215 0216 0217 def ensure_relative (path): 0218 """Take the full path 'path', and make it a relative path so 0219 it can be the second argument to os.path.join(). 0220 """ 0221 drive, path = os.path.splitdrive(path) 0222 if sys.platform == 'mac': 0223 return os.sep + path 0224 else: 0225 if path[0:1] == os.sep: 0226 path = drive + path[1:] 0227 return path 0228
Generated by PyXR 0.9.4