0001 """distutils.file_util 0002 0003 Utility functions for operating on single files. 0004 """ 0005 0006 # This module should be kept compatible with Python 1.5.2. 0007 0008 __revision__ = "$Id: file_util.py,v 1.16 2004/07/18 06:14:42 tim_one Exp $" 0009 0010 import os 0011 from distutils.errors import DistutilsFileError 0012 from distutils import log 0013 0014 # for generating verbose output in 'copy_file()' 0015 _copy_action = { None: 'copying', 0016 'hard': 'hard linking', 0017 'sym': 'symbolically linking' } 0018 0019 0020 def _copy_file_contents (src, dst, buffer_size=16*1024): 0021 """Copy the file 'src' to 'dst'; both must be filenames. Any error 0022 opening either file, reading from 'src', or writing to 'dst', raises 0023 DistutilsFileError. Data is read/written in chunks of 'buffer_size' 0024 bytes (default 16k). No attempt is made to handle anything apart from 0025 regular files. 0026 """ 0027 # Stolen from shutil module in the standard library, but with 0028 # custom error-handling added. 0029 0030 fsrc = None 0031 fdst = None 0032 try: 0033 try: 0034 fsrc = open(src, 'rb') 0035 except os.error, (errno, errstr): 0036 raise DistutilsFileError, \ 0037 "could not open '%s': %s" % (src, errstr) 0038 0039 if os.path.exists(dst): 0040 try: 0041 os.unlink(dst) 0042 except os.error, (errno, errstr): 0043 raise DistutilsFileError, \ 0044 "could not delete '%s': %s" % (dst, errstr) 0045 0046 try: 0047 fdst = open(dst, 'wb') 0048 except os.error, (errno, errstr): 0049 raise DistutilsFileError, \ 0050 "could not create '%s': %s" % (dst, errstr) 0051 0052 while 1: 0053 try: 0054 buf = fsrc.read(buffer_size) 0055 except os.error, (errno, errstr): 0056 raise DistutilsFileError, \ 0057 "could not read from '%s': %s" % (src, errstr) 0058 0059 if not buf: 0060 break 0061 0062 try: 0063 fdst.write(buf) 0064 except os.error, (errno, errstr): 0065 raise DistutilsFileError, \ 0066 "could not write to '%s': %s" % (dst, errstr) 0067 0068 finally: 0069 if fdst: 0070 fdst.close() 0071 if fsrc: 0072 fsrc.close() 0073 0074 # _copy_file_contents() 0075 0076 def copy_file (src, dst, 0077 preserve_mode=1, 0078 preserve_times=1, 0079 update=0, 0080 link=None, 0081 verbose=0, 0082 dry_run=0): 0083 0084 """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is 0085 copied there with the same name; otherwise, it must be a filename. (If 0086 the file exists, it will be ruthlessly clobbered.) If 'preserve_mode' 0087 is true (the default), the file's mode (type and permission bits, or 0088 whatever is analogous on the current platform) is copied. If 0089 'preserve_times' is true (the default), the last-modified and 0090 last-access times are copied as well. If 'update' is true, 'src' will 0091 only be copied if 'dst' does not exist, or if 'dst' does exist but is 0092 older than 'src'. 0093 0094 'link' allows you to make hard links (os.link) or symbolic links 0095 (os.symlink) instead of copying: set it to "hard" or "sym"; if it is 0096 None (the default), files are copied. Don't set 'link' on systems that 0097 don't support it: 'copy_file()' doesn't check if hard or symbolic 0098 linking is available. 0099 0100 Under Mac OS, uses the native file copy function in macostools; on 0101 other systems, uses '_copy_file_contents()' to copy file contents. 0102 0103 Return a tuple (dest_name, copied): 'dest_name' is the actual name of 0104 the output file, and 'copied' is true if the file was copied (or would 0105 have been copied, if 'dry_run' true). 0106 """ 0107 # XXX if the destination file already exists, we clobber it if 0108 # copying, but blow up if linking. Hmmm. And I don't know what 0109 # macostools.copyfile() does. Should definitely be consistent, and 0110 # should probably blow up if destination exists and we would be 0111 # changing it (ie. it's not already a hard/soft link to src OR 0112 # (not update) and (src newer than dst). 0113 0114 from distutils.dep_util import newer 0115 from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE 0116 0117 if not os.path.isfile(src): 0118 raise DistutilsFileError, \ 0119 "can't copy '%s': doesn't exist or not a regular file" % src 0120 0121 if os.path.isdir(dst): 0122 dir = dst 0123 dst = os.path.join(dst, os.path.basename(src)) 0124 else: 0125 dir = os.path.dirname(dst) 0126 0127 if update and not newer(src, dst): 0128 log.debug("not copying %s (output up-to-date)", src) 0129 return dst, 0 0130 0131 try: 0132 action = _copy_action[link] 0133 except KeyError: 0134 raise ValueError, \ 0135 "invalid value '%s' for 'link' argument" % link 0136 if os.path.basename(dst) == os.path.basename(src): 0137 log.info("%s %s -> %s", action, src, dir) 0138 else: 0139 log.info("%s %s -> %s", action, src, dst) 0140 0141 if dry_run: 0142 return (dst, 1) 0143 0144 # On Mac OS, use the native file copy routine 0145 if os.name == 'mac': 0146 import macostools 0147 try: 0148 macostools.copy(src, dst, 0, preserve_times) 0149 except os.error, exc: 0150 raise DistutilsFileError, \ 0151 "could not copy '%s' to '%s': %s" % (src, dst, exc[-1]) 0152 0153 # If linking (hard or symbolic), use the appropriate system call 0154 # (Unix only, of course, but that's the caller's responsibility) 0155 elif link == 'hard': 0156 if not (os.path.exists(dst) and os.path.samefile(src, dst)): 0157 os.link(src, dst) 0158 elif link == 'sym': 0159 if not (os.path.exists(dst) and os.path.samefile(src, dst)): 0160 os.symlink(src, dst) 0161 0162 # Otherwise (non-Mac, not linking), copy the file contents and 0163 # (optionally) copy the times and mode. 0164 else: 0165 _copy_file_contents(src, dst) 0166 if preserve_mode or preserve_times: 0167 st = os.stat(src) 0168 0169 # According to David Ascher <da@ski.org>, utime() should be done 0170 # before chmod() (at least under NT). 0171 if preserve_times: 0172 os.utime(dst, (st[ST_ATIME], st[ST_MTIME])) 0173 if preserve_mode: 0174 os.chmod(dst, S_IMODE(st[ST_MODE])) 0175 0176 return (dst, 1) 0177 0178 # copy_file () 0179 0180 0181 # XXX I suspect this is Unix-specific -- need porting help! 0182 def move_file (src, dst, 0183 verbose=0, 0184 dry_run=0): 0185 0186 """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will 0187 be moved into it with the same name; otherwise, 'src' is just renamed 0188 to 'dst'. Return the new full name of the file. 0189 0190 Handles cross-device moves on Unix using 'copy_file()'. What about 0191 other systems??? 0192 """ 0193 from os.path import exists, isfile, isdir, basename, dirname 0194 import errno 0195 0196 log.info("moving %s -> %s", src, dst) 0197 0198 if dry_run: 0199 return dst 0200 0201 if not isfile(src): 0202 raise DistutilsFileError, \ 0203 "can't move '%s': not a regular file" % src 0204 0205 if isdir(dst): 0206 dst = os.path.join(dst, basename(src)) 0207 elif exists(dst): 0208 raise DistutilsFileError, \ 0209 "can't move '%s': destination '%s' already exists" % \ 0210 (src, dst) 0211 0212 if not isdir(dirname(dst)): 0213 raise DistutilsFileError, \ 0214 "can't move '%s': destination '%s' not a valid path" % \ 0215 (src, dst) 0216 0217 copy_it = 0 0218 try: 0219 os.rename(src, dst) 0220 except os.error, (num, msg): 0221 if num == errno.EXDEV: 0222 copy_it = 1 0223 else: 0224 raise DistutilsFileError, \ 0225 "couldn't move '%s' to '%s': %s" % (src, dst, msg) 0226 0227 if copy_it: 0228 copy_file(src, dst) 0229 try: 0230 os.unlink(src) 0231 except os.error, (num, msg): 0232 try: 0233 os.unlink(dst) 0234 except os.error: 0235 pass 0236 raise DistutilsFileError, \ 0237 ("couldn't move '%s' to '%s' by copy/delete: " + 0238 "delete '%s' failed: %s") % \ 0239 (src, dst, src, msg) 0240 0241 return dst 0242 0243 # move_file () 0244 0245 0246 def write_file (filename, contents): 0247 """Create a file with the specified name and write 'contents' (a 0248 sequence of strings without line terminators) to it. 0249 """ 0250 f = open(filename, "w") 0251 for line in contents: 0252 f.write(line + "\n") 0253 f.close() 0254
Generated by PyXR 0.9.4