Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(4966)

Side by Side Diff: Lib/shutil.py

Issue 15202: followlinks/follow_symlinks/symlinks flags unification.
Patch Set: Created 10 months, 3 weeks ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « Doc/library/shutil.rst ('k') | Lib/test/test_shutil.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
OLDNEW
« no previous file with comments | « Doc/library/shutil.rst ('k') | Lib/test/test_shutil.py » ('j') | no next file with comments »

RSS Feeds Recent Issues | This issue
This is Rietveld cbc36f91f3f7