Issue #7701: Fix crash in binascii.b2a_uu() in debug mode when given a
[python.git] / Lib / shutil.py
bloba689256f15dc0dcc10305d733f980848c7e76ee7
1 """Utility functions for copying files and directory trees.
3 XXX The functions here don't copy the resource fork or other metadata on Mac.
5 """
7 import os
8 import sys
9 import stat
10 from os.path import abspath
11 import fnmatch
13 __all__ = ["copyfileobj","copyfile","copymode","copystat","copy","copy2",
14 "copytree","move","rmtree","Error", "SpecialFileError"]
16 class Error(EnvironmentError):
17 pass
19 class SpecialFileError(EnvironmentError):
20 """Raised when trying to do a kind of operation (e.g. copying) which is
21 not supported on a special file (e.g. a named pipe)"""
23 try:
24 WindowsError
25 except NameError:
26 WindowsError = None
28 def copyfileobj(fsrc, fdst, length=16*1024):
29 """copy data from file-like object fsrc to file-like object fdst"""
30 while 1:
31 buf = fsrc.read(length)
32 if not buf:
33 break
34 fdst.write(buf)
36 def _samefile(src, dst):
37 # Macintosh, Unix.
38 if hasattr(os.path,'samefile'):
39 try:
40 return os.path.samefile(src, dst)
41 except OSError:
42 return False
44 # All other platforms: check for same pathname.
45 return (os.path.normcase(os.path.abspath(src)) ==
46 os.path.normcase(os.path.abspath(dst)))
48 def copyfile(src, dst):
49 """Copy data from src to dst"""
50 if _samefile(src, dst):
51 raise Error, "`%s` and `%s` are the same file" % (src, dst)
53 fsrc = None
54 fdst = None
55 for fn in [src, dst]:
56 try:
57 st = os.stat(fn)
58 except OSError:
59 # File most likely does not exist
60 pass
61 else:
62 # XXX What about other special files? (sockets, devices...)
63 if stat.S_ISFIFO(st.st_mode):
64 raise SpecialFileError("`%s` is a named pipe" % fn)
65 try:
66 fsrc = open(src, 'rb')
67 fdst = open(dst, 'wb')
68 copyfileobj(fsrc, fdst)
69 finally:
70 if fdst:
71 fdst.close()
72 if fsrc:
73 fsrc.close()
75 def copymode(src, dst):
76 """Copy mode bits from src to dst"""
77 if hasattr(os, 'chmod'):
78 st = os.stat(src)
79 mode = stat.S_IMODE(st.st_mode)
80 os.chmod(dst, mode)
82 def copystat(src, dst):
83 """Copy all stat info (mode bits, atime, mtime, flags) from src to dst"""
84 st = os.stat(src)
85 mode = stat.S_IMODE(st.st_mode)
86 if hasattr(os, 'utime'):
87 os.utime(dst, (st.st_atime, st.st_mtime))
88 if hasattr(os, 'chmod'):
89 os.chmod(dst, mode)
90 if hasattr(os, 'chflags') and hasattr(st, 'st_flags'):
91 os.chflags(dst, st.st_flags)
94 def copy(src, dst):
95 """Copy data and mode bits ("cp src dst").
97 The destination may be a directory.
99 """
100 if os.path.isdir(dst):
101 dst = os.path.join(dst, os.path.basename(src))
102 copyfile(src, dst)
103 copymode(src, dst)
105 def copy2(src, dst):
106 """Copy data and all stat info ("cp -p src dst").
108 The destination may be a directory.
111 if os.path.isdir(dst):
112 dst = os.path.join(dst, os.path.basename(src))
113 copyfile(src, dst)
114 copystat(src, dst)
116 def ignore_patterns(*patterns):
117 """Function that can be used as copytree() ignore parameter.
119 Patterns is a sequence of glob-style patterns
120 that are used to exclude files"""
121 def _ignore_patterns(path, names):
122 ignored_names = []
123 for pattern in patterns:
124 ignored_names.extend(fnmatch.filter(names, pattern))
125 return set(ignored_names)
126 return _ignore_patterns
128 def copytree(src, dst, symlinks=False, ignore=None):
129 """Recursively copy a directory tree using copy2().
131 The destination directory must not already exist.
132 If exception(s) occur, an Error is raised with a list of reasons.
134 If the optional symlinks flag is true, symbolic links in the
135 source tree result in symbolic links in the destination tree; if
136 it is false, the contents of the files pointed to by symbolic
137 links are copied.
139 The optional ignore argument is a callable. If given, it
140 is called with the `src` parameter, which is the directory
141 being visited by copytree(), and `names` which is the list of
142 `src` contents, as returned by os.listdir():
144 callable(src, names) -> ignored_names
146 Since copytree() is called recursively, the callable will be
147 called once for each directory that is copied. It returns a
148 list of names relative to the `src` directory that should
149 not be copied.
151 XXX Consider this example code rather than the ultimate tool.
154 names = os.listdir(src)
155 if ignore is not None:
156 ignored_names = ignore(src, names)
157 else:
158 ignored_names = set()
160 os.makedirs(dst)
161 errors = []
162 for name in names:
163 if name in ignored_names:
164 continue
165 srcname = os.path.join(src, name)
166 dstname = os.path.join(dst, name)
167 try:
168 if symlinks and os.path.islink(srcname):
169 linkto = os.readlink(srcname)
170 os.symlink(linkto, dstname)
171 elif os.path.isdir(srcname):
172 copytree(srcname, dstname, symlinks, ignore)
173 else:
174 # Will raise a SpecialFileError for unsupported file types
175 copy2(srcname, dstname)
176 # catch the Error from the recursive copytree so that we can
177 # continue with other files
178 except Error, err:
179 errors.extend(err.args[0])
180 except EnvironmentError, why:
181 errors.append((srcname, dstname, str(why)))
182 try:
183 copystat(src, dst)
184 except OSError, why:
185 if WindowsError is not None and isinstance(why, WindowsError):
186 # Copying file access times may fail on Windows
187 pass
188 else:
189 errors.extend((src, dst, str(why)))
190 if errors:
191 raise Error, errors
193 def rmtree(path, ignore_errors=False, onerror=None):
194 """Recursively delete a directory tree.
196 If ignore_errors is set, errors are ignored; otherwise, if onerror
197 is set, it is called to handle the error with arguments (func,
198 path, exc_info) where func is os.listdir, os.remove, or os.rmdir;
199 path is the argument to that function that caused it to fail; and
200 exc_info is a tuple returned by sys.exc_info(). If ignore_errors
201 is false and onerror is None, an exception is raised.
204 if ignore_errors:
205 def onerror(*args):
206 pass
207 elif onerror is None:
208 def onerror(*args):
209 raise
210 try:
211 if os.path.islink(path):
212 # symlinks to directories are forbidden, see bug #1669
213 raise OSError("Cannot call rmtree on a symbolic link")
214 except OSError:
215 onerror(os.path.islink, path, sys.exc_info())
216 # can't continue even if onerror hook returns
217 return
218 names = []
219 try:
220 names = os.listdir(path)
221 except os.error, err:
222 onerror(os.listdir, path, sys.exc_info())
223 for name in names:
224 fullname = os.path.join(path, name)
225 try:
226 mode = os.lstat(fullname).st_mode
227 except os.error:
228 mode = 0
229 if stat.S_ISDIR(mode):
230 rmtree(fullname, ignore_errors, onerror)
231 else:
232 try:
233 os.remove(fullname)
234 except os.error, err:
235 onerror(os.remove, fullname, sys.exc_info())
236 try:
237 os.rmdir(path)
238 except os.error:
239 onerror(os.rmdir, path, sys.exc_info())
242 def _basename(path):
243 # A basename() variant which first strips the trailing slash, if present.
244 # Thus we always get the last component of the path, even for directories.
245 return os.path.basename(path.rstrip(os.path.sep))
247 def move(src, dst):
248 """Recursively move a file or directory to another location. This is
249 similar to the Unix "mv" command.
251 If the destination is a directory or a symlink to a directory, the source
252 is moved inside the directory. The destination path must not already
253 exist.
255 If the destination already exists but is not a directory, it may be
256 overwritten depending on os.rename() semantics.
258 If the destination is on our current filesystem, then rename() is used.
259 Otherwise, src is copied to the destination and then removed.
260 A lot more could be done here... A look at a mv.c shows a lot of
261 the issues this implementation glosses over.
264 real_dst = dst
265 if os.path.isdir(dst):
266 real_dst = os.path.join(dst, _basename(src))
267 if os.path.exists(real_dst):
268 raise Error, "Destination path '%s' already exists" % real_dst
269 try:
270 os.rename(src, real_dst)
271 except OSError:
272 if os.path.isdir(src):
273 if _destinsrc(src, dst):
274 raise Error, "Cannot move a directory '%s' into itself '%s'." % (src, dst)
275 copytree(src, real_dst, symlinks=True)
276 rmtree(src)
277 else:
278 copy2(src, real_dst)
279 os.unlink(src)
281 def _destinsrc(src, dst):
282 src = abspath(src)
283 dst = abspath(dst)
284 if not src.endswith(os.path.sep):
285 src += os.path.sep
286 if not dst.endswith(os.path.sep):
287 dst += os.path.sep
288 return dst.startswith(src)