"""Utility functions for copying files and directory trees.
XXX The functions here don't copy the resource fork or other metadata on Mac.
from os
.path
import abspath
__all__
= ["copyfileobj","copyfile","copymode","copystat","copy","copy2",
"copytree","move","rmtree","Error"]
class Error(exceptions
.EnvironmentError):
def copyfileobj(fsrc
, fdst
, length
=16*1024):
"""copy data from file-like object fsrc to file-like object fdst"""
if hasattr(os
.path
,'samefile'):
return os
.path
.samefile(src
, dst
)
# All other platforms: check for same pathname.
return (os
.path
.normcase(os
.path
.abspath(src
)) ==
os
.path
.normcase(os
.path
.abspath(dst
)))
"""Copy data from src to dst"""
raise Error
, "`%s` and `%s` are the same file" % (src
, dst
)
"""Copy mode bits from src to dst"""
mode
= stat
.S_IMODE(st
.st_mode
)
"""Copy all stat info (mode bits, atime and mtime) from src to dst"""
mode
= stat
.S_IMODE(st
.st_mode
)
os
.utime(dst
, (st
.st_atime
, st
.st_mtime
))
"""Copy data and mode bits ("cp src dst").
The destination may be a directory.
dst
= os
.path
.join(dst
, os
.path
.basename(src
))
"""Copy data and all stat info ("cp -p src dst").
The destination may be a directory.
dst
= os
.path
.join(dst
, os
.path
.basename(src
))
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
XXX Consider this example code rather than the ultimate tool.
srcname
= os
.path
.join(src
, name
)
dstname
= os
.path
.join(dst
, name
)
if symlinks
and os
.path
.islink(srcname
):
linkto
= os
.readlink(srcname
)
os
.symlink(linkto
, dstname
)
elif os
.path
.isdir(srcname
):
copytree(srcname
, dstname
, symlinks
)
# XXX What about devices, sockets etc.?
except (IOError, os
.error
), why
:
errors
.append((srcname
, dstname
, why
))
# catch the Error from the recursive copytree so that we can
# continue with other files
errors
.extend(err
.args
[0])
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.
onerror(os
.listdir
, path
, sys
.exc_info())
fullname
= os
.path
.join(path
, name
)
mode
= os
.lstat(fullname
).st_mode
rmtree(fullname
, ignore_errors
, onerror
)
onerror(os
.remove
, fullname
, sys
.exc_info())
onerror(os
.rmdir
, path
, sys
.exc_info())
"""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.
raise Error
, "Cannot move a directory '%s' into itself '%s'." % (src
, dst
)
copytree(src
, dst
, symlinks
=True)
return abspath(dst
).startswith(abspath(src
))