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 and mtime) 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'):
73 """Copy data and mode bits ("cp src dst").
75 The destination may be a directory.
78 if os
.path
.isdir(dst
):
79 dst
= os
.path
.join(dst
, os
.path
.basename(src
))
84 """Copy data and all stat info ("cp -p src dst").
86 The destination may be a directory.
89 if os
.path
.isdir(dst
):
90 dst
= os
.path
.join(dst
, os
.path
.basename(src
))
95 def copytree(src
, dst
, symlinks
=False):
96 """Recursively copy a directory tree using copy2().
98 The destination directory must not already exist.
99 If exception(s) occur, an Error is raised with a list of reasons.
101 If the optional symlinks flag is true, symbolic links in the
102 source tree result in symbolic links in the destination tree; if
103 it is false, the contents of the files pointed to by symbolic
106 XXX Consider this example code rather than the ultimate tool.
109 names
= os
.listdir(src
)
113 srcname
= os
.path
.join(src
, name
)
114 dstname
= os
.path
.join(dst
, name
)
116 if symlinks
and os
.path
.islink(srcname
):
117 linkto
= os
.readlink(srcname
)
118 os
.symlink(linkto
, dstname
)
119 elif os
.path
.isdir(srcname
):
120 copytree(srcname
, dstname
, symlinks
)
122 copy2(srcname
, dstname
)
123 # XXX What about devices, sockets etc.?
124 except (IOError, os
.error
), why
:
125 errors
.append((srcname
, dstname
, str(why
)))
126 # catch the Error from the recursive copytree so that we can
127 # continue with other files
129 errors
.extend(err
.args
[0])
133 # can't copy file access times on Windows
136 errors
.extend((src
, dst
, str(why
)))
140 def rmtree(path
, ignore_errors
=False, onerror
=None):
141 """Recursively delete a directory tree.
143 If ignore_errors is set, errors are ignored; otherwise, if onerror
144 is set, it is called to handle the error with arguments (func,
145 path, exc_info) where func is os.listdir, os.remove, or os.rmdir;
146 path is the argument to that function that caused it to fail; and
147 exc_info is a tuple returned by sys.exc_info(). If ignore_errors
148 is false and onerror is None, an exception is raised.
154 elif onerror
is None:
159 names
= os
.listdir(path
)
160 except os
.error
, err
:
161 onerror(os
.listdir
, path
, sys
.exc_info())
163 fullname
= os
.path
.join(path
, name
)
165 mode
= os
.lstat(fullname
).st_mode
168 if stat
.S_ISDIR(mode
):
169 rmtree(fullname
, ignore_errors
, onerror
)
173 except os
.error
, err
:
174 onerror(os
.remove
, fullname
, sys
.exc_info())
178 onerror(os
.rmdir
, path
, sys
.exc_info())
181 """Recursively move a file or directory to another location.
183 If the destination is on our current filesystem, then simply use
184 rename. Otherwise, copy src to the dst and then remove src.
185 A lot more could be done here... A look at a mv.c shows a lot of
186 the issues this implementation glosses over.
193 if os
.path
.isdir(src
):
194 if destinsrc(src
, dst
):
195 raise Error
, "Cannot move a directory '%s' into itself '%s'." % (src
, dst
)
196 copytree(src
, dst
, symlinks
=True)
202 def destinsrc(src
, dst
):
203 return abspath(dst
).startswith(abspath(src
))