+++ /dev/null
-"""distutils.file_util\r
-\r
-Utility functions for operating on single files.\r
-"""\r
-\r
-__revision__ = "$Id$"\r
-\r
-import os\r
-from distutils.errors import DistutilsFileError\r
-from distutils import log\r
-\r
-# for generating verbose output in 'copy_file()'\r
-_copy_action = {None: 'copying',\r
- 'hard': 'hard linking',\r
- 'sym': 'symbolically linking'}\r
-\r
-\r
-def _copy_file_contents(src, dst, buffer_size=16*1024):\r
- """Copy the file 'src' to 'dst'.\r
-\r
- Both must be filenames. Any error opening either file, reading from\r
- 'src', or writing to 'dst', raises DistutilsFileError. Data is\r
- read/written in chunks of 'buffer_size' bytes (default 16k). No attempt\r
- is made to handle anything apart from regular files.\r
- """\r
- # Stolen from shutil module in the standard library, but with\r
- # custom error-handling added.\r
- fsrc = None\r
- fdst = None\r
- try:\r
- try:\r
- fsrc = open(src, 'rb')\r
- except os.error, (errno, errstr):\r
- raise DistutilsFileError("could not open '%s': %s" % (src, errstr))\r
-\r
- if os.path.exists(dst):\r
- try:\r
- os.unlink(dst)\r
- except os.error, (errno, errstr):\r
- raise DistutilsFileError(\r
- "could not delete '%s': %s" % (dst, errstr))\r
-\r
- try:\r
- fdst = open(dst, 'wb')\r
- except os.error, (errno, errstr):\r
- raise DistutilsFileError(\r
- "could not create '%s': %s" % (dst, errstr))\r
-\r
- while 1:\r
- try:\r
- buf = fsrc.read(buffer_size)\r
- except os.error, (errno, errstr):\r
- raise DistutilsFileError(\r
- "could not read from '%s': %s" % (src, errstr))\r
-\r
- if not buf:\r
- break\r
-\r
- try:\r
- fdst.write(buf)\r
- except os.error, (errno, errstr):\r
- raise DistutilsFileError(\r
- "could not write to '%s': %s" % (dst, errstr))\r
-\r
- finally:\r
- if fdst:\r
- fdst.close()\r
- if fsrc:\r
- fsrc.close()\r
-\r
-def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0,\r
- link=None, verbose=1, dry_run=0):\r
- """Copy a file 'src' to 'dst'.\r
-\r
- If 'dst' is a directory, then 'src' is copied there with the same name;\r
- otherwise, it must be a filename. (If the file exists, it will be\r
- ruthlessly clobbered.) If 'preserve_mode' is true (the default),\r
- the file's mode (type and permission bits, or whatever is analogous on\r
- the current platform) is copied. If 'preserve_times' is true (the\r
- default), the last-modified and last-access times are copied as well.\r
- If 'update' is true, 'src' will only be copied if 'dst' does not exist,\r
- or if 'dst' does exist but is older than 'src'.\r
-\r
- 'link' allows you to make hard links (os.link) or symbolic links\r
- (os.symlink) instead of copying: set it to "hard" or "sym"; if it is\r
- None (the default), files are copied. Don't set 'link' on systems that\r
- don't support it: 'copy_file()' doesn't check if hard or symbolic\r
- linking is available.\r
-\r
- Under Mac OS, uses the native file copy function in macostools; on\r
- other systems, uses '_copy_file_contents()' to copy file contents.\r
-\r
- Return a tuple (dest_name, copied): 'dest_name' is the actual name of\r
- the output file, and 'copied' is true if the file was copied (or would\r
- have been copied, if 'dry_run' true).\r
- """\r
- # XXX if the destination file already exists, we clobber it if\r
- # copying, but blow up if linking. Hmmm. And I don't know what\r
- # macostools.copyfile() does. Should definitely be consistent, and\r
- # should probably blow up if destination exists and we would be\r
- # changing it (ie. it's not already a hard/soft link to src OR\r
- # (not update) and (src newer than dst).\r
-\r
- from distutils.dep_util import newer\r
- from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE\r
-\r
- if not os.path.isfile(src):\r
- raise DistutilsFileError(\r
- "can't copy '%s': doesn't exist or not a regular file" % src)\r
-\r
- if os.path.isdir(dst):\r
- dir = dst\r
- dst = os.path.join(dst, os.path.basename(src))\r
- else:\r
- dir = os.path.dirname(dst)\r
-\r
- if update and not newer(src, dst):\r
- if verbose >= 1:\r
- log.debug("not copying %s (output up-to-date)", src)\r
- return dst, 0\r
-\r
- try:\r
- action = _copy_action[link]\r
- except KeyError:\r
- raise ValueError("invalid value '%s' for 'link' argument" % link)\r
-\r
- if verbose >= 1:\r
- if os.path.basename(dst) == os.path.basename(src):\r
- log.info("%s %s -> %s", action, src, dir)\r
- else:\r
- log.info("%s %s -> %s", action, src, dst)\r
-\r
- if dry_run:\r
- return (dst, 1)\r
-\r
- # If linking (hard or symbolic), use the appropriate system call\r
- # (Unix only, of course, but that's the caller's responsibility)\r
- if link == 'hard':\r
- if not (os.path.exists(dst) and os.path.samefile(src, dst)):\r
- os.link(src, dst)\r
- elif link == 'sym':\r
- if not (os.path.exists(dst) and os.path.samefile(src, dst)):\r
- os.symlink(src, dst)\r
-\r
- # Otherwise (non-Mac, not linking), copy the file contents and\r
- # (optionally) copy the times and mode.\r
- else:\r
- _copy_file_contents(src, dst)\r
- if preserve_mode or preserve_times:\r
- st = os.stat(src)\r
-\r
- # According to David Ascher <da@ski.org>, utime() should be done\r
- # before chmod() (at least under NT).\r
- if preserve_times:\r
- os.utime(dst, (st[ST_ATIME], st[ST_MTIME]))\r
- if preserve_mode:\r
- os.chmod(dst, S_IMODE(st[ST_MODE]))\r
-\r
- return (dst, 1)\r
-\r
-# XXX I suspect this is Unix-specific -- need porting help!\r
-def move_file (src, dst, verbose=1, dry_run=0):\r
- """Move a file 'src' to 'dst'.\r
-\r
- If 'dst' is a directory, the file will be moved into it with the same\r
- name; otherwise, 'src' is just renamed to 'dst'. Return the new\r
- full name of the file.\r
-\r
- Handles cross-device moves on Unix using 'copy_file()'. What about\r
- other systems???\r
- """\r
- from os.path import exists, isfile, isdir, basename, dirname\r
- import errno\r
-\r
- if verbose >= 1:\r
- log.info("moving %s -> %s", src, dst)\r
-\r
- if dry_run:\r
- return dst\r
-\r
- if not isfile(src):\r
- raise DistutilsFileError("can't move '%s': not a regular file" % src)\r
-\r
- if isdir(dst):\r
- dst = os.path.join(dst, basename(src))\r
- elif exists(dst):\r
- raise DistutilsFileError(\r
- "can't move '%s': destination '%s' already exists" %\r
- (src, dst))\r
-\r
- if not isdir(dirname(dst)):\r
- raise DistutilsFileError(\r
- "can't move '%s': destination '%s' not a valid path" % \\r
- (src, dst))\r
-\r
- copy_it = 0\r
- try:\r
- os.rename(src, dst)\r
- except os.error, (num, msg):\r
- if num == errno.EXDEV:\r
- copy_it = 1\r
- else:\r
- raise DistutilsFileError(\r
- "couldn't move '%s' to '%s': %s" % (src, dst, msg))\r
-\r
- if copy_it:\r
- copy_file(src, dst, verbose=verbose)\r
- try:\r
- os.unlink(src)\r
- except os.error, (num, msg):\r
- try:\r
- os.unlink(dst)\r
- except os.error:\r
- pass\r
- raise DistutilsFileError(\r
- ("couldn't move '%s' to '%s' by copy/delete: " +\r
- "delete '%s' failed: %s") %\r
- (src, dst, src, msg))\r
- return dst\r
-\r
-\r
-def write_file (filename, contents):\r
- """Create a file with the specified name and write 'contents' (a\r
- sequence of strings without line terminators) to it.\r
- """\r
- f = open(filename, "w")\r
- try:\r
- for line in contents:\r
- f.write(line + "\n")\r
- finally:\r
- f.close()\r