3 Utility functions for operating on single files.
6 # created 2000/04/03, Greg Ward (extracted from util.py)
12 from distutils
.errors
import DistutilsFileError
15 # for generating verbose output in 'copy_file()'
16 _copy_action
= { None: 'copying',
17 'hard': 'hard linking',
18 'sym': 'symbolically linking' }
21 def _copy_file_contents (src
, dst
, buffer_size
=16*1024):
22 """Copy the file 'src' to 'dst'; both must be filenames. Any error
23 opening either file, reading from 'src', or writing to 'dst', raises
24 DistutilsFileError. Data is read/written in chunks of 'buffer_size'
25 bytes (default 16k). No attempt is made to handle anything apart from
28 # Stolen from shutil module in the standard library, but with
29 # custom error-handling added.
35 fsrc
= open(src
, 'rb')
36 except os
.error
, (errno
, errstr
):
37 raise DistutilsFileError
, \
38 "could not open '%s': %s" % (src
, errstr
)
41 fdst
= open(dst
, 'wb')
42 except os
.error
, (errno
, errstr
):
43 raise DistutilsFileError
, \
44 "could not create '%s': %s" % (dst
, errstr
)
48 buf
= fsrc
.read(buffer_size
)
49 except os
.error
, (errno
, errstr
):
50 raise DistutilsFileError
, \
51 "could not read from '%s': %s" % (src
, errstr
)
58 except os
.error
, (errno
, errstr
):
59 raise DistutilsFileError
, \
60 "could not write to '%s': %s" % (dst
, errstr
)
68 # _copy_file_contents()
71 def copy_file (src
, dst
,
79 """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is
80 copied there with the same name; otherwise, it must be a filename. (If
81 the file exists, it will be ruthlessly clobbered.) If 'preserve_mode'
82 is true (the default), the file's mode (type and permission bits, or
83 whatever is analogous on the current platform) is copied. If
84 'preserve_times' is true (the default), the last-modified and
85 last-access times are copied as well. If 'update' is true, 'src' will
86 only be copied if 'dst' does not exist, or if 'dst' does exist but is
87 older than 'src'. If 'verbose' is true, then a one-line summary of the
88 copy will be printed to stdout.
90 'link' allows you to make hard links (os.link) or symbolic links
91 (os.symlink) instead of copying: set it to "hard" or "sym"; if it is
92 None (the default), files are copied. Don't set 'link' on systems that
93 don't support it: 'copy_file()' doesn't check if hard or symbolic
96 Under Mac OS, uses the native file copy function in macostools; on
97 other systems, uses '_copy_file_contents()' to copy file contents.
99 Return a tuple (dest_name, copied): 'dest_name' is the actual name of
100 the output file, and 'copied' is true if the file was copied (or would
101 have been copied, if 'dry_run' true).
103 # XXX if the destination file already exists, we clobber it if
104 # copying, but blow up if linking. Hmmm. And I don't know what
105 # macostools.copyfile() does. Should definitely be consistent, and
106 # should probably blow up if destination exists and we would be
107 # changing it (ie. it's not already a hard/soft link to src OR
108 # (not update) and (src newer than dst).
110 from distutils
.dep_util
import newer
112 if not os
.path
.isfile(src
):
113 raise DistutilsFileError
, \
114 "can't copy '%s': doesn't exist or not a regular file" % src
116 if os
.path
.isdir(dst
):
118 dst
= os
.path
.join(dst
, os
.path
.basename(src
))
120 dir = os
.path
.dirname(dst
)
122 if update
and not newer(src
, dst
):
124 print "not copying %s (output up-to-date)" % src
128 action
= _copy_action
[link
]
131 "invalid value '%s' for 'link' argument" % link
133 if os
.path
.basename(dst
) == os
.path
.basename(src
):
134 print "%s %s -> %s" % (action
, src
, dir)
136 print "%s %s -> %s" % (action
, src
, dst
)
141 # On Mac OS, use the native file copy routine
145 macostools
.copy(src
, dst
, 0, preserve_times
)
146 except os
.error
, exc
:
147 raise DistutilsFileError
, \
148 "could not copy '%s' to '%s': %s" % (src
, dst
, exc
[-1])
150 # If linking (hard or symbolic), use the appropriate system call
151 # (Unix only, of course, but that's the caller's responsibility)
153 if not (os
.path
.exists(dst
) and os
.path
.samefile(src
, dst
)):
156 if not (os
.path
.exists(dst
) and os
.path
.samefile(src
, dst
)):
159 # Otherwise (non-Mac, not linking), copy the file contents and
160 # (optionally) copy the times and mode.
162 _copy_file_contents(src
, dst
)
163 if preserve_mode
or preserve_times
:
166 # According to David Ascher <da@ski.org>, utime() should be done
167 # before chmod() (at least under NT).
169 os
.utime(dst
, (st
[ST_ATIME
], st
[ST_MTIME
]))
171 os
.chmod(dst
, S_IMODE(st
[ST_MODE
]))
178 # XXX I suspect this is Unix-specific -- need porting help!
179 def move_file (src
, dst
,
183 """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will
184 be moved into it with the same name; otherwise, 'src' is just renamed
185 to 'dst'. Return the new full name of the file.
187 Handles cross-device moves on Unix using 'copy_file()'. What about
190 from os
.path
import exists
, isfile
, isdir
, basename
, dirname
193 print "moving %s -> %s" % (src
, dst
)
199 raise DistutilsFileError
, \
200 "can't move '%s': not a regular file" % src
203 dst
= os
.path
.join(dst
, basename(src
))
205 raise DistutilsFileError
, \
206 "can't move '%s': destination '%s' already exists" % \
209 if not isdir(dirname(dst
)):
210 raise DistutilsFileError
, \
211 "can't move '%s': destination '%s' not a valid path" % \
217 except os
.error
, (num
, msg
):
218 if num
== errno
.EXDEV
:
221 raise DistutilsFileError
, \
222 "couldn't move '%s' to '%s': %s" % (src
, dst
, msg
)
228 except os
.error
, (num
, msg
):
233 raise DistutilsFileError
, \
234 ("couldn't move '%s' to '%s' by copy/delete: " +
235 "delete '%s' failed: %s") % \
243 def write_file (filename
, contents
):
244 """Create a file with the specified name and write 'contents' (a
245 sequence of strings without line terminators) to it.
247 f
= open(filename
, "w")
248 for line
in contents
: