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.
10 from os
.path
import abspath
13 __all__
= ["copyfileobj","copyfile","copymode","copystat","copy","copy2",
14 "copytree","move","rmtree","Error"]
16 class Error(EnvironmentError):
24 def copyfileobj(fsrc
, fdst
, length
=16*1024):
25 """copy data from file-like object fsrc to file-like object fdst"""
27 buf
= fsrc
.read(length
)
32 def _samefile(src
, dst
):
34 if hasattr(os
.path
,'samefile'):
36 return os
.path
.samefile(src
, dst
)
40 # All other platforms: check for same pathname.
41 return (os
.path
.normcase(os
.path
.abspath(src
)) ==
42 os
.path
.normcase(os
.path
.abspath(dst
)))
44 def copyfile(src
, dst
):
45 """Copy data from src to dst"""
46 if _samefile(src
, dst
):
47 raise Error
, "`%s` and `%s` are the same file" % (src
, dst
)
52 fsrc
= open(src
, 'rb')
53 fdst
= open(dst
, 'wb')
54 copyfileobj(fsrc
, fdst
)
61 def copymode(src
, dst
):
62 """Copy mode bits from src to dst"""
63 if hasattr(os
, 'chmod'):
65 mode
= stat
.S_IMODE(st
.st_mode
)
68 def copystat(src
, dst
):
69 """Copy all stat info (mode bits, atime, mtime, flags) from src to dst"""
71 mode
= stat
.S_IMODE(st
.st_mode
)
72 if hasattr(os
, 'utime'):
73 os
.utime(dst
, (st
.st_atime
, st
.st_mtime
))
74 if hasattr(os
, 'chmod'):
76 if hasattr(os
, 'chflags') and hasattr(st
, 'st_flags'):
77 os
.chflags(dst
, st
.st_flags
)
81 """Copy data and mode bits ("cp src dst").
83 The destination may be a directory.
86 if os
.path
.isdir(dst
):
87 dst
= os
.path
.join(dst
, os
.path
.basename(src
))
92 """Copy data and all stat info ("cp -p src dst").
94 The destination may be a directory.
97 if os
.path
.isdir(dst
):
98 dst
= os
.path
.join(dst
, os
.path
.basename(src
))
102 def ignore_patterns(*patterns
):
103 """Function that can be used as copytree() ignore parameter.
105 Patterns is a sequence of glob-style patterns
106 that are used to exclude files"""
107 def _ignore_patterns(path
, names
):
109 for pattern
in patterns
:
110 ignored_names
.extend(fnmatch
.filter(names
, pattern
))
111 return set(ignored_names
)
112 return _ignore_patterns
114 def copytree(src
, dst
, symlinks
=False, ignore
=None):
115 """Recursively copy a directory tree using copy2().
117 The destination directory must not already exist.
118 If exception(s) occur, an Error is raised with a list of reasons.
120 If the optional symlinks flag is true, symbolic links in the
121 source tree result in symbolic links in the destination tree; if
122 it is false, the contents of the files pointed to by symbolic
125 The optional ignore argument is a callable. If given, it
126 is called with the `src` parameter, which is the directory
127 being visited by copytree(), and `names` which is the list of
128 `src` contents, as returned by os.listdir():
130 callable(src, names) -> ignored_names
132 Since copytree() is called recursively, the callable will be
133 called once for each directory that is copied. It returns a
134 list of names relative to the `src` directory that should
137 XXX Consider this example code rather than the ultimate tool.
140 names
= os
.listdir(src
)
141 if ignore
is not None:
142 ignored_names
= ignore(src
, names
)
144 ignored_names
= set()
149 if name
in ignored_names
:
151 srcname
= os
.path
.join(src
, name
)
152 dstname
= os
.path
.join(dst
, name
)
154 if symlinks
and os
.path
.islink(srcname
):
155 linkto
= os
.readlink(srcname
)
156 os
.symlink(linkto
, dstname
)
157 elif os
.path
.isdir(srcname
):
158 copytree(srcname
, dstname
, symlinks
, ignore
)
160 copy2(srcname
, dstname
)
161 # XXX What about devices, sockets etc.?
162 except (IOError, os
.error
), why
:
163 errors
.append((srcname
, dstname
, str(why
)))
164 # catch the Error from the recursive copytree so that we can
165 # continue with other files
167 errors
.extend(err
.args
[0])
171 if WindowsError is not None and isinstance(why
, WindowsError):
172 # Copying file access times may fail on Windows
175 errors
.extend((src
, dst
, str(why
)))
179 def rmtree(path
, ignore_errors
=False, onerror
=None):
180 """Recursively delete a directory tree.
182 If ignore_errors is set, errors are ignored; otherwise, if onerror
183 is set, it is called to handle the error with arguments (func,
184 path, exc_info) where func is os.listdir, os.remove, or os.rmdir;
185 path is the argument to that function that caused it to fail; and
186 exc_info is a tuple returned by sys.exc_info(). If ignore_errors
187 is false and onerror is None, an exception is raised.
193 elif onerror
is None:
197 if os
.path
.islink(path
):
198 # symlinks to directories are forbidden, see bug #1669
199 raise OSError("Cannot call rmtree on a symbolic link")
201 onerror(os
.path
.islink
, path
, sys
.exc_info())
202 # can't continue even if onerror hook returns
206 names
= os
.listdir(path
)
207 except os
.error
, err
:
208 onerror(os
.listdir
, path
, sys
.exc_info())
210 fullname
= os
.path
.join(path
, name
)
212 mode
= os
.lstat(fullname
).st_mode
215 if stat
.S_ISDIR(mode
):
216 rmtree(fullname
, ignore_errors
, onerror
)
220 except os
.error
, err
:
221 onerror(os
.remove
, fullname
, sys
.exc_info())
225 onerror(os
.rmdir
, path
, sys
.exc_info())
229 # A basename() variant which first strips the trailing slash, if present.
230 # Thus we always get the last component of the path, even for directories.
231 return os
.path
.basename(path
.rstrip(os
.path
.sep
))
234 """Recursively move a file or directory to another location. This is
235 similar to the Unix "mv" command.
237 If the destination is a directory or a symlink to a directory, the source
238 is moved inside the directory. The destination path must not already
241 If the destination already exists but is not a directory, it may be
242 overwritten depending on os.rename() semantics.
244 If the destination is on our current filesystem, then rename() is used.
245 Otherwise, src is copied to the destination and then removed.
246 A lot more could be done here... A look at a mv.c shows a lot of
247 the issues this implementation glosses over.
251 if os
.path
.isdir(dst
):
252 real_dst
= os
.path
.join(dst
, _basename(src
))
253 if os
.path
.exists(real_dst
):
254 raise Error
, "Destination path '%s' already exists" % real_dst
256 os
.rename(src
, real_dst
)
258 if os
.path
.isdir(src
):
259 if destinsrc(src
, dst
):
260 raise Error
, "Cannot move a directory '%s' into itself '%s'." % (src
, dst
)
261 copytree(src
, real_dst
, symlinks
=True)
267 def destinsrc(src
, dst
):
268 return abspath(dst
).startswith(abspath(src
))