Path: blob/main/test/lib/python3.9/site-packages/setuptools/_distutils/file_util.py
4799 views
"""distutils.file_util12Utility functions for operating on single files.3"""45import os6from distutils.errors import DistutilsFileError7from distutils import log89# for generating verbose output in 'copy_file()'10_copy_action = { None: 'copying',11'hard': 'hard linking',12'sym': 'symbolically linking' }131415def _copy_file_contents(src, dst, buffer_size=16*1024):16"""Copy the file 'src' to 'dst'; both must be filenames. Any error17opening either file, reading from 'src', or writing to 'dst', raises18DistutilsFileError. Data is read/written in chunks of 'buffer_size'19bytes (default 16k). No attempt is made to handle anything apart from20regular files.21"""22# Stolen from shutil module in the standard library, but with23# custom error-handling added.24fsrc = None25fdst = None26try:27try:28fsrc = open(src, 'rb')29except OSError as e:30raise DistutilsFileError("could not open '%s': %s" % (src, e.strerror))3132if os.path.exists(dst):33try:34os.unlink(dst)35except OSError as e:36raise DistutilsFileError(37"could not delete '%s': %s" % (dst, e.strerror))3839try:40fdst = open(dst, 'wb')41except OSError as e:42raise DistutilsFileError(43"could not create '%s': %s" % (dst, e.strerror))4445while True:46try:47buf = fsrc.read(buffer_size)48except OSError as e:49raise DistutilsFileError(50"could not read from '%s': %s" % (src, e.strerror))5152if not buf:53break5455try:56fdst.write(buf)57except OSError as e:58raise DistutilsFileError(59"could not write to '%s': %s" % (dst, e.strerror))60finally:61if fdst:62fdst.close()63if fsrc:64fsrc.close()6566def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0,67link=None, verbose=1, dry_run=0):68"""Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is69copied there with the same name; otherwise, it must be a filename. (If70the file exists, it will be ruthlessly clobbered.) If 'preserve_mode'71is true (the default), the file's mode (type and permission bits, or72whatever is analogous on the current platform) is copied. If73'preserve_times' is true (the default), the last-modified and74last-access times are copied as well. If 'update' is true, 'src' will75only be copied if 'dst' does not exist, or if 'dst' does exist but is76older than 'src'.7778'link' allows you to make hard links (os.link) or symbolic links79(os.symlink) instead of copying: set it to "hard" or "sym"; if it is80None (the default), files are copied. Don't set 'link' on systems that81don't support it: 'copy_file()' doesn't check if hard or symbolic82linking is available. If hardlink fails, falls back to83_copy_file_contents().8485Under Mac OS, uses the native file copy function in macostools; on86other systems, uses '_copy_file_contents()' to copy file contents.8788Return a tuple (dest_name, copied): 'dest_name' is the actual name of89the output file, and 'copied' is true if the file was copied (or would90have been copied, if 'dry_run' true).91"""92# XXX if the destination file already exists, we clobber it if93# copying, but blow up if linking. Hmmm. And I don't know what94# macostools.copyfile() does. Should definitely be consistent, and95# should probably blow up if destination exists and we would be96# changing it (ie. it's not already a hard/soft link to src OR97# (not update) and (src newer than dst).9899from distutils.dep_util import newer100from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE101102if not os.path.isfile(src):103raise DistutilsFileError(104"can't copy '%s': doesn't exist or not a regular file" % src)105106if os.path.isdir(dst):107dir = dst108dst = os.path.join(dst, os.path.basename(src))109else:110dir = os.path.dirname(dst)111112if update and not newer(src, dst):113if verbose >= 1:114log.debug("not copying %s (output up-to-date)", src)115return (dst, 0)116117try:118action = _copy_action[link]119except KeyError:120raise ValueError("invalid value '%s' for 'link' argument" % link)121122if verbose >= 1:123if os.path.basename(dst) == os.path.basename(src):124log.info("%s %s -> %s", action, src, dir)125else:126log.info("%s %s -> %s", action, src, dst)127128if dry_run:129return (dst, 1)130131# If linking (hard or symbolic), use the appropriate system call132# (Unix only, of course, but that's the caller's responsibility)133elif link == 'hard':134if not (os.path.exists(dst) and os.path.samefile(src, dst)):135try:136os.link(src, dst)137return (dst, 1)138except OSError:139# If hard linking fails, fall back on copying file140# (some special filesystems don't support hard linking141# even under Unix, see issue #8876).142pass143elif link == 'sym':144if not (os.path.exists(dst) and os.path.samefile(src, dst)):145os.symlink(src, dst)146return (dst, 1)147148# Otherwise (non-Mac, not linking), copy the file contents and149# (optionally) copy the times and mode.150_copy_file_contents(src, dst)151if preserve_mode or preserve_times:152st = os.stat(src)153154# According to David Ascher <[email protected]>, utime() should be done155# before chmod() (at least under NT).156if preserve_times:157os.utime(dst, (st[ST_ATIME], st[ST_MTIME]))158if preserve_mode:159os.chmod(dst, S_IMODE(st[ST_MODE]))160161return (dst, 1)162163164# XXX I suspect this is Unix-specific -- need porting help!165def move_file (src, dst,166verbose=1,167dry_run=0):168169"""Move a file 'src' to 'dst'. If 'dst' is a directory, the file will170be moved into it with the same name; otherwise, 'src' is just renamed171to 'dst'. Return the new full name of the file.172173Handles cross-device moves on Unix using 'copy_file()'. What about174other systems???175"""176from os.path import exists, isfile, isdir, basename, dirname177import errno178179if verbose >= 1:180log.info("moving %s -> %s", src, dst)181182if dry_run:183return dst184185if not isfile(src):186raise DistutilsFileError("can't move '%s': not a regular file" % src)187188if isdir(dst):189dst = os.path.join(dst, basename(src))190elif exists(dst):191raise DistutilsFileError(192"can't move '%s': destination '%s' already exists" %193(src, dst))194195if not isdir(dirname(dst)):196raise DistutilsFileError(197"can't move '%s': destination '%s' not a valid path" %198(src, dst))199200copy_it = False201try:202os.rename(src, dst)203except OSError as e:204(num, msg) = e.args205if num == errno.EXDEV:206copy_it = True207else:208raise DistutilsFileError(209"couldn't move '%s' to '%s': %s" % (src, dst, msg))210211if copy_it:212copy_file(src, dst, verbose=verbose)213try:214os.unlink(src)215except OSError as e:216(num, msg) = e.args217try:218os.unlink(dst)219except OSError:220pass221raise DistutilsFileError(222"couldn't move '%s' to '%s' by copy/delete: "223"delete '%s' failed: %s"224% (src, dst, src, msg))225return dst226227228def write_file (filename, contents):229"""Create a file with the specified name and write 'contents' (a230sequence of strings without line terminators) to it.231"""232f = open(filename, "w")233try:234for line in contents:235f.write(line + "\n")236finally:237f.close()238239240