cpython/Lib/shutil.py
Christian Heimes 9bd667ad03 Merged revisions 60124-60142 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r60131 | georg.brandl | 2008-01-20 12:13:29 +0100 (Sun, 20 Jan 2008) | 3 lines

  #1351692: in pprint, always call format() for dict and list items to enable
  custom formatting of contents via subclassing PrettyPrinter.
........
  r60133 | georg.brandl | 2008-01-20 12:43:03 +0100 (Sun, 20 Jan 2008) | 2 lines

  #1178141: add addinfourl.code to get http status code from urllib.
........
  r60134 | georg.brandl | 2008-01-20 13:05:43 +0100 (Sun, 20 Jan 2008) | 4 lines

  #856047: respect the ``no_proxy`` env var when checking for proxies
  in urllib and using the other ``_proxy`` env vars.
  Original patch by Donovan Baarda.
........
  r60135 | georg.brandl | 2008-01-20 13:18:17 +0100 (Sun, 20 Jan 2008) | 4 lines

  #1664522: in urllib, don't read non-existing directories in ftp mode,
  returning a 0-byte file -- raise an IOError instead.
  Original patch from Phil Knirsch.
........
  r60136 | georg.brandl | 2008-01-20 13:57:47 +0100 (Sun, 20 Jan 2008) | 2 lines

  #799369: document possible sys.platform values.
........
  r60137 | georg.brandl | 2008-01-20 14:08:37 +0100 (Sun, 20 Jan 2008) | 2 lines

  #652749: document the constants added to the builtins by site.py.
........
  r60138 | georg.brandl | 2008-01-20 14:59:46 +0100 (Sun, 20 Jan 2008) | 2 lines

  #1648: add sys.gettrace() and sys.getprofile().
........
  r60139 | georg.brandl | 2008-01-20 15:17:42 +0100 (Sun, 20 Jan 2008) | 2 lines

  #1669: don't allow shutil.rmtree() to be called on a symlink.
........
  r60140 | georg.brandl | 2008-01-20 15:20:02 +0100 (Sun, 20 Jan 2008) | 2 lines

  Fix test_pyclbr after urllib change.
........
  r60141 | christian.heimes | 2008-01-20 15:28:28 +0100 (Sun, 20 Jan 2008) | 1 line

  Fixed a wrong assumption in configure.in and Include/pyport.h. The is finite function is not called isfinite() but finite(). Sorry, my fault. :)
........
  r60142 | georg.brandl | 2008-01-20 15:31:27 +0100 (Sun, 20 Jan 2008) | 2 lines

  #1876: fix typos in test_operator.
........
2008-01-20 15:14:11 +00:00

214 lines
6.2 KiB
Python

"""Utility functions for copying files and directory trees.
XXX The functions here don't copy the resource fork or other metadata on Mac.
"""
import os
import sys
import stat
from os.path import abspath
__all__ = ["copyfileobj","copyfile","copymode","copystat","copy","copy2",
"copytree","move","rmtree","Error"]
class Error(EnvironmentError):
pass
def copyfileobj(fsrc, fdst, length=16*1024):
"""copy data from file-like object fsrc to file-like object fdst"""
while 1:
buf = fsrc.read(length)
if not buf:
break
fdst.write(buf)
def _samefile(src, dst):
# Macintosh, Unix.
if hasattr(os.path,'samefile'):
try:
return os.path.samefile(src, dst)
except OSError:
return False
# All other platforms: check for same pathname.
return (os.path.normcase(os.path.abspath(src)) ==
os.path.normcase(os.path.abspath(dst)))
def copyfile(src, dst):
"""Copy data from src to dst"""
if _samefile(src, dst):
raise Error("`%s` and `%s` are the same file" % (src, dst))
fsrc = None
fdst = None
try:
fsrc = open(src, 'rb')
fdst = open(dst, 'wb')
copyfileobj(fsrc, fdst)
finally:
if fdst:
fdst.close()
if fsrc:
fsrc.close()
def copymode(src, dst):
"""Copy mode bits from src to dst"""
if hasattr(os, 'chmod'):
st = os.stat(src)
mode = stat.S_IMODE(st.st_mode)
os.chmod(dst, mode)
def copystat(src, dst):
"""Copy all stat info (mode bits, atime, mtime, flags) from src to dst"""
st = os.stat(src)
mode = stat.S_IMODE(st.st_mode)
if hasattr(os, 'utime'):
os.utime(dst, (st.st_atime, st.st_mtime))
if hasattr(os, 'chmod'):
os.chmod(dst, mode)
if hasattr(os, 'chflags') and hasattr(st, 'st_flags'):
os.chflags(dst, st.st_flags)
def copy(src, dst):
"""Copy data and mode bits ("cp src dst").
The destination may be a directory.
"""
if os.path.isdir(dst):
dst = os.path.join(dst, os.path.basename(src))
copyfile(src, dst)
copymode(src, dst)
def copy2(src, dst):
"""Copy data and all stat info ("cp -p src dst").
The destination may be a directory.
"""
if os.path.isdir(dst):
dst = os.path.join(dst, os.path.basename(src))
copyfile(src, dst)
copystat(src, dst)
def copytree(src, dst, symlinks=False):
"""Recursively copy a directory tree using copy2().
The destination directory must not already exist.
If exception(s) occur, an Error is raised with a list of reasons.
If the optional symlinks flag is true, symbolic links in the
source tree result in symbolic links in the destination tree; if
it is false, the contents of the files pointed to by symbolic
links are copied.
XXX Consider this example code rather than the ultimate tool.
"""
names = os.listdir(src)
os.makedirs(dst)
errors = []
for name in names:
srcname = os.path.join(src, name)
dstname = os.path.join(dst, name)
try:
if symlinks and os.path.islink(srcname):
linkto = os.readlink(srcname)
os.symlink(linkto, dstname)
elif os.path.isdir(srcname):
copytree(srcname, dstname, symlinks)
else:
copy2(srcname, dstname)
# XXX What about devices, sockets etc.?
except (IOError, os.error) as why:
errors.append((srcname, dstname, str(why)))
# catch the Error from the recursive copytree so that we can
# continue with other files
except Error as err:
errors.extend(err.args[0])
try:
copystat(src, dst)
except WindowsError:
# can't copy file access times on Windows
pass
except OSError as why:
errors.extend((src, dst, str(why)))
if errors:
raise Error(errors)
def rmtree(path, ignore_errors=False, onerror=None):
"""Recursively delete a directory tree.
If ignore_errors is set, errors are ignored; otherwise, if onerror
is set, it is called to handle the error with arguments (func,
path, exc_info) where func is os.listdir, os.remove, or os.rmdir;
path is the argument to that function that caused it to fail; and
exc_info is a tuple returned by sys.exc_info(). If ignore_errors
is false and onerror is None, an exception is raised.
"""
if ignore_errors:
def onerror(*args):
pass
elif onerror is None:
def onerror(*args):
raise
try:
if os.path.islink(path):
# symlinks to directories are forbidden, see bug #1669
raise OSError("Cannot call rmtree on a symbolic link")
except OSError:
onerror(os.path.islink, path, sys.exc_info())
# can't continue even if onerror hook returns
return
names = []
try:
names = os.listdir(path)
except os.error as err:
onerror(os.listdir, path, sys.exc_info())
for name in names:
fullname = os.path.join(path, name)
try:
mode = os.lstat(fullname).st_mode
except os.error:
mode = 0
if stat.S_ISDIR(mode):
rmtree(fullname, ignore_errors, onerror)
else:
try:
os.remove(fullname)
except os.error as err:
onerror(os.remove, fullname, sys.exc_info())
try:
os.rmdir(path)
except os.error:
onerror(os.rmdir, path, sys.exc_info())
def move(src, dst):
"""Recursively move a file or directory to another location.
If the destination is on our current filesystem, then simply use
rename. Otherwise, copy src to the dst and then remove src.
A lot more could be done here... A look at a mv.c shows a lot of
the issues this implementation glosses over.
"""
try:
os.rename(src, dst)
except OSError:
if os.path.isdir(src):
if destinsrc(src, dst):
raise Error("Cannot move a directory '%s' into itself '%s'." % (src, dst))
copytree(src, dst, symlinks=True)
rmtree(src)
else:
copy2(src,dst)
os.unlink(src)
def destinsrc(src, dst):
return abspath(dst).startswith(abspath(src))