0001 """Utility functions for copying files and directory trees. 0002 0003 XXX The functions here don't copy the resource fork or other metadata on Mac. 0004 0005 """ 0006 0007 import os 0008 import sys 0009 import stat 0010 import exceptions 0011 from os.path import abspath 0012 0013 __all__ = ["copyfileobj","copyfile","copymode","copystat","copy","copy2", 0014 "copytree","move","rmtree","Error"] 0015 0016 class Error(exceptions.EnvironmentError): 0017 pass 0018 0019 def copyfileobj(fsrc, fdst, length=16*1024): 0020 """copy data from file-like object fsrc to file-like object fdst""" 0021 while 1: 0022 buf = fsrc.read(length) 0023 if not buf: 0024 break 0025 fdst.write(buf) 0026 0027 def _samefile(src, dst): 0028 # Macintosh, Unix. 0029 if hasattr(os.path,'samefile'): 0030 try: 0031 return os.path.samefile(src, dst) 0032 except OSError: 0033 return False 0034 0035 # All other platforms: check for same pathname. 0036 return (os.path.normcase(os.path.abspath(src)) == 0037 os.path.normcase(os.path.abspath(dst))) 0038 0039 def copyfile(src, dst): 0040 """Copy data from src to dst""" 0041 if _samefile(src, dst): 0042 raise Error, "`%s` and `%s` are the same file" % (src, dst) 0043 0044 fsrc = None 0045 fdst = None 0046 try: 0047 fsrc = open(src, 'rb') 0048 fdst = open(dst, 'wb') 0049 copyfileobj(fsrc, fdst) 0050 finally: 0051 if fdst: 0052 fdst.close() 0053 if fsrc: 0054 fsrc.close() 0055 0056 def copymode(src, dst): 0057 """Copy mode bits from src to dst""" 0058 if hasattr(os, 'chmod'): 0059 st = os.stat(src) 0060 mode = stat.S_IMODE(st.st_mode) 0061 os.chmod(dst, mode) 0062 0063 def copystat(src, dst): 0064 """Copy all stat info (mode bits, atime and mtime) from src to dst""" 0065 st = os.stat(src) 0066 mode = stat.S_IMODE(st.st_mode) 0067 if hasattr(os, 'utime'): 0068 os.utime(dst, (st.st_atime, st.st_mtime)) 0069 if hasattr(os, 'chmod'): 0070 os.chmod(dst, mode) 0071 0072 0073 def copy(src, dst): 0074 """Copy data and mode bits ("cp src dst"). 0075 0076 The destination may be a directory. 0077 0078 """ 0079 if os.path.isdir(dst): 0080 dst = os.path.join(dst, os.path.basename(src)) 0081 copyfile(src, dst) 0082 copymode(src, dst) 0083 0084 def copy2(src, dst): 0085 """Copy data and all stat info ("cp -p src dst"). 0086 0087 The destination may be a directory. 0088 0089 """ 0090 if os.path.isdir(dst): 0091 dst = os.path.join(dst, os.path.basename(src)) 0092 copyfile(src, dst) 0093 copystat(src, dst) 0094 0095 0096 def copytree(src, dst, symlinks=False): 0097 """Recursively copy a directory tree using copy2(). 0098 0099 The destination directory must not already exist. 0100 If exception(s) occur, an Error is raised with a list of reasons. 0101 0102 If the optional symlinks flag is true, symbolic links in the 0103 source tree result in symbolic links in the destination tree; if 0104 it is false, the contents of the files pointed to by symbolic 0105 links are copied. 0106 0107 XXX Consider this example code rather than the ultimate tool. 0108 0109 """ 0110 names = os.listdir(src) 0111 os.mkdir(dst) 0112 errors = [] 0113 for name in names: 0114 srcname = os.path.join(src, name) 0115 dstname = os.path.join(dst, name) 0116 try: 0117 if symlinks and os.path.islink(srcname): 0118 linkto = os.readlink(srcname) 0119 os.symlink(linkto, dstname) 0120 elif os.path.isdir(srcname): 0121 copytree(srcname, dstname, symlinks) 0122 else: 0123 copy2(srcname, dstname) 0124 # XXX What about devices, sockets etc.? 0125 except (IOError, os.error), why: 0126 errors.append((srcname, dstname, why)) 0127 if errors: 0128 raise Error, errors 0129 0130 def rmtree(path, ignore_errors=False, onerror=None): 0131 """Recursively delete a directory tree. 0132 0133 If ignore_errors is set, errors are ignored; otherwise, if onerror 0134 is set, it is called to handle the error with arguments (func, 0135 path, exc_info) where func is os.listdir, os.remove, or os.rmdir; 0136 path is the argument to that function that caused it to fail; and 0137 exc_info is a tuple returned by sys.exc_info(). If ignore_errors 0138 is false and onerror is None, an exception is raised. 0139 0140 """ 0141 if ignore_errors: 0142 def onerror(*args): 0143 pass 0144 elif onerror is None: 0145 def onerror(*args): 0146 raise 0147 names = [] 0148 try: 0149 names = os.listdir(path) 0150 except os.error, err: 0151 onerror(os.listdir, path, sys.exc_info()) 0152 for name in names: 0153 fullname = os.path.join(path, name) 0154 try: 0155 mode = os.lstat(fullname).st_mode 0156 except os.error: 0157 mode = 0 0158 if stat.S_ISDIR(mode): 0159 rmtree(fullname, ignore_errors, onerror) 0160 else: 0161 try: 0162 os.remove(fullname) 0163 except os.error, err: 0164 onerror(os.remove, fullname, sys.exc_info()) 0165 try: 0166 os.rmdir(path) 0167 except os.error: 0168 onerror(os.rmdir, path, sys.exc_info()) 0169 0170 def move(src, dst): 0171 """Recursively move a file or directory to another location. 0172 0173 If the destination is on our current filesystem, then simply use 0174 rename. Otherwise, copy src to the dst and then remove src. 0175 A lot more could be done here... A look at a mv.c shows a lot of 0176 the issues this implementation glosses over. 0177 0178 """ 0179 0180 try: 0181 os.rename(src, dst) 0182 except OSError: 0183 if os.path.isdir(src): 0184 if destinsrc(src, dst): 0185 raise Error, "Cannot move a directory '%s' into itself '%s'." % (src, dst) 0186 copytree(src, dst, symlinks=True) 0187 rmtree(src) 0188 else: 0189 copy2(src,dst) 0190 os.unlink(src) 0191 0192 def destinsrc(src, dst): 0193 return abspath(dst).startswith(abspath(src)) 0194
Generated by PyXR 0.9.4