Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b403806

Browse files
committed
Issue #15202: Consistently use the name "follow_symlinks" for
new parameters in os and shutil functions. Patch by Serhiy Storchaka.
1 parent 509d87d commit b403806

7 files changed

Lines changed: 65 additions & 59 deletions

File tree

Doc/library/os.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2211,7 +2211,7 @@ features:
22112211
os.rmdir(os.path.join(root, name))
22122212

22132213

2214-
.. function:: fwalk(top='.', topdown=True, onerror=None, followlinks=False, *, dir_fd=None)
2214+
.. function:: fwalk(top='.', topdown=True, onerror=None, *, follow_symlinks=False, dir_fd=None)
22152215

22162216
.. index::
22172217
single: directory; walking
@@ -2224,7 +2224,9 @@ features:
22242224
and *dirfd* is a file descriptor referring to the directory *dirpath*.
22252225

22262226
This function always supports :ref:`paths relative to directory descriptors
2227-
<dir_fd>`.
2227+
<dir_fd>` and :ref:`not following symlinks <follow_symlinks>`. Note however
2228+
that, unlike other functions, the :funk:`fwalk` default value for
2229+
*follow_symlinks* is ``False``.
22282230

22292231
.. note::
22302232

Doc/library/shutil.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Directory and files operations
4747
be copied.
4848

4949

50-
.. function:: copyfile(src, dst, symlinks=False)
50+
.. function:: copyfile(src, dst, *, follow_symlinks=True)
5151

5252
Copy the contents (no metadata) of the file named *src* to a file named
5353
*dst* and return *dst*. *dst* must be the complete target file name; look at

Lib/os.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
424424

425425
if {open, stat} <= supports_dir_fd and {listdir, stat} <= supports_fd:
426426

427-
def fwalk(top=".", topdown=True, onerror=None, followlinks=False, *, dir_fd=None):
427+
def fwalk(top=".", topdown=True, onerror=None, *, follow_symlinks=False, dir_fd=None):
428428
"""Directory tree generator.
429429
430430
This behaves exactly like walk(), except that it yields a 4-tuple
@@ -435,7 +435,7 @@ def fwalk(top=".", topdown=True, onerror=None, followlinks=False, *, dir_fd=None
435435
and `dirfd` is a file descriptor referring to the directory `dirpath`.
436436
437437
The advantage of fwalk() over walk() is that it's safe against symlink
438-
races (when followlinks is False).
438+
races (when follow_symlinks is False).
439439
440440
If dir_fd is not None, it should be a file descriptor open to a directory,
441441
and top should be relative; top will then be relative to that directory.
@@ -462,13 +462,13 @@ def fwalk(top=".", topdown=True, onerror=None, followlinks=False, *, dir_fd=None
462462
orig_st = stat(top, follow_symlinks=False, dir_fd=dir_fd)
463463
topfd = open(top, O_RDONLY, dir_fd=dir_fd)
464464
try:
465-
if (followlinks or (st.S_ISDIR(orig_st.st_mode) and
466-
path.samestat(orig_st, stat(topfd)))):
467-
yield from _fwalk(topfd, top, topdown, onerror, followlinks)
465+
if (follow_symlinks or (st.S_ISDIR(orig_st.st_mode) and
466+
path.samestat(orig_st, stat(topfd)))):
467+
yield from _fwalk(topfd, top, topdown, onerror, follow_symlinks)
468468
finally:
469469
close(topfd)
470470

471-
def _fwalk(topfd, toppath, topdown, onerror, followlinks):
471+
def _fwalk(topfd, toppath, topdown, onerror, follow_symlinks):
472472
# Note: This uses O(depth of the directory tree) file descriptors: if
473473
# necessary, it can be adapted to only require O(1) FDs, see issue
474474
# #13734.
@@ -499,16 +499,16 @@ def _fwalk(topfd, toppath, topdown, onerror, followlinks):
499499

500500
for name in dirs:
501501
try:
502-
orig_st = stat(name, dir_fd=topfd, follow_symlinks=followlinks)
502+
orig_st = stat(name, dir_fd=topfd, follow_symlinks=follow_symlinks)
503503
dirfd = open(name, O_RDONLY, dir_fd=topfd)
504504
except error as err:
505505
if onerror is not None:
506506
onerror(err)
507507
return
508508
try:
509-
if followlinks or path.samestat(orig_st, stat(dirfd)):
509+
if follow_symlinks or path.samestat(orig_st, stat(dirfd)):
510510
dirpath = path.join(toppath, name)
511-
yield from _fwalk(dirfd, dirpath, topdown, onerror, followlinks)
511+
yield from _fwalk(dirfd, dirpath, topdown, onerror, follow_symlinks)
512512
finally:
513513
close(dirfd)
514514

Lib/shutil.py

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,10 @@ def _samefile(src, dst):
8282
return (os.path.normcase(os.path.abspath(src)) ==
8383
os.path.normcase(os.path.abspath(dst)))
8484

85-
def copyfile(src, dst, symlinks=False):
85+
def copyfile(src, dst, *, follow_symlinks=True):
8686
"""Copy data from src to dst.
8787
88-
If optional flag `symlinks` is set and `src` is a symbolic link, a new
88+
If follow_symlinks is not set and src is a symbolic link, a new
8989
symlink will be created instead of copying the file it points to.
9090
9191
"""
@@ -103,23 +103,23 @@ def copyfile(src, dst, symlinks=False):
103103
if stat.S_ISFIFO(st.st_mode):
104104
raise SpecialFileError("`%s` is a named pipe" % fn)
105105

106-
if symlinks and os.path.islink(src):
106+
if not follow_symlinks and os.path.islink(src):
107107
os.symlink(os.readlink(src), dst)
108108
else:
109109
with open(src, 'rb') as fsrc:
110110
with open(dst, 'wb') as fdst:
111111
copyfileobj(fsrc, fdst)
112112
return dst
113113

114-
def copymode(src, dst, symlinks=False):
114+
def copymode(src, dst, *, follow_symlinks=True):
115115
"""Copy mode bits from src to dst.
116116
117-
If the optional flag `symlinks` is set, symlinks aren't followed if and
118-
only if both `src` and `dst` are symlinks. If `lchmod` isn't available (eg.
119-
Linux), in these cases, this method does nothing.
117+
If follow_symlinks is not set, symlinks aren't followed if and only
118+
if both `src` and `dst` are symlinks. If `lchmod` isn't available
119+
(e.g. Linux) this method does nothing.
120120
121121
"""
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):
123123
if hasattr(os, 'lchmod'):
124124
stat_func, chmod_func = os.lstat, os.lchmod
125125
else:
@@ -133,38 +133,38 @@ def copymode(src, dst, symlinks=False):
133133
chmod_func(dst, stat.S_IMODE(st.st_mode))
134134

135135
if hasattr(os, 'listxattr'):
136-
def _copyxattr(src, dst, symlinks=False):
136+
def _copyxattr(src, dst, *, follow_symlinks=True):
137137
"""Copy extended filesystem attributes from `src` to `dst`.
138138
139139
Overwrite existing attributes.
140140
141-
If the optional flag `symlinks` is set, symlinks won't be followed.
141+
If `follow_symlinks` is false, symlinks won't be followed.
142142
143143
"""
144144

145-
for name in os.listxattr(src, follow_symlinks=symlinks):
145+
for name in os.listxattr(src, follow_symlinks=follow_symlinks):
146146
try:
147-
value = os.getxattr(src, name, follow_symlinks=symlinks)
148-
os.setxattr(dst, name, value, follow_symlinks=symlinks)
147+
value = os.getxattr(src, name, follow_symlinks=follow_symlinks)
148+
os.setxattr(dst, name, value, follow_symlinks=follow_symlinks)
149149
except OSError as e:
150150
if e.errno not in (errno.EPERM, errno.ENOTSUP, errno.ENODATA):
151151
raise
152152
else:
153153
def _copyxattr(*args, **kwargs):
154154
pass
155155

156-
def copystat(src, dst, symlinks=False):
156+
def copystat(src, dst, *, follow_symlinks=True):
157157
"""Copy all stat info (mode bits, atime, mtime, flags) from src to dst.
158158
159-
If the optional flag `symlinks` is set, symlinks aren't followed if and
159+
If the optional flag `follow_symlinks` is not set, symlinks aren't followed if and
160160
only if both `src` and `dst` are symlinks.
161161
162162
"""
163163
def _nop(*args, ns=None, follow_symlinks=None):
164164
pass
165165

166166
# follow symlinks (aka don't not follow symlinks)
167-
follow = not (symlinks and os.path.islink(src) and os.path.islink(dst))
167+
follow = follow_symlinks or not (os.path.islink(src) and os.path.islink(dst))
168168
if follow:
169169
# use the real function if it exists
170170
def lookup(name):
@@ -205,37 +205,37 @@ def lookup(name):
205205
break
206206
else:
207207
raise
208-
_copyxattr(src, dst, symlinks=follow)
208+
_copyxattr(src, dst, follow_symlinks=follow)
209209

210-
def copy(src, dst, symlinks=False):
210+
def copy(src, dst, *, follow_symlinks=True):
211211
"""Copy data and mode bits ("cp src dst"). Return the file's destination.
212212
213213
The destination may be a directory.
214214
215-
If the optional flag `symlinks` is set, symlinks won't be followed. This
215+
If follow_symlinks is false, symlinks won't be followed. This
216216
resembles GNU's "cp -P src dst".
217217
218218
"""
219219
if os.path.isdir(dst):
220220
dst = os.path.join(dst, os.path.basename(src))
221-
copyfile(src, dst, symlinks=symlinks)
222-
copymode(src, dst, symlinks=symlinks)
221+
copyfile(src, dst, follow_symlinks=follow_symlinks)
222+
copymode(src, dst, follow_symlinks=follow_symlinks)
223223
return dst
224224

225-
def copy2(src, dst, symlinks=False):
225+
def copy2(src, dst, *, follow_symlinks=True):
226226
"""Copy data and all stat info ("cp -p src dst"). Return the file's
227227
destination."
228228
229229
The destination may be a directory.
230230
231-
If the optional flag `symlinks` is set, symlinks won't be followed. This
231+
If follow_symlinks is false, symlinks won't be followed. This
232232
resembles GNU's "cp -P src dst".
233233
234234
"""
235235
if os.path.isdir(dst):
236236
dst = os.path.join(dst, os.path.basename(src))
237-
copyfile(src, dst, symlinks=symlinks)
238-
copystat(src, dst, symlinks=symlinks)
237+
copyfile(src, dst, follow_symlinks=follow_symlinks)
238+
copystat(src, dst, follow_symlinks=follow_symlinks)
239239
return dst
240240

241241
def ignore_patterns(*patterns):
@@ -307,7 +307,7 @@ def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2,
307307
# code with a custom `copy_function` may rely on copytree
308308
# doing the right thing.
309309
os.symlink(linkto, dstname)
310-
copystat(srcname, dstname, symlinks=symlinks)
310+
copystat(srcname, dstname, follow_symlinks=not symlinks)
311311
else:
312312
# ignore dangling symlink if the flag is on
313313
if not os.path.exists(linkto) and ignore_dangling_symlinks:

Lib/test/test_os.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -758,10 +758,11 @@ def _compare_to_walk(self, walk_kwargs, fwalk_kwargs):
758758
"""
759759
compare with walk() results.
760760
"""
761-
for topdown, followlinks in itertools.product((True, False), repeat=2):
762-
d = {'topdown': topdown, 'followlinks': followlinks}
763-
walk_kwargs.update(d)
764-
fwalk_kwargs.update(d)
761+
walk_kwargs = walk_kwargs.copy()
762+
fwalk_kwargs = fwalk_kwargs.copy()
763+
for topdown, follow_symlinks in itertools.product((True, False), repeat=2):
764+
walk_kwargs.update(topdown=topdown, followlinks=follow_symlinks)
765+
fwalk_kwargs.update(topdown=topdown, follow_symlinks=follow_symlinks)
765766

766767
expected = {}
767768
for root, dirs, files in os.walk(**walk_kwargs):
@@ -787,9 +788,9 @@ def test_dir_fd(self):
787788

788789
def test_yields_correct_dir_fd(self):
789790
# check returned file descriptors
790-
for topdown, followlinks in itertools.product((True, False), repeat=2):
791-
args = support.TESTFN, topdown, None, followlinks
792-
for root, dirs, files, rootfd in os.fwalk(*args):
791+
for topdown, follow_symlinks in itertools.product((True, False), repeat=2):
792+
args = support.TESTFN, topdown, None
793+
for root, dirs, files, rootfd in os.fwalk(*args, follow_symlinks=follow_symlinks):
793794
# check that the FD is valid
794795
os.fstat(rootfd)
795796
# redundant check

Lib/test/test_shutil.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -277,17 +277,17 @@ def test_copymode_symlink_to_symlink(self):
277277
os.lchmod(src_link, stat.S_IRWXO|stat.S_IRWXG)
278278
# link to link
279279
os.lchmod(dst_link, stat.S_IRWXO)
280-
shutil.copymode(src_link, dst_link, symlinks=True)
280+
shutil.copymode(src_link, dst_link, follow_symlinks=False)
281281
self.assertEqual(os.lstat(src_link).st_mode,
282282
os.lstat(dst_link).st_mode)
283283
self.assertNotEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
284284
# src link - use chmod
285285
os.lchmod(dst_link, stat.S_IRWXO)
286-
shutil.copymode(src_link, dst, symlinks=True)
286+
shutil.copymode(src_link, dst, follow_symlinks=False)
287287
self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
288288
# dst link - use chmod
289289
os.lchmod(dst_link, stat.S_IRWXO)
290-
shutil.copymode(src, dst_link, symlinks=True)
290+
shutil.copymode(src, dst_link, follow_symlinks=False)
291291
self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
292292

293293
@unittest.skipIf(hasattr(os, 'lchmod'), 'requires os.lchmod to be missing')
@@ -302,7 +302,7 @@ def test_copymode_symlink_to_symlink_wo_lchmod(self):
302302
write_file(dst, 'foo')
303303
os.symlink(src, src_link)
304304
os.symlink(dst, dst_link)
305-
shutil.copymode(src_link, dst_link, symlinks=True) # silent fail
305+
shutil.copymode(src_link, dst_link, follow_symlinks=False) # silent fail
306306

307307
@support.skip_unless_symlink
308308
def test_copystat_symlinks(self):
@@ -326,10 +326,10 @@ def test_copystat_symlinks(self):
326326
src_link_stat = os.lstat(src_link)
327327
# follow
328328
if hasattr(os, 'lchmod'):
329-
shutil.copystat(src_link, dst_link, symlinks=False)
329+
shutil.copystat(src_link, dst_link, follow_symlinks=True)
330330
self.assertNotEqual(src_link_stat.st_mode, os.stat(dst).st_mode)
331331
# don't follow
332-
shutil.copystat(src_link, dst_link, symlinks=True)
332+
shutil.copystat(src_link, dst_link, follow_symlinks=False)
333333
dst_link_stat = os.lstat(dst_link)
334334
if os.utime in os.supports_follow_symlinks:
335335
for attr in 'st_atime', 'st_mtime':
@@ -341,7 +341,7 @@ def test_copystat_symlinks(self):
341341
if hasattr(os, 'lchflags') and hasattr(src_link_stat, 'st_flags'):
342342
self.assertEqual(src_link_stat.st_flags, dst_link_stat.st_flags)
343343
# tell to follow but dst is not a link
344-
shutil.copystat(src_link, dst, symlinks=True)
344+
shutil.copystat(src_link, dst, follow_symlinks=False)
345345
self.assertTrue(abs(os.stat(src).st_mtime - os.stat(dst).st_mtime) <
346346
00000.1)
347347

@@ -439,10 +439,10 @@ def test_copyxattr_symlinks(self):
439439
dst_link = os.path.join(tmp_dir, 'qux')
440440
write_file(dst, 'bar')
441441
os.symlink(dst, dst_link)
442-
shutil._copyxattr(src_link, dst_link, symlinks=True)
442+
shutil._copyxattr(src_link, dst_link, follow_symlinks=False)
443443
self.assertEqual(os.getxattr(dst_link, 'trusted.foo', follow_symlinks=False), b'43')
444444
self.assertRaises(OSError, os.getxattr, dst, 'trusted.foo')
445-
shutil._copyxattr(src_link, dst, symlinks=True)
445+
shutil._copyxattr(src_link, dst, follow_symlinks=False)
446446
self.assertEqual(os.getxattr(dst, 'trusted.foo'), b'43')
447447

448448
@support.skip_unless_symlink
@@ -456,12 +456,12 @@ def test_copy_symlinks(self):
456456
if hasattr(os, 'lchmod'):
457457
os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
458458
# don't follow
459-
shutil.copy(src_link, dst, symlinks=False)
459+
shutil.copy(src_link, dst, follow_symlinks=True)
460460
self.assertFalse(os.path.islink(dst))
461461
self.assertEqual(read_file(src), read_file(dst))
462462
os.remove(dst)
463463
# follow
464-
shutil.copy(src_link, dst, symlinks=True)
464+
shutil.copy(src_link, dst, follow_symlinks=False)
465465
self.assertTrue(os.path.islink(dst))
466466
self.assertEqual(os.readlink(dst), os.readlink(src_link))
467467
if hasattr(os, 'lchmod'):
@@ -483,12 +483,12 @@ def test_copy2_symlinks(self):
483483
src_stat = os.stat(src)
484484
src_link_stat = os.lstat(src_link)
485485
# follow
486-
shutil.copy2(src_link, dst, symlinks=False)
486+
shutil.copy2(src_link, dst, follow_symlinks=True)
487487
self.assertFalse(os.path.islink(dst))
488488
self.assertEqual(read_file(src), read_file(dst))
489489
os.remove(dst)
490490
# don't follow
491-
shutil.copy2(src_link, dst, symlinks=True)
491+
shutil.copy2(src_link, dst, follow_symlinks=False)
492492
self.assertTrue(os.path.islink(dst))
493493
self.assertEqual(os.readlink(dst), os.readlink(src_link))
494494
dst_stat = os.lstat(dst)
@@ -526,7 +526,7 @@ def test_copyfile_symlinks(self):
526526
write_file(src, 'foo')
527527
os.symlink(src, link)
528528
# don't follow
529-
shutil.copyfile(link, dst_link, symlinks=True)
529+
shutil.copyfile(link, dst_link, follow_symlinks=False)
530530
self.assertTrue(os.path.islink(dst_link))
531531
self.assertEqual(os.readlink(link), os.readlink(dst_link))
532532
# follow

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ What's New in Python 3.3.0 Beta 2?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #15202: Consistently use the name "follow_symlinks" for
14+
new parameters in os and shutil functions.
15+
1316
- Issue #15314: __main__.__loader__ is now set correctly during
1417
interpreter startup
1518

0 commit comments

Comments
 (0)