3 Utility functions for manipulating directories and directory trees."""
5 # This module should be kept compatible with Python 2.1.
11 from distutils
.errors
import DistutilsFileError
, DistutilsInternalError
12 from distutils
import log
14 # cache for by mkpath() -- in addition to cheapening redundant calls,
15 # eliminates redundant "creating /foo/bar/baz" messages in dry-run mode
18 # I don't use os.makedirs because a) it's new to Python 1.5.2, and
19 # b) it blows up if the directory already exists (I want to silently
20 # succeed in that case).
21 def mkpath (name
, mode
=0777, verbose
=0, dry_run
=0):
22 """Create a directory and any missing ancestor directories. If the
23 directory already exists (or if 'name' is the empty string, which
24 means the current directory, which of course exists), then do
25 nothing. Raise DistutilsFileError if unable to create some
26 directory along the way (eg. some sub-path exists, but is a file
27 rather than a directory). If 'verbose' is true, print a one-line
28 summary of each mkdir to stdout. Return the list of directories
33 # Detect a common bug -- name is None
34 if not isinstance(name
, StringTypes
):
35 raise DistutilsInternalError
, \
36 "mkpath: 'name' must be a string (got %r)" % (name
,)
38 # XXX what's the better way to handle verbosity? print as we create
39 # each directory in the path (the current behaviour), or only announce
40 # the creation of the whole path? (quite easy to do the latter since
41 # we're not using a recursive algorithm)
43 name
= os
.path
.normpath(name
)
45 if os
.path
.isdir(name
) or name
== '':
47 if _path_created
.get(os
.path
.abspath(name
)):
50 (head
, tail
) = os
.path
.split(name
)
51 tails
= [tail
] # stack of lone dirs to create
53 while head
and tail
and not os
.path
.isdir(head
):
54 #print "splitting '%s': " % head,
55 (head
, tail
) = os
.path
.split(head
)
56 #print "to ('%s','%s')" % (head, tail)
57 tails
.insert(0, tail
) # push next higher dir onto stack
59 #print "stack of tails:", tails
61 # now 'head' contains the deepest directory that already exists
62 # (that is, the child of 'head' in 'name' is the highest directory
63 # that does *not* exist)
65 #print "head = %s, d = %s: " % (head, d),
66 head
= os
.path
.join(head
, d
)
67 abs_head
= os
.path
.abspath(head
)
69 if _path_created
.get(abs_head
):
72 log
.info("creating %s", head
)
77 created_dirs
.append(head
)
79 raise DistutilsFileError
, \
80 "could not create '%s': %s" % (head
, exc
[-1])
82 _path_created
[abs_head
] = 1
88 def create_tree (base_dir
, files
, mode
=0777, verbose
=0, dry_run
=0):
90 """Create all the empty directories under 'base_dir' needed to
91 put 'files' there. 'base_dir' is just the a name of a directory
92 which doesn't necessarily exist yet; 'files' is a list of filenames
93 to be interpreted relative to 'base_dir'. 'base_dir' + the
94 directory portion of every file in 'files' will be created if it
95 doesn't already exist. 'mode', 'verbose' and 'dry_run' flags are as
98 # First get the list of directories to create
101 need_dir
[os
.path
.join(base_dir
, os
.path
.dirname(file))] = 1
102 need_dirs
= need_dir
.keys()
106 for dir in need_dirs
:
107 mkpath(dir, mode
, dry_run
=dry_run
)
112 def copy_tree (src
, dst
,
120 """Copy an entire directory tree 'src' to a new location 'dst'. Both
121 'src' and 'dst' must be directory names. If 'src' is not a
122 directory, raise DistutilsFileError. If 'dst' does not exist, it is
123 created with 'mkpath()'. The end result of the copy is that every
124 file in 'src' is copied to 'dst', and directories under 'src' are
125 recursively copied to 'dst'. Return the list of files that were
126 copied or might have been copied, using their output name. The
127 return value is unaffected by 'update' or 'dry_run': it is simply
128 the list of all files under 'src', with the names changed to be
131 'preserve_mode' and 'preserve_times' are the same as for
132 'copy_file'; note that they only apply to regular files, not to
133 directories. If 'preserve_symlinks' is true, symlinks will be
134 copied as symlinks (on platforms that support them!); otherwise
135 (the default), the destination of the symlink will be copied.
136 'update' and 'verbose' are the same as for 'copy_file'."""
138 from distutils
.file_util
import copy_file
140 if not dry_run
and not os
.path
.isdir(src
):
141 raise DistutilsFileError
, \
142 "cannot copy tree '%s': not a directory" % src
144 names
= os
.listdir(src
)
145 except os
.error
, (errno
, errstr
):
149 raise DistutilsFileError
, \
150 "error listing files in '%s': %s" % (src
, errstr
)
158 src_name
= os
.path
.join(src
, n
)
159 dst_name
= os
.path
.join(dst
, n
)
161 if preserve_symlinks
and os
.path
.islink(src_name
):
162 link_dest
= os
.readlink(src_name
)
163 log
.info("linking %s -> %s", dst_name
, link_dest
)
165 os
.symlink(link_dest
, dst_name
)
166 outputs
.append(dst_name
)
168 elif os
.path
.isdir(src_name
):
170 copy_tree(src_name
, dst_name
, preserve_mode
,
171 preserve_times
, preserve_symlinks
, update
,
174 copy_file(src_name
, dst_name
, preserve_mode
,
175 preserve_times
, update
, dry_run
=dry_run
)
176 outputs
.append(dst_name
)
182 # Helper for remove_tree()
183 def _build_cmdtuple(path
, cmdtuples
):
184 for f
in os
.listdir(path
):
185 real_f
= os
.path
.join(path
,f
)
186 if os
.path
.isdir(real_f
) and not os
.path
.islink(real_f
):
187 _build_cmdtuple(real_f
, cmdtuples
)
189 cmdtuples
.append((os
.remove
, real_f
))
190 cmdtuples
.append((os
.rmdir
, path
))
193 def remove_tree (directory
, verbose
=0, dry_run
=0):
194 """Recursively remove an entire directory tree. Any errors are ignored
195 (apart from being reported to stdout if 'verbose' is true).
197 from distutils
.util
import grok_environment_error
200 log
.info("removing '%s' (and everything under it)", directory
)
204 _build_cmdtuple(directory
, cmdtuples
)
205 for cmd
in cmdtuples
:
207 apply(cmd
[0], (cmd
[1],))
208 # remove dir from cache if it's already there
209 abspath
= os
.path
.abspath(cmd
[1])
210 if _path_created
.has_key(abspath
):
211 del _path_created
[abspath
]
212 except (IOError, OSError), exc
:
213 log
.warn(grok_environment_error(
214 exc
, "error removing %s: " % directory
))
217 def ensure_relative (path
):
218 """Take the full path 'path', and make it a relative path so
219 it can be the second argument to os.path.join().
221 drive
, path
= os
.path
.splitdrive(path
)
222 if sys
.platform
== 'mac':
225 if path
[0:1] == os
.sep
:
226 path
= drive
+ path
[1:]