summaryrefslogtreecommitdiff
path: root/Lib/shutil.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/shutil.py')
-rw-r--r--Lib/shutil.py61
1 files changed, 37 insertions, 24 deletions
diff --git a/Lib/shutil.py b/Lib/shutil.py
index 5a4d4f6e79..d767a0c945 100644
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -36,20 +36,24 @@ __all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2",
"register_archive_format", "unregister_archive_format",
"get_unpack_formats", "register_unpack_format",
"unregister_unpack_format", "unpack_archive",
- "ignore_patterns", "chown", "which"]
+ "ignore_patterns", "chown", "which", "get_terminal_size",
+ "SameFileError"]
# disk_usage is added later, if available on the platform
-class Error(EnvironmentError):
+class Error(OSError):
pass
-class SpecialFileError(EnvironmentError):
+class SameFileError(Error):
+ """Raised when source and destination are the same file."""
+
+class SpecialFileError(OSError):
"""Raised when trying to do a kind of operation (e.g. copying) which is
not supported on a special file (e.g. a named pipe)"""
-class ExecError(EnvironmentError):
+class ExecError(OSError):
"""Raised when a command could not be executed"""
-class ReadError(EnvironmentError):
+class ReadError(OSError):
"""Raised when an archive cannot be read"""
class RegistryError(Exception):
@@ -57,11 +61,6 @@ class RegistryError(Exception):
and unpacking registeries fails"""
-try:
- WindowsError
-except NameError:
- WindowsError = None
-
def copyfileobj(fsrc, fdst, length=16*1024):
"""copy data from file-like object fsrc to file-like object fdst"""
while 1:
@@ -90,7 +89,7 @@ def copyfile(src, dst, *, follow_symlinks=True):
"""
if _samefile(src, dst):
- raise Error("`%s` and `%s` are the same file" % (src, dst))
+ raise SameFileError("{!r} and {!r} are the same file".format(src, dst))
for fn in [src, dst]:
try:
@@ -221,6 +220,9 @@ def copy(src, dst, *, follow_symlinks=True):
If follow_symlinks is false, symlinks won't be followed. This
resembles GNU's "cp -P src dst".
+ If source and destination are the same file, a SameFileError will be
+ raised.
+
"""
if os.path.isdir(dst):
dst = os.path.join(dst, os.path.basename(src))
@@ -319,7 +321,11 @@ def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2,
if not os.path.exists(linkto) and ignore_dangling_symlinks:
continue
# otherwise let the copy occurs. copy2 will raise an error
- copy_function(srcname, dstname)
+ if os.path.isdir(srcname):
+ copytree(srcname, dstname, symlinks, ignore,
+ copy_function)
+ else:
+ copy_function(srcname, dstname)
elif os.path.isdir(srcname):
copytree(srcname, dstname, symlinks, ignore, copy_function)
else:
@@ -329,15 +335,13 @@ def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2,
# continue with other files
except Error as err:
errors.extend(err.args[0])
- except EnvironmentError as why:
+ except OSError as why:
errors.append((srcname, dstname, str(why)))
try:
copystat(src, dst)
except OSError as why:
- if WindowsError is not None and isinstance(why, WindowsError):
- # Copying file access times may fail on Windows
- pass
- else:
+ # Copying file access times may fail on Windows
+ if getattr(why, 'winerror', None) is None:
errors.append((src, dst, str(why)))
if errors:
raise Error(errors)
@@ -356,24 +360,24 @@ def _rmtree_unsafe(path, onerror):
names = []
try:
names = os.listdir(path)
- except os.error:
+ except OSError:
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:
+ except OSError:
mode = 0
if stat.S_ISDIR(mode):
_rmtree_unsafe(fullname, onerror)
else:
try:
os.unlink(fullname)
- except os.error:
+ except OSError:
onerror(os.unlink, fullname, sys.exc_info())
try:
os.rmdir(path)
- except os.error:
+ except OSError:
onerror(os.rmdir, path, sys.exc_info())
# Version using fd-based APIs to protect against races
@@ -464,7 +468,7 @@ def rmtree(path, ignore_errors=False, onerror=None):
_rmtree_safe_fd(fd, path, onerror)
try:
os.rmdir(path)
- except os.error:
+ except OSError:
onerror(os.rmdir, path, sys.exc_info())
else:
try:
@@ -600,7 +604,7 @@ def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,
archive_name = base_name + '.tar' + compress_ext.get(compress, '')
archive_dir = os.path.dirname(archive_name)
- if not os.path.exists(archive_dir):
+ if archive_dir and not os.path.exists(archive_dir):
if logger is not None:
logger.info("creating %s", archive_dir)
if not dry_run:
@@ -660,7 +664,7 @@ def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None):
zip_filename = base_name + ".zip"
archive_dir = os.path.dirname(base_name)
- if not os.path.exists(archive_dir):
+ if archive_dir and not os.path.exists(archive_dir):
if logger is not None:
logger.info("creating %s", archive_dir)
if not dry_run:
@@ -683,7 +687,16 @@ def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None):
if not dry_run:
with zipfile.ZipFile(zip_filename, "w",
compression=zipfile.ZIP_DEFLATED) as zf:
+ path = os.path.normpath(base_dir)
+ zf.write(path, path)
+ if logger is not None:
+ logger.info("adding '%s'", path)
for dirpath, dirnames, filenames in os.walk(base_dir):
+ for name in sorted(dirnames):
+ path = os.path.normpath(os.path.join(dirpath, name))
+ zf.write(path, path)
+ if logger is not None:
+ logger.info("adding '%s'", path)
for name in filenames:
path = os.path.normpath(os.path.join(dirpath, name))
if os.path.isfile(path):