]> git.lizzy.rs Git - plan9front.git/blob - sys/lib/python/shutil.py
dist/mkfile: run binds in subshell
[plan9front.git] / sys / lib / python / shutil.py
1 """Utility functions for copying files and directory trees.
2
3 XXX The functions here don't copy the resource fork or other metadata on Mac.
4
5 """
6
7 import os
8 import sys
9 import stat
10 from os.path import abspath
11
12 __all__ = ["copyfileobj","copyfile","copymode","copystat","copy","copy2",
13            "copytree","move","rmtree","Error"]
14
15 class Error(EnvironmentError):
16     pass
17
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)
25
26 def _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
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)
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
55 def 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
62 def 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
72 def 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
83 def 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
95 def 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
140 def 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
180 def 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
202 def destinsrc(src, dst):
203     return abspath(dst).startswith(abspath(src))