diff options
author | Larry Hastings <larry@hastings.org> | 2012-06-22 23:30:09 (GMT) |
---|---|---|
committer | Larry Hastings <larry@hastings.org> | 2012-06-22 23:30:09 (GMT) |
commit | 9cf065cfdc4245ea7e31edcb2e6ede0cea47d148 (patch) | |
tree | 22d8450865a023586034d555ae72e3753f95e84c /Lib/shutil.py | |
parent | f0f4742b495554238d1204ce0002c1ef1ba23507 (diff) | |
download | cpython-9cf065cfdc4245ea7e31edcb2e6ede0cea47d148.zip cpython-9cf065cfdc4245ea7e31edcb2e6ede0cea47d148.tar.gz cpython-9cf065cfdc4245ea7e31edcb2e6ede0cea47d148.tar.bz2 |
Issue #14626: Large refactoring of functions / parameters in the os module.
Many functions now support "dir_fd" and "follow_symlinks" parameters;
some also support accepting an open file descriptor in place of of a path
string. Added os.support_* collections as LBYL helpers. Removed many
functions only previously seen in 3.3 alpha releases (often starting with
"f" or "l", or ending with "at"). Originally suggested by Serhiy Storchaka;
implemented by Larry Hastings.
Diffstat (limited to 'Lib/shutil.py')
-rw-r--r-- | Lib/shutil.py | 63 |
1 files changed, 36 insertions, 27 deletions
diff --git a/Lib/shutil.py b/Lib/shutil.py index 8da46d1..401e9ea 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -139,27 +139,45 @@ def copystat(src, dst, symlinks=False): only if both `src` and `dst` are symlinks. """ - def _nop(*args, ns=None): + def _nop(*args, ns=None, follow_symlinks=None): pass - if symlinks and os.path.islink(src) and os.path.islink(dst): - stat_func = os.lstat - utime_func = os.lutimes if hasattr(os, 'lutimes') else _nop - chmod_func = os.lchmod if hasattr(os, 'lchmod') else _nop - chflags_func = os.lchflags if hasattr(os, 'lchflags') else _nop + # follow symlinks (aka don't not follow symlinks) + follow = not (symlinks and os.path.islink(src) and os.path.islink(dst)) + if follow: + # use the real function if it exists + def lookup(name): + return getattr(os, name, _nop) else: - stat_func = os.stat - utime_func = os.utime if hasattr(os, 'utime') else _nop - chmod_func = os.chmod if hasattr(os, 'chmod') else _nop - chflags_func = os.chflags if hasattr(os, 'chflags') else _nop - - st = stat_func(src) + # use the real function only if it exists + # *and* it supports follow_symlinks + def lookup(name): + fn = getattr(os, name, _nop) + if fn in os.supports_follow_symlinks: + return fn + return _nop + + st = lookup("stat")(src, follow_symlinks=follow) mode = stat.S_IMODE(st.st_mode) - utime_func(dst, ns=(st.st_atime_ns, st.st_mtime_ns)) - chmod_func(dst, mode) + lookup("utime")(dst, ns=(st.st_atime_ns, st.st_mtime_ns), + follow_symlinks=follow) + try: + lookup("chmod")(dst, mode, follow_symlinks=follow) + except NotImplementedError: + # if we got a NotImplementedError, it's because + # * follow_symlinks=False, + # * lchown() is unavailable, and + # * either + # * fchownat() is unvailable or + # * fchownat() doesn't implement AT_SYMLINK_NOFOLLOW. + # (it returned ENOSUP.) + # therefore we're out of options--we simply cannot chown the + # symlink. give up, suppress the error. + # (which is what shutil always did in this circumstance.) + pass if hasattr(st, 'st_flags'): try: - chflags_func(dst, st.st_flags) + lookup("chflags")(dst, st.st_flags, follow_symlinks=follow) except OSError as why: for err in 'EOPNOTSUPP', 'ENOTSUP': if hasattr(errno, err) and why.errno == getattr(errno, err): @@ -176,20 +194,11 @@ if hasattr(os, 'listxattr'): If the optional flag `symlinks` is set, symlinks won't be followed. """ - if symlinks: - listxattr = os.llistxattr - removexattr = os.lremovexattr - setxattr = os.lsetxattr - getxattr = os.lgetxattr - else: - listxattr = os.listxattr - removexattr = os.removexattr - setxattr = os.setxattr - getxattr = os.getxattr - for attr in listxattr(src): + for name in os.listxattr(src, follow_symlinks=symlinks): try: - setxattr(dst, attr, getxattr(src, attr)) + value = os.getxattr(src, name, follow_symlinks=symlinks) + os.setxattr(dst, name, value, follow_symlinks=symlinks) except OSError as e: if e.errno not in (errno.EPERM, errno.ENOTSUP, errno.ENODATA): raise |