3 Utility functions for manipulating directories and directory trees."""
9 from distutils
.errors
import DistutilsFileError
, DistutilsInternalError
10 from distutils
import log
12 # cache for by mkpath() -- in addition to cheapening redundant calls,
13 # eliminates redundant "creating /foo/bar/baz" messages in dry-run mode
16 # I don't use os.makedirs because a) it's new to Python 1.5.2, and
17 # b) it blows up if the directory already exists (I want to silently
18 # succeed in that case).
19 def mkpath (name
, mode
=0777, verbose
=1, dry_run
=0):
20 """Create a directory and any missing ancestor directories. If the
21 directory already exists (or if 'name' is the empty string, which
22 means the current directory, which of course exists), then do
23 nothing. Raise DistutilsFileError if unable to create some
24 directory along the way (eg. some sub-path exists, but is a file
25 rather than a directory). If 'verbose' is true, print a one-line
26 summary of each mkdir to stdout. Return the list of directories
31 # Detect a common bug -- name is None
32 if not isinstance(name
, StringTypes
):
33 raise DistutilsInternalError
, \
34 "mkpath: 'name' must be a string (got %r)" % (name
,)
36 # XXX what's the better way to handle verbosity? print as we create
37 # each directory in the path (the current behaviour), or only announce
38 # the creation of the whole path? (quite easy to do the latter since
39 # we're not using a recursive algorithm)
41 name
= os
.path
.normpath(name
)
43 if os
.path
.isdir(name
) or name
== '':
45 if _path_created
.get(os
.path
.abspath(name
)):
48 (head
, tail
) = os
.path
.split(name
)
49 tails
= [tail
] # stack of lone dirs to create
51 while head
and tail
and not os
.path
.isdir(head
):
52 (head
, tail
) = os
.path
.split(head
)
53 tails
.insert(0, tail
) # push next higher dir onto stack
55 # now 'head' contains the deepest directory that already exists
56 # (that is, the child of 'head' in 'name' is the highest directory
57 # that does *not* exist)
59 #print "head = %s, d = %s: " % (head, d),
60 head
= os
.path
.join(head
, d
)
61 abs_head
= os
.path
.abspath(head
)
63 if _path_created
.get(abs_head
):
67 log
.info("creating %s", head
)
72 created_dirs
.append(head
)
74 raise DistutilsFileError
, \
75 "could not create '%s': %s" % (head
, exc
[-1])
77 _path_created
[abs_head
] = 1
83 def create_tree (base_dir
, files
, mode
=0777, verbose
=1, dry_run
=0):
85 """Create all the empty directories under 'base_dir' needed to
86 put 'files' there. 'base_dir' is just the a name of a directory
87 which doesn't necessarily exist yet; 'files' is a list of filenames
88 to be interpreted relative to 'base_dir'. 'base_dir' + the
89 directory portion of every file in 'files' will be created if it
90 doesn't already exist. 'mode', 'verbose' and 'dry_run' flags are as
93 # First get the list of directories to create
96 need_dir
[os
.path
.join(base_dir
, os
.path
.dirname(file))] = 1
97 need_dirs
= need_dir
.keys()
101 for dir in need_dirs
:
102 mkpath(dir, mode
, verbose
=verbose
, dry_run
=dry_run
)
107 def copy_tree (src
, dst
,
115 """Copy an entire directory tree 'src' to a new location 'dst'. Both
116 'src' and 'dst' must be directory names. If 'src' is not a
117 directory, raise DistutilsFileError. If 'dst' does not exist, it is
118 created with 'mkpath()'. The end result of the copy is that every
119 file in 'src' is copied to 'dst', and directories under 'src' are
120 recursively copied to 'dst'. Return the list of files that were
121 copied or might have been copied, using their output name. The
122 return value is unaffected by 'update' or 'dry_run': it is simply
123 the list of all files under 'src', with the names changed to be
126 'preserve_mode' and 'preserve_times' are the same as for
127 'copy_file'; note that they only apply to regular files, not to
128 directories. If 'preserve_symlinks' is true, symlinks will be
129 copied as symlinks (on platforms that support them!); otherwise
130 (the default), the destination of the symlink will be copied.
131 'update' and 'verbose' are the same as for 'copy_file'."""
133 from distutils
.file_util
import copy_file
135 if not dry_run
and not os
.path
.isdir(src
):
136 raise DistutilsFileError
, \
137 "cannot copy tree '%s': not a directory" % src
139 names
= os
.listdir(src
)
140 except os
.error
, (errno
, errstr
):
144 raise DistutilsFileError
, \
145 "error listing files in '%s': %s" % (src
, errstr
)
148 mkpath(dst
, verbose
=verbose
)
153 src_name
= os
.path
.join(src
, n
)
154 dst_name
= os
.path
.join(dst
, n
)
156 if preserve_symlinks
and os
.path
.islink(src_name
):
157 link_dest
= os
.readlink(src_name
)
159 log
.info("linking %s -> %s", dst_name
, link_dest
)
161 os
.symlink(link_dest
, dst_name
)
162 outputs
.append(dst_name
)
164 elif os
.path
.isdir(src_name
):
166 copy_tree(src_name
, dst_name
, preserve_mode
,
167 preserve_times
, preserve_symlinks
, update
,
168 verbose
=verbose
, dry_run
=dry_run
))
170 copy_file(src_name
, dst_name
, preserve_mode
,
171 preserve_times
, update
, verbose
=verbose
,
173 outputs
.append(dst_name
)
179 # Helper for remove_tree()
180 def _build_cmdtuple(path
, cmdtuples
):
181 for f
in os
.listdir(path
):
182 real_f
= os
.path
.join(path
,f
)
183 if os
.path
.isdir(real_f
) and not os
.path
.islink(real_f
):
184 _build_cmdtuple(real_f
, cmdtuples
)
186 cmdtuples
.append((os
.remove
, real_f
))
187 cmdtuples
.append((os
.rmdir
, path
))
190 def remove_tree (directory
, verbose
=1, dry_run
=0):
191 """Recursively remove an entire directory tree. Any errors are ignored
192 (apart from being reported to stdout if 'verbose' is true).
194 from distutils
.util
import grok_environment_error
198 log
.info("removing '%s' (and everything under it)", directory
)
202 _build_cmdtuple(directory
, cmdtuples
)
203 for cmd
in cmdtuples
:
205 apply(cmd
[0], (cmd
[1],))
206 # remove dir from cache if it's already there
207 abspath
= os
.path
.abspath(cmd
[1])
208 if abspath
in _path_created
:
209 del _path_created
[abspath
]
210 except (IOError, OSError), exc
:
211 log
.warn(grok_environment_error(
212 exc
, "error removing %s: " % directory
))
215 def ensure_relative (path
):
216 """Take the full path 'path', and make it a relative path so
217 it can be the second argument to os.path.join().
219 drive
, path
= os
.path
.splitdrive(path
)
220 if sys
.platform
== 'mac':
223 if path
[0:1] == os
.sep
:
224 path
= drive
+ path
[1:]