source: trunk/essentials/dev-lang/python/Lib/shutil.py@ 3314

Last change on this file since 3314 was 3225, checked in by bird, 19 years ago

Python 2.5

File size: 5.8 KB
Line 
1"""Utility functions for copying files and directory trees.
2
3XXX The functions here don't copy the resource fork or other metadata on Mac.
4
5"""
6
7import os
8import sys
9import stat
10from os.path import abspath
11
12__all__ = ["copyfileobj","copyfile","copymode","copystat","copy","copy2",
13 "copytree","move","rmtree","Error"]
14
15class Error(EnvironmentError):
16 pass
17
18def copyfileobj(fsrc, fdst, length=16*1024):
19 """copy data from file-like object fsrc to file-like object fdst"""
20 while 1:
21 buf = fsrc.read(length)
22 if not buf:
23 break
24 fdst.write(buf)
25
26def _samefile(src, dst):
27 # Macintosh, Unix.
28 if hasattr(os.path,'samefile'):
29 try:
30 return os.path.samefile(src, dst)
31 except OSError:
32 return False
33
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)))
37
38def 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)
42
43 fsrc = None
44 fdst = None
45 try:
46 fsrc = open(src, 'rb')
47 fdst = open(dst, 'wb')
48 copyfileobj(fsrc, fdst)
49 finally:
50 if fdst:
51 fdst.close()
52 if fsrc:
53 fsrc.close()
54
55def copymode(src, dst):
56 """Copy mode bits from src to dst"""
57 if hasattr(os, 'chmod'):
58 st = os.stat(src)
59 mode = stat.S_IMODE(st.st_mode)
60 os.chmod(dst, mode)
61
62def copystat(src, dst):
63 """Copy all stat info (mode bits, atime and mtime) from src to dst"""
64 st = os.stat(src)
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'):
69 os.chmod(dst, mode)
70
71
72def copy(src, dst):
73 """Copy data and mode bits ("cp src dst").
74
75 The destination may be a directory.
76
77 """
78 if os.path.isdir(dst):
79 dst = os.path.join(dst, os.path.basename(src))
80 copyfile(src, dst)
81 copymode(src, dst)
82
83def copy2(src, dst):
84 """Copy data and all stat info ("cp -p src dst").
85
86 The destination may be a directory.
87
88 """
89 if os.path.isdir(dst):
90 dst = os.path.join(dst, os.path.basename(src))
91 copyfile(src, dst)
92 copystat(src, dst)
93
94
95def copytree(src, dst, symlinks=False):
96 """Recursively copy a directory tree using copy2().
97
98 The destination directory must not already exist.
99 If exception(s) occur, an Error is raised with a list of reasons.
100
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
104 links are copied.
105
106 XXX Consider this example code rather than the ultimate tool.
107
108 """
109 names = os.listdir(src)
110 os.makedirs(dst)
111 errors = []
112 for name in names:
113 srcname = os.path.join(src, name)
114 dstname = os.path.join(dst, name)
115 try:
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)
121 else:
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
128 except Error, err:
129 errors.extend(err.args[0])
130 try:
131 copystat(src, dst)
132 except WindowsError:
133 # can't copy file access times on Windows
134 pass
135 except OSError, why:
136 errors.extend((src, dst, str(why)))
137 if errors:
138 raise Error, errors
139
140def rmtree(path, ignore_errors=False, onerror=None):
141 """Recursively delete a directory tree.
142
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.
149
150 """
151 if ignore_errors:
152 def onerror(*args):
153 pass
154 elif onerror is None:
155 def onerror(*args):
156 raise
157 names = []
158 try:
159 names = os.listdir(path)
160 except os.error, err:
161 onerror(os.listdir, path, sys.exc_info())
162 for name in names:
163 fullname = os.path.join(path, name)
164 try:
165 mode = os.lstat(fullname).st_mode
166 except os.error:
167 mode = 0
168 if stat.S_ISDIR(mode):
169 rmtree(fullname, ignore_errors, onerror)
170 else:
171 try:
172 os.remove(fullname)
173 except os.error, err:
174 onerror(os.remove, fullname, sys.exc_info())
175 try:
176 os.rmdir(path)
177 except os.error:
178 onerror(os.rmdir, path, sys.exc_info())
179
180def move(src, dst):
181 """Recursively move a file or directory to another location.
182
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.
187
188 """
189
190 try:
191 os.rename(src, dst)
192 except OSError:
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)
197 rmtree(src)
198 else:
199 copy2(src,dst)
200 os.unlink(src)
201
202def destinsrc(src, dst):
203 return abspath(dst).startswith(abspath(src))
Note: See TracBrowser for help on using the repository browser.