mirror of
https://github.com/python/cpython.git
synced 2025-01-14 04:25:54 +08:00
2ee470f7f9
svn+ssh://pythondev@svn.python.org/python/trunk ........ r64722 | georg.brandl | 2008-07-05 12:13:36 +0200 (Sat, 05 Jul 2008) | 4 lines #2663: support an *ignore* argument to shutil.copytree(). Patch by Tarek Ziade. This is a new feature, but Barry authorized adding it in the beta period. ........ r64729 | mark.dickinson | 2008-07-05 13:33:52 +0200 (Sat, 05 Jul 2008) | 5 lines Issue 3188: accept float('infinity') as well as float('inf'). This makes the float constructor behave in the same way as specified by various other language standards, including C99, IEEE 754r, and the IBM Decimal standard. ........ r64753 | gregory.p.smith | 2008-07-06 05:35:58 +0200 (Sun, 06 Jul 2008) | 4 lines - Issue #2862: Make int and float freelist management consistent with other freelists. Changes their CompactFreeList apis into ClearFreeList apis and calls them via gc.collect(). ........ r64845 | raymond.hettinger | 2008-07-10 16:03:19 +0200 (Thu, 10 Jul 2008) | 1 line Issue 3301: Bisect functions behaved badly when lo was negative. ........ r64846 | raymond.hettinger | 2008-07-10 16:34:57 +0200 (Thu, 10 Jul 2008) | 1 line Issue 3285: Fractions from_float() and from_decimal() accept Integral arguments. ........ r64849 | andrew.kuchling | 2008-07-10 16:43:31 +0200 (Thu, 10 Jul 2008) | 1 line Wording changes ........ r64871 | raymond.hettinger | 2008-07-11 14:00:21 +0200 (Fri, 11 Jul 2008) | 1 line Add cautionary note on the use of PySequence_Fast_ITEMS. ........ r64880 | amaury.forgeotdarc | 2008-07-11 23:28:25 +0200 (Fri, 11 Jul 2008) | 5 lines #3317 in zipfile module, restore the previous names of global variables: some applications relied on them. Also remove duplicated lines. ........ r64881 | amaury.forgeotdarc | 2008-07-11 23:45:06 +0200 (Fri, 11 Jul 2008) | 3 lines #3342: In tracebacks, printed source lines were not indented since r62555. #3343: Py_DisplaySourceLine should be a private function. Rename it to _Py_DisplaySourceLine. ........ r64882 | josiah.carlson | 2008-07-12 00:17:14 +0200 (Sat, 12 Jul 2008) | 2 lines Fix for the AttributeError in test_asynchat. ........ r64885 | josiah.carlson | 2008-07-12 01:26:59 +0200 (Sat, 12 Jul 2008) | 2 lines Fixed test for asyncore. ........ r64888 | matthias.klose | 2008-07-12 09:51:48 +0200 (Sat, 12 Jul 2008) | 2 lines - Fix bashisms in Tools/faqwiz/move-faqwiz.sh ........ r64897 | benjamin.peterson | 2008-07-12 22:16:19 +0200 (Sat, 12 Jul 2008) | 1 line fix various doc typos #3320 ........ r64900 | alexandre.vassalotti | 2008-07-13 00:06:53 +0200 (Sun, 13 Jul 2008) | 2 lines Fixed typo. ........ r64901 | benjamin.peterson | 2008-07-13 01:41:19 +0200 (Sun, 13 Jul 2008) | 1 line #1778443 robotparser fixes from Aristotelis Mikropoulos ........ r64915 | nick.coghlan | 2008-07-13 16:52:36 +0200 (Sun, 13 Jul 2008) | 1 line Fix issue 3221 by emitting a RuntimeWarning instead of raising SystemError when the parent module can't be found during an absolute import (likely due to non-PEP 361 aware code which sets a module level __package__ attribute) ........ r64926 | martin.v.loewis | 2008-07-13 22:31:49 +0200 (Sun, 13 Jul 2008) | 2 lines Add turtle into the module index. ........ r64927 | alexandre.vassalotti | 2008-07-13 22:42:44 +0200 (Sun, 13 Jul 2008) | 3 lines Issue #3274: Use a less common identifier for the temporary variable in Py_CLEAR(). ........ r64928 | andrew.kuchling | 2008-07-13 23:43:25 +0200 (Sun, 13 Jul 2008) | 1 line Re-word ........ r64929 | andrew.kuchling | 2008-07-13 23:43:52 +0200 (Sun, 13 Jul 2008) | 1 line Add various items; move ctypes items into a subsection of their own ........ r64938 | andrew.kuchling | 2008-07-14 02:35:32 +0200 (Mon, 14 Jul 2008) | 1 line Typo fixes ........ r64939 | andrew.kuchling | 2008-07-14 02:40:55 +0200 (Mon, 14 Jul 2008) | 1 line Typo fix ........ r64940 | andrew.kuchling | 2008-07-14 03:18:16 +0200 (Mon, 14 Jul 2008) | 1 line Typo fix ........ r64941 | andrew.kuchling | 2008-07-14 03:18:31 +0200 (Mon, 14 Jul 2008) | 1 line Expand the multiprocessing section ........ r64944 | gregory.p.smith | 2008-07-14 08:06:48 +0200 (Mon, 14 Jul 2008) | 7 lines Fix posix.fork1() / os.fork1() to only call PyOS_AfterFork() in the child process rather than both parent and child. Does anyone actually use fork1()? It appears to be a Solaris thing but if Python is built with pthreads on Solaris, fork1() and fork() should be the same. ........ r64961 | jesse.noller | 2008-07-15 15:47:33 +0200 (Tue, 15 Jul 2008) | 1 line multiprocessing/connection.py patch to remove fqdn oddness for issue 3270 ........ r64966 | nick.coghlan | 2008-07-15 17:40:22 +0200 (Tue, 15 Jul 2008) | 1 line Add missing NEWS entry for r64962 ........ r64973 | jesse.noller | 2008-07-15 20:29:18 +0200 (Tue, 15 Jul 2008) | 1 line Revert 3270 patch: self._address is in pretty widespread use, need to revisit ........
263 lines
8.1 KiB
Python
263 lines
8.1 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
|
|
import fnmatch
|
|
|
|
__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 ignore_patterns(*patterns):
|
|
"""Function that can be used as copytree() ignore parameter.
|
|
|
|
Patterns is a sequence of glob-style patterns
|
|
that are used to exclude files"""
|
|
def _ignore_patterns(path, names):
|
|
ignored_names = []
|
|
for pattern in patterns:
|
|
ignored_names.extend(fnmatch.filter(names, pattern))
|
|
return set(ignored_names)
|
|
return _ignore_patterns
|
|
|
|
def copytree(src, dst, symlinks=False, ignore=None):
|
|
"""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.
|
|
|
|
The optional ignore argument is a callable. If given, it
|
|
is called with the `src` parameter, which is the directory
|
|
being visited by copytree(), and `names` which is the list of
|
|
`src` contents, as returned by os.listdir():
|
|
|
|
callable(src, names) -> ignored_names
|
|
|
|
Since copytree() is called recursively, the callable will be
|
|
called once for each directory that is copied. It returns a
|
|
list of names relative to the `src` directory that should
|
|
not be copied.
|
|
|
|
XXX Consider this example code rather than the ultimate tool.
|
|
|
|
"""
|
|
names = os.listdir(src)
|
|
if ignore is not None:
|
|
ignored_names = ignore(src, names)
|
|
else:
|
|
ignored_names = set()
|
|
|
|
os.makedirs(dst)
|
|
errors = []
|
|
for name in names:
|
|
if name in ignored_names:
|
|
continue
|
|
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, ignore)
|
|
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 _basename(path):
|
|
# A basename() variant which first strips the trailing slash, if present.
|
|
# Thus we always get the last component of the path, even for directories.
|
|
return os.path.basename(path.rstrip(os.path.sep))
|
|
|
|
def move(src, dst):
|
|
"""Recursively move a file or directory to another location. This is
|
|
similar to the Unix "mv" command.
|
|
|
|
If the destination is a directory or a symlink to a directory, the source
|
|
is moved inside the directory. The destination path must not already
|
|
exist.
|
|
|
|
If the destination already exists but is not a directory, it may be
|
|
overwritten depending on os.rename() semantics.
|
|
|
|
If the destination is on our current filesystem, then rename() is used.
|
|
Otherwise, src is copied to the destination and then removed.
|
|
A lot more could be done here... A look at a mv.c shows a lot of
|
|
the issues this implementation glosses over.
|
|
|
|
"""
|
|
real_dst = dst
|
|
if os.path.isdir(dst):
|
|
real_dst = os.path.join(dst, _basename(src))
|
|
if os.path.exists(real_dst):
|
|
raise Error("Destination path '%s' already exists" % real_dst)
|
|
try:
|
|
os.rename(src, real_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, real_dst, symlinks=True)
|
|
rmtree(src)
|
|
else:
|
|
copy2(src, real_dst)
|
|
os.unlink(src)
|
|
|
|
def destinsrc(src, dst):
|
|
return abspath(dst).startswith(abspath(src))
|