| LEFT | RIGHT |
| (no file at all) | |
| 1 """Utility functions for copying and archiving files and directory trees. | 1 """Utility functions for copying and archiving files and directory trees. |
| 2 | 2 |
| 3 XXX The functions here don't copy the resource fork or other metadata on Mac. | 3 XXX The functions here don't copy the resource fork or other metadata on Mac. |
| 4 | 4 |
| 5 """ | 5 """ |
| 6 | 6 |
| 7 import os | 7 import os |
| 8 import sys | 8 import sys |
| 9 import stat | 9 import stat |
| 10 from os.path import abspath | 10 from os.path import abspath |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 75 if hasattr(os.path, 'samefile'): | 75 if hasattr(os.path, 'samefile'): |
| 76 try: | 76 try: |
| 77 return os.path.samefile(src, dst) | 77 return os.path.samefile(src, dst) |
| 78 except OSError: | 78 except OSError: |
| 79 return False | 79 return False |
| 80 | 80 |
| 81 # All other platforms: check for same pathname. | 81 # All other platforms: check for same pathname. |
| 82 return (os.path.normcase(os.path.abspath(src)) == | 82 return (os.path.normcase(os.path.abspath(src)) == |
| 83 os.path.normcase(os.path.abspath(dst))) | 83 os.path.normcase(os.path.abspath(dst))) |
| 84 | 84 |
| 85 def copyfile(src, dst, symlinks=False): | 85 def copyfile(src, dst, *, follow_symlinks=True): |
| 86 """Copy data from src to dst. | 86 """Copy data from src to dst. |
| 87 | 87 |
| 88 If optional flag `symlinks` is set and `src` is a symbolic link, a new | 88 If optional flag `follow_symlinks` is not set and `src` is a symbolic link,
a new |
| 89 symlink will be created instead of copying the file it points to. | 89 symlink will be created instead of copying the file it points to. |
| 90 | 90 |
| 91 """ | 91 """ |
| 92 if _samefile(src, dst): | 92 if _samefile(src, dst): |
| 93 raise Error("`%s` and `%s` are the same file" % (src, dst)) | 93 raise Error("`%s` and `%s` are the same file" % (src, dst)) |
| 94 | 94 |
| 95 for fn in [src, dst]: | 95 for fn in [src, dst]: |
| 96 try: | 96 try: |
| 97 st = os.stat(fn) | 97 st = os.stat(fn) |
| 98 except OSError: | 98 except OSError: |
| 99 # File most likely does not exist | 99 # File most likely does not exist |
| 100 pass | 100 pass |
| 101 else: | 101 else: |
| 102 # XXX What about other special files? (sockets, devices...) | 102 # XXX What about other special files? (sockets, devices...) |
| 103 if stat.S_ISFIFO(st.st_mode): | 103 if stat.S_ISFIFO(st.st_mode): |
| 104 raise SpecialFileError("`%s` is a named pipe" % fn) | 104 raise SpecialFileError("`%s` is a named pipe" % fn) |
| 105 | 105 |
| 106 if symlinks and os.path.islink(src): | 106 if not follow_symlinks and os.path.islink(src): |
| 107 os.symlink(os.readlink(src), dst) | 107 os.symlink(os.readlink(src), dst) |
| 108 else: | 108 else: |
| 109 with open(src, 'rb') as fsrc: | 109 with open(src, 'rb') as fsrc: |
| 110 with open(dst, 'wb') as fdst: | 110 with open(dst, 'wb') as fdst: |
| 111 copyfileobj(fsrc, fdst) | 111 copyfileobj(fsrc, fdst) |
| 112 return dst | 112 return dst |
| 113 | 113 |
| 114 def copymode(src, dst, symlinks=False): | 114 def copymode(src, dst, *, follow_symlinks=True): |
| 115 """Copy mode bits from src to dst. | 115 """Copy mode bits from src to dst. |
| 116 | 116 |
| 117 If the optional flag `symlinks` is set, symlinks aren't followed if and | 117 If the optional flag `follow_symlinks` is not set, symlinks aren't followed
if and |
| 118 only if both `src` and `dst` are symlinks. If `lchmod` isn't available (eg. | 118 only if both `src` and `dst` are symlinks. If `lchmod` isn't available (eg. |
| 119 Linux), in these cases, this method does nothing. | 119 Linux), in these cases, this method does nothing. |
| 120 | 120 |
| 121 """ | 121 """ |
| 122 if symlinks and os.path.islink(src) and os.path.islink(dst): | 122 if not follow_symlinks and os.path.islink(src) and os.path.islink(dst): |
| 123 if hasattr(os, 'lchmod'): | 123 if hasattr(os, 'lchmod'): |
| 124 stat_func, chmod_func = os.lstat, os.lchmod | 124 stat_func, chmod_func = os.lstat, os.lchmod |
| 125 else: | 125 else: |
| 126 return | 126 return |
| 127 elif hasattr(os, 'chmod'): | 127 elif hasattr(os, 'chmod'): |
| 128 stat_func, chmod_func = os.stat, os.chmod | 128 stat_func, chmod_func = os.stat, os.chmod |
| 129 else: | 129 else: |
| 130 return | 130 return |
| 131 | 131 |
| 132 st = stat_func(src) | 132 st = stat_func(src) |
| 133 chmod_func(dst, stat.S_IMODE(st.st_mode)) | 133 chmod_func(dst, stat.S_IMODE(st.st_mode)) |
| 134 | 134 |
| 135 def copystat(src, dst, symlinks=False): | 135 def copystat(src, dst, *, follow_symlinks=True): |
| 136 """Copy all stat info (mode bits, atime, mtime, flags) from src to dst. | 136 """Copy all stat info (mode bits, atime, mtime, flags) from src to dst. |
| 137 | 137 |
| 138 If the optional flag `symlinks` is set, symlinks aren't followed if and | 138 If the optional flag `follow_symlinks` is not set, symlinks aren't followed
if and |
| 139 only if both `src` and `dst` are symlinks. | 139 only if both `src` and `dst` are symlinks. |
| 140 | 140 |
| 141 """ | 141 """ |
| 142 def _nop(*args, ns=None, follow_symlinks=None): | 142 def _nop(*args, ns=None, follow_symlinks=None): |
| 143 pass | 143 pass |
| 144 | 144 |
| 145 # follow symlinks (aka don't not follow symlinks) | 145 # follow symlinks (aka don't not follow symlinks) |
| 146 follow = not (symlinks and os.path.islink(src) and os.path.islink(dst)) | 146 follow = follow_symlinks or not (os.path.islink(src) and os.path.islink(dst)
) |
| 147 if follow: | 147 if follow: |
| 148 # use the real function if it exists | 148 # use the real function if it exists |
| 149 def lookup(name): | 149 def lookup(name): |
| 150 return getattr(os, name, _nop) | 150 return getattr(os, name, _nop) |
| 151 else: | 151 else: |
| 152 # use the real function only if it exists | 152 # use the real function only if it exists |
| 153 # *and* it supports follow_symlinks | 153 # *and* it supports follow_symlinks |
| 154 def lookup(name): | 154 def lookup(name): |
| 155 fn = getattr(os, name, _nop) | 155 fn = getattr(os, name, _nop) |
| 156 if fn in os.supports_follow_symlinks: | 156 if fn in os.supports_follow_symlinks: |
| (...skipping 22 matching lines...) Expand all Loading... |
| 179 try: | 179 try: |
| 180 lookup("chflags")(dst, st.st_flags, follow_symlinks=follow) | 180 lookup("chflags")(dst, st.st_flags, follow_symlinks=follow) |
| 181 except OSError as why: | 181 except OSError as why: |
| 182 for err in 'EOPNOTSUPP', 'ENOTSUP': | 182 for err in 'EOPNOTSUPP', 'ENOTSUP': |
| 183 if hasattr(errno, err) and why.errno == getattr(errno, err): | 183 if hasattr(errno, err) and why.errno == getattr(errno, err): |
| 184 break | 184 break |
| 185 else: | 185 else: |
| 186 raise | 186 raise |
| 187 | 187 |
| 188 if hasattr(os, 'listxattr'): | 188 if hasattr(os, 'listxattr'): |
| 189 def _copyxattr(src, dst, symlinks=False): | 189 def _copyxattr(src, dst, *, follow_symlinks=True): |
| 190 """Copy extended filesystem attributes from `src` to `dst`. | 190 """Copy extended filesystem attributes from `src` to `dst`. |
| 191 | 191 |
| 192 Overwrite existing attributes. | 192 Overwrite existing attributes. |
| 193 | 193 |
| 194 If the optional flag `symlinks` is set, symlinks won't be followed. | 194 If the optional flag `follow_symlinks` is not set, symlinks won't be fol
lowed. |
| 195 | 195 |
| 196 """ | 196 """ |
| 197 | 197 |
| 198 for name in os.listxattr(src, follow_symlinks=symlinks): | 198 for name in os.listxattr(src, follow_symlinks=follow_symlinks): |
| 199 try: | 199 try: |
| 200 value = os.getxattr(src, name, follow_symlinks=symlinks) | 200 value = os.getxattr(src, name, follow_symlinks=follow_symlinks) |
| 201 os.setxattr(dst, name, value, follow_symlinks=symlinks) | 201 os.setxattr(dst, name, value, follow_symlinks=follow_symlinks) |
| 202 except OSError as e: | 202 except OSError as e: |
| 203 if e.errno not in (errno.EPERM, errno.ENOTSUP, errno.ENODATA): | 203 if e.errno not in (errno.EPERM, errno.ENOTSUP, errno.ENODATA): |
| 204 raise | 204 raise |
| 205 else: | 205 else: |
| 206 def _copyxattr(*args, **kwargs): | 206 def _copyxattr(*args, **kwargs): |
| 207 pass | 207 pass |
| 208 | 208 |
| 209 def copy(src, dst, symlinks=False): | 209 def copy(src, dst, *, follow_symlinks=True): |
| 210 """Copy data and mode bits ("cp src dst"). Return the file's destination. | 210 """Copy data and mode bits ("cp src dst"). Return the file's destination. |
| 211 | 211 |
| 212 The destination may be a directory. | 212 The destination may be a directory. |
| 213 | 213 |
| 214 If the optional flag `symlinks` is set, symlinks won't be followed. This | 214 If the optional flag `follow_symlinks` is not set, symlinks won't be followe
d. This |
| 215 resembles GNU's "cp -P src dst". | 215 resembles GNU's "cp -P src dst". |
| 216 | 216 |
| 217 """ | 217 """ |
| 218 if os.path.isdir(dst): | 218 if os.path.isdir(dst): |
| 219 dst = os.path.join(dst, os.path.basename(src)) | 219 dst = os.path.join(dst, os.path.basename(src)) |
| 220 copyfile(src, dst, symlinks=symlinks) | 220 copyfile(src, dst, follow_symlinks=follow_symlinks) |
| 221 copymode(src, dst, symlinks=symlinks) | 221 copymode(src, dst, follow_symlinks=follow_symlinks) |
| 222 return dst | 222 return dst |
| 223 | 223 |
| 224 def copy2(src, dst, symlinks=False): | 224 def copy2(src, dst, *, follow_symlinks=True): |
| 225 """Copy data and all stat info ("cp -p src dst"). Return the file's | 225 """Copy data and all stat info ("cp -p src dst"). Return the file's |
| 226 destination." | 226 destination." |
| 227 | 227 |
| 228 The destination may be a directory. | 228 The destination may be a directory. |
| 229 | 229 |
| 230 If the optional flag `symlinks` is set, symlinks won't be followed. This | 230 If the optional flag `follow_symlinks` is not set, symlinks won't be followe
d. This |
| 231 resembles GNU's "cp -P src dst". | 231 resembles GNU's "cp -P src dst". |
| 232 | 232 |
| 233 """ | 233 """ |
| 234 if os.path.isdir(dst): | 234 if os.path.isdir(dst): |
| 235 dst = os.path.join(dst, os.path.basename(src)) | 235 dst = os.path.join(dst, os.path.basename(src)) |
| 236 copyfile(src, dst, symlinks=symlinks) | 236 copyfile(src, dst, follow_symlinks=follow_symlinks) |
| 237 copystat(src, dst, symlinks=symlinks) | 237 copystat(src, dst, follow_symlinks=follow_symlinks) |
| 238 _copyxattr(src, dst, symlinks=symlinks) | 238 _copyxattr(src, dst, follow_symlinks=follow_symlinks) |
| 239 return dst | 239 return dst |
| 240 | 240 |
| 241 def ignore_patterns(*patterns): | 241 def ignore_patterns(*patterns): |
| 242 """Function that can be used as copytree() ignore parameter. | 242 """Function that can be used as copytree() ignore parameter. |
| 243 | 243 |
| 244 Patterns is a sequence of glob-style patterns | 244 Patterns is a sequence of glob-style patterns |
| 245 that are used to exclude files""" | 245 that are used to exclude files""" |
| 246 def _ignore_patterns(path, names): | 246 def _ignore_patterns(path, names): |
| 247 ignored_names = [] | 247 ignored_names = [] |
| 248 for pattern in patterns: | 248 for pattern in patterns: |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 300 srcname = os.path.join(src, name) | 300 srcname = os.path.join(src, name) |
| 301 dstname = os.path.join(dst, name) | 301 dstname = os.path.join(dst, name) |
| 302 try: | 302 try: |
| 303 if os.path.islink(srcname): | 303 if os.path.islink(srcname): |
| 304 linkto = os.readlink(srcname) | 304 linkto = os.readlink(srcname) |
| 305 if symlinks: | 305 if symlinks: |
| 306 # We can't just leave it to `copy_function` because legacy | 306 # We can't just leave it to `copy_function` because legacy |
| 307 # code with a custom `copy_function` may rely on copytree | 307 # code with a custom `copy_function` may rely on copytree |
| 308 # doing the right thing. | 308 # doing the right thing. |
| 309 os.symlink(linkto, dstname) | 309 os.symlink(linkto, dstname) |
| 310 copystat(srcname, dstname, symlinks=symlinks) | 310 copystat(srcname, dstname, follow_symlinks=not symlinks) |
| 311 else: | 311 else: |
| 312 # ignore dangling symlink if the flag is on | 312 # ignore dangling symlink if the flag is on |
| 313 if not os.path.exists(linkto) and ignore_dangling_symlinks: | 313 if not os.path.exists(linkto) and ignore_dangling_symlinks: |
| 314 continue | 314 continue |
| 315 # otherwise let the copy occurs. copy2 will raise an error | 315 # otherwise let the copy occurs. copy2 will raise an error |
| 316 copy_function(srcname, dstname) | 316 copy_function(srcname, dstname) |
| 317 elif os.path.isdir(srcname): | 317 elif os.path.isdir(srcname): |
| 318 copytree(srcname, dstname, symlinks, ignore, copy_function) | 318 copytree(srcname, dstname, symlinks, ignore, copy_function) |
| 319 else: | 319 else: |
| 320 # Will raise a SpecialFileError for unsupported file types | 320 # Will raise a SpecialFileError for unsupported file types |
| (...skipping 772 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1093 seen = set() | 1093 seen = set() |
| 1094 for dir in path: | 1094 for dir in path: |
| 1095 dir = os.path.normcase(dir) | 1095 dir = os.path.normcase(dir) |
| 1096 if not dir in seen: | 1096 if not dir in seen: |
| 1097 seen.add(dir) | 1097 seen.add(dir) |
| 1098 for thefile in files: | 1098 for thefile in files: |
| 1099 name = os.path.join(dir, thefile) | 1099 name = os.path.join(dir, thefile) |
| 1100 if _access_check(name, mode): | 1100 if _access_check(name, mode): |
| 1101 return name | 1101 return name |
| 1102 return None | 1102 return None |
| LEFT | RIGHT |