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
12 __all__
= ["copyfileobj","copyfile","copymode","copystat","copy","copy2",
13 "copytree","move","rmtree","Error"]
15 class Error(EnvironmentError):
18 def copyfileobj(fsrc
, fdst
, length
=16*1024):
19 """copy data from file-like object fsrc to file-like object fdst"""
21 buf
= fsrc
.read(length
)
26 def _samefile(src
, dst
):
28 if hasattr(os
.path
,'samefile'):
30 return os
.path
.samefile(src
, dst
)
34 # All other platforms: check for same pathname.
35 return (os
.path
.normcase(os
.path
.abspath(src
)) ==
36 os
.path
.normcase(os
.path
.abspath(dst
)))
38 def copyfile(src
, dst
):
39 """Copy data from src to dst"""
40 if _samefile(src
, dst
):
41 raise Error
, "`%s` and `%s` are the same file" % (src
, dst
)
46 fsrc
= open(src
, 'rb')
47 fdst
= open(dst
, 'wb')
48 copyfileobj(fsrc
, fdst
)
55 def copymode(src
, dst
):
56 """Copy mode bits from src to dst"""
57 if hasattr(os
, 'chmod'):
59 mode
= stat
.S_IMODE(st
.st_mode
)
62 def copystat(src
, dst
):
63 """Copy all stat info (mode bits, atime, mtime, flags) from src to dst"""
65 mode
= stat
.S_IMODE(st
.st_mode
)
66 if hasattr(os
, 'utime'):
67 os
.utime(dst
, (st
.st_atime
, st
.st_mtime
))
68 if hasattr(os
, 'chmod'):
70 if hasattr(os
, 'chflags') and hasattr(st
, 'st_flags'):
71 os
.chflags(dst
, st
.st_flags
)
75 """Copy data and mode bits ("cp src dst").
77 The destination may be a directory.
80 if os
.path
.isdir(dst
):
81 dst
= os
.path
.join(dst
, os
.path
.basename(src
))
86 """Copy data and all stat info ("cp -p src dst").
88 The destination may be a directory.
91 if os
.path
.isdir(dst
):
92 dst
= os
.path
.join(dst
, os
.path
.basename(src
))
97 def copytree(src
, dst
, symlinks
=False):
98 """Recursively copy a directory tree using copy2().
100 The destination directory must not already exist.
101 If exception(s) occur, an Error is raised with a list of reasons.
103 If the optional symlinks flag is true, symbolic links in the
104 source tree result in symbolic links in the destination tree; if
105 it is false, the contents of the files pointed to by symbolic
108 XXX Consider this example code rather than the ultimate tool.
111 names
= os
.listdir(src
)
115 srcname
= os
.path
.join(src
, name
)
116 dstname
= os
.path
.join(dst
, name
)
118 if symlinks
and os
.path
.islink(srcname
):
119 linkto
= os
.readlink(srcname
)
120 os
.symlink(linkto
, dstname
)
121 elif os
.path
.isdir(srcname
):
122 copytree(srcname
, dstname
, symlinks
)
124 copy2(srcname
, dstname
)
125 # XXX What about devices, sockets etc.?
126 except (IOError, os
.error
), why
:
127 errors
.append((srcname
, dstname
, str(why
)))
128 # catch the Error from the recursive copytree so that we can
129 # continue with other files
131 errors
.extend(err
.args
[0])
135 # can't copy file access times on Windows
138 errors
.extend((src
, dst
, str(why
)))
142 def rmtree(path
, ignore_errors
=False, onerror
=None):
143 """Recursively delete a directory tree.
145 If ignore_errors is set, errors are ignored; otherwise, if onerror
146 is set, it is called to handle the error with arguments (func,
147 path, exc_info) where func is os.listdir, os.remove, or os.rmdir;
148 path is the argument to that function that caused it to fail; and
149 exc_info is a tuple returned by sys.exc_info(). If ignore_errors
150 is false and onerror is None, an exception is raised.
156 elif onerror
is None:
161 names
= os
.listdir(path
)
162 except os
.error
, err
:
163 onerror(os
.listdir
, path
, sys
.exc_info())
165 fullname
= os
.path
.join(path
, name
)
167 mode
= os
.lstat(fullname
).st_mode
170 if stat
.S_ISDIR(mode
):
171 rmtree(fullname
, ignore_errors
, onerror
)
175 except os
.error
, err
:
176 onerror(os
.remove
, fullname
, sys
.exc_info())
180 onerror(os
.rmdir
, path
, sys
.exc_info())
183 """Recursively move a file or directory to another location.
185 If the destination is on our current filesystem, then simply use
186 rename. Otherwise, copy src to the dst and then remove src.
187 A lot more could be done here... A look at a mv.c shows a lot of
188 the issues this implementation glosses over.
195 if os
.path
.isdir(src
):
196 if destinsrc(src
, dst
):
197 raise Error
, "Cannot move a directory '%s' into itself '%s'." % (src
, dst
)
198 copytree(src
, dst
, symlinks
=True)
204 def destinsrc(src
, dst
):
205 return abspath(dst
).startswith(abspath(src
))