Move setting of ioready 'wait' earlier in call chain, to
[python/dscho.git] / Lib / shutil.py
blob5341786d528d6487be7d282fdcc6bdd1f90cdcc9
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 import exceptions
12 __all__ = ["copyfileobj","copyfile","copymode","copystat","copy","copy2",
13 "copytree","move","rmtree","Error"]
15 class Error(exceptions.EnvironmentError):
16 pass
18 def 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)
27 def copyfile(src, dst):
28 """Copy data from src to dst"""
29 fsrc = None
30 fdst = None
31 # check for same pathname; all platforms
32 _src = os.path.normcase(os.path.abspath(src))
33 _dst = os.path.normcase(os.path.abspath(dst))
34 if _src == _dst:
35 return
36 try:
37 fsrc = open(src, 'rb')
38 fdst = open(dst, 'wb')
39 copyfileobj(fsrc, fdst)
40 finally:
41 if fdst:
42 fdst.close()
43 if fsrc:
44 fsrc.close()
46 def copymode(src, dst):
47 """Copy mode bits from src to dst"""
48 if hasattr(os, 'chmod'):
49 st = os.stat(src)
50 mode = stat.S_IMODE(st.st_mode)
51 os.chmod(dst, mode)
53 def copystat(src, dst):
54 """Copy all stat info (mode bits, atime and mtime) from src to dst"""
55 st = os.stat(src)
56 mode = stat.S_IMODE(st.st_mode)
57 if hasattr(os, 'utime'):
58 os.utime(dst, (st.st_atime, st.st_mtime))
59 if hasattr(os, 'chmod'):
60 os.chmod(dst, mode)
63 def copy(src, dst):
64 """Copy data and mode bits ("cp src dst").
66 The destination may be a directory.
68 """
69 if os.path.isdir(dst):
70 dst = os.path.join(dst, os.path.basename(src))
71 copyfile(src, dst)
72 copymode(src, dst)
74 def copy2(src, dst):
75 """Copy data and all stat info ("cp -p src dst").
77 The destination may be a directory.
79 """
80 if os.path.isdir(dst):
81 dst = os.path.join(dst, os.path.basename(src))
82 copyfile(src, dst)
83 copystat(src, dst)
86 def copytree(src, dst, symlinks=False):
87 """Recursively copy a directory tree using copy2().
89 The destination directory must not already exist.
90 If exception(s) occur, an Error is raised with a list of reasons.
92 If the optional symlinks flag is true, symbolic links in the
93 source tree result in symbolic links in the destination tree; if
94 it is false, the contents of the files pointed to by symbolic
95 links are copied.
97 XXX Consider this example code rather than the ultimate tool.
99 """
100 names = os.listdir(src)
101 os.mkdir(dst)
102 errors = []
103 for name in names:
104 srcname = os.path.join(src, name)
105 dstname = os.path.join(dst, name)
106 try:
107 if symlinks and os.path.islink(srcname):
108 linkto = os.readlink(srcname)
109 os.symlink(linkto, dstname)
110 elif os.path.isdir(srcname):
111 copytree(srcname, dstname, symlinks)
112 else:
113 copy2(srcname, dstname)
114 # XXX What about devices, sockets etc.?
115 except (IOError, os.error), why:
116 errors.append((srcname, dstname, why))
117 if errors:
118 raise Error, errors
120 def rmtree(path, ignore_errors=False, onerror=None):
121 """Recursively delete a directory tree.
123 If ignore_errors is set, errors are ignored; otherwise, if
124 onerror is set, it is called to handle the error; otherwise, an
125 exception is raised.
127 cmdtuples = []
128 arg = path
129 try:
130 _build_cmdtuple(path, cmdtuples)
131 for func, arg in cmdtuples:
132 func(arg)
133 except OSError:
134 exc = sys.exc_info()
135 if ignore_errors:
136 pass
137 elif onerror is not None:
138 onerror(func, arg, exc)
139 else:
140 raise exc[0], (exc[1][0], exc[1][1] + ' removing '+arg)
142 # Helper for rmtree()
143 def _build_cmdtuple(path, cmdtuples):
144 for f in os.listdir(path):
145 real_f = os.path.join(path,f)
146 if os.path.isdir(real_f) and not os.path.islink(real_f):
147 _build_cmdtuple(real_f, cmdtuples)
148 else:
149 cmdtuples.append((os.remove, real_f))
150 cmdtuples.append((os.rmdir, path))
153 def move(src, dst):
154 """Recursively move a file or directory to another location.
156 If the destination is on our current filesystem, then simply use
157 rename. Otherwise, copy src to the dst and then remove src.
158 A lot more could be done here... A look at a mv.c shows a lot of
159 the issues this implementation glosses over.
163 try:
164 os.rename(src, dst)
165 except OSError:
166 if os.path.isdir(src):
167 copytree(src, dst, symlinks=True)
168 rmtree(src)
169 else:
170 copy2(src,dst)
171 os.unlink(src)