Issue #3002: `shutil.copyfile()` and `shutil.copytree()` now raise an
[python.git] / Lib / shutil.py
blobc46ec47fd3b32371efe598ec6a2636c9e3ab861a
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 # XXX What about other special files? (sockets, devices...)
62 if stat.S_ISFIFO(st.st_mode):
63 raise SpecialFileError("`%s` is a named pipe" % fn)
64 try:
65 fsrc = open(src, 'rb')
66 fdst = open(dst, 'wb')
67 copyfileobj(fsrc, fdst)
68 finally:
69 if fdst:
70 fdst.close()
71 if fsrc:
72 fsrc.close()
74 def copymode(src, dst):
75 """Copy mode bits from src to dst"""
76 if hasattr(os, 'chmod'):
77 st = os.stat(src)
78 mode = stat.S_IMODE(st.st_mode)
79 os.chmod(dst, mode)
81 def copystat(src, dst):
82 """Copy all stat info (mode bits, atime, mtime, flags) from src to dst"""
83 st = os.stat(src)
84 mode = stat.S_IMODE(st.st_mode)
85 if hasattr(os, 'utime'):
86 os.utime(dst, (st.st_atime, st.st_mtime))
87 if hasattr(os, 'chmod'):
88 os.chmod(dst, mode)
89 if hasattr(os, 'chflags') and hasattr(st, 'st_flags'):
90 os.chflags(dst, st.st_flags)
93 def copy(src, dst):
94 """Copy data and mode bits ("cp src dst").
96 The destination may be a directory.
98 """
99 if os.path.isdir(dst):
100 dst = os.path.join(dst, os.path.basename(src))
101 copyfile(src, dst)
102 copymode(src, dst)
104 def copy2(src, dst):
105 """Copy data and all stat info ("cp -p src dst").
107 The destination may be a directory.
110 if os.path.isdir(dst):
111 dst = os.path.join(dst, os.path.basename(src))
112 copyfile(src, dst)
113 copystat(src, dst)
115 def ignore_patterns(*patterns):
116 """Function that can be used as copytree() ignore parameter.
118 Patterns is a sequence of glob-style patterns
119 that are used to exclude files"""
120 def _ignore_patterns(path, names):
121 ignored_names = []
122 for pattern in patterns:
123 ignored_names.extend(fnmatch.filter(names, pattern))
124 return set(ignored_names)
125 return _ignore_patterns
127 def copytree(src, dst, symlinks=False, ignore=None):
128 """Recursively copy a directory tree using copy2().
130 The destination directory must not already exist.
131 If exception(s) occur, an Error is raised with a list of reasons.
133 If the optional symlinks flag is true, symbolic links in the
134 source tree result in symbolic links in the destination tree; if
135 it is false, the contents of the files pointed to by symbolic
136 links are copied.
138 The optional ignore argument is a callable. If given, it
139 is called with the `src` parameter, which is the directory
140 being visited by copytree(), and `names` which is the list of
141 `src` contents, as returned by os.listdir():
143 callable(src, names) -> ignored_names
145 Since copytree() is called recursively, the callable will be
146 called once for each directory that is copied. It returns a
147 list of names relative to the `src` directory that should
148 not be copied.
150 XXX Consider this example code rather than the ultimate tool.
153 names = os.listdir(src)
154 if ignore is not None:
155 ignored_names = ignore(src, names)
156 else:
157 ignored_names = set()
159 os.makedirs(dst)
160 errors = []
161 for name in names:
162 if name in ignored_names:
163 continue
164 srcname = os.path.join(src, name)
165 dstname = os.path.join(dst, name)
166 try:
167 if symlinks and os.path.islink(srcname):
168 linkto = os.readlink(srcname)
169 os.symlink(linkto, dstname)
170 elif os.path.isdir(srcname):
171 copytree(srcname, dstname, symlinks, ignore)
172 else:
173 # Will raise a SpecialFileError for unsupported file types
174 copy2(srcname, dstname)
175 # catch the Error from the recursive copytree so that we can
176 # continue with other files
177 except Error, err:
178 errors.extend(err.args[0])
179 except EnvironmentError, why:
180 errors.append((srcname, dstname, str(why)))
181 try:
182 copystat(src, dst)
183 except OSError, why:
184 if WindowsError is not None and isinstance(why, WindowsError):
185 # Copying file access times may fail on Windows
186 pass
187 else:
188 errors.extend((src, dst, str(why)))
189 if errors:
190 raise Error, errors
192 def rmtree(path, ignore_errors=False, onerror=None):
193 """Recursively delete a directory tree.
195 If ignore_errors is set, errors are ignored; otherwise, if onerror
196 is set, it is called to handle the error with arguments (func,
197 path, exc_info) where func is os.listdir, os.remove, or os.rmdir;
198 path is the argument to that function that caused it to fail; and
199 exc_info is a tuple returned by sys.exc_info(). If ignore_errors
200 is false and onerror is None, an exception is raised.
203 if ignore_errors:
204 def onerror(*args):
205 pass
206 elif onerror is None:
207 def onerror(*args):
208 raise
209 try:
210 if os.path.islink(path):
211 # symlinks to directories are forbidden, see bug #1669
212 raise OSError("Cannot call rmtree on a symbolic link")
213 except OSError:
214 onerror(os.path.islink, path, sys.exc_info())
215 # can't continue even if onerror hook returns
216 return
217 names = []
218 try:
219 names = os.listdir(path)
220 except os.error, err:
221 onerror(os.listdir, path, sys.exc_info())
222 for name in names:
223 fullname = os.path.join(path, name)
224 try:
225 mode = os.lstat(fullname).st_mode
226 except os.error:
227 mode = 0
228 if stat.S_ISDIR(mode):
229 rmtree(fullname, ignore_errors, onerror)
230 else:
231 try:
232 os.remove(fullname)
233 except os.error, err:
234 onerror(os.remove, fullname, sys.exc_info())
235 try:
236 os.rmdir(path)
237 except os.error:
238 onerror(os.rmdir, path, sys.exc_info())
241 def _basename(path):
242 # A basename() variant which first strips the trailing slash, if present.
243 # Thus we always get the last component of the path, even for directories.
244 return os.path.basename(path.rstrip(os.path.sep))
246 def move(src, dst):
247 """Recursively move a file or directory to another location. This is
248 similar to the Unix "mv" command.
250 If the destination is a directory or a symlink to a directory, the source
251 is moved inside the directory. The destination path must not already
252 exist.
254 If the destination already exists but is not a directory, it may be
255 overwritten depending on os.rename() semantics.
257 If the destination is on our current filesystem, then rename() is used.
258 Otherwise, src is copied to the destination and then removed.
259 A lot more could be done here... A look at a mv.c shows a lot of
260 the issues this implementation glosses over.
263 real_dst = dst
264 if os.path.isdir(dst):
265 real_dst = os.path.join(dst, _basename(src))
266 if os.path.exists(real_dst):
267 raise Error, "Destination path '%s' already exists" % real_dst
268 try:
269 os.rename(src, real_dst)
270 except OSError:
271 if os.path.isdir(src):
272 if _destinsrc(src, dst):
273 raise Error, "Cannot move a directory '%s' into itself '%s'." % (src, dst)
274 copytree(src, real_dst, symlinks=True)
275 rmtree(src)
276 else:
277 copy2(src, real_dst)
278 os.unlink(src)
280 def _destinsrc(src, dst):
281 src = abspath(src)
282 dst = abspath(dst)
283 if not src.endswith(os.path.sep):
284 src += os.path.sep
285 if not dst.endswith(os.path.sep):
286 dst += os.path.sep
287 return dst.startswith(src)