summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorg Brandl <georg@python.org>2007-03-21 09:10:29 +0000
committerGeorg Brandl <georg@python.org>2007-03-21 09:10:29 +0000
commitcae9f3d91609635374956e9ad71109bf1e90fa58 (patch)
treef49288c8da9c6356f2d84275222e3c50a904cdac
parent5cb76c19ba5b22b926f69d017a79eb2de296785a (diff)
downloadcpython-git-cae9f3d91609635374956e9ad71109bf1e90fa58.tar.gz
New test for rev. 54407 which only uses directories under TESTFN.
-rw-r--r--Doc/lib/libos.tex22
-rw-r--r--Lib/os.py10
-rw-r--r--Lib/test/test_os.py66
-rw-r--r--Misc/NEWS4
4 files changed, 70 insertions, 32 deletions
diff --git a/Doc/lib/libos.tex b/Doc/lib/libos.tex
index 1a39693b80..638ed6b6ed 100644
--- a/Doc/lib/libos.tex
+++ b/Doc/lib/libos.tex
@@ -1233,7 +1233,8 @@ Availability: Macintosh, \UNIX, Windows.
\end{funcdesc}
\begin{funcdesc}{walk}{top\optional{, topdown\code{=True}
- \optional{, onerror\code{=None}}}}
+ \optional{, onerror\code{=None}\optional{,
+ followlinks\code{=False}}}}}
\index{directory!walking}
\index{directory!traversal}
\function{walk()} generates the file names in a directory tree, by
@@ -1273,24 +1274,25 @@ report the error to continue with the walk, or raise the exception
to abort the walk. Note that the filename is available as the
\code{filename} attribute of the exception object.
+By default, \function{walk()} will not walk down into symbolic links that
+resolve to directories. Set \var{followlinks} to True to visit directories
+pointed to by symlinks, on systems that support them.
+
\versionadded[The \var{followlinks} parameter]{2.6}
\begin{notice}
+Be aware that setting \var{followlinks} to true can lead to infinite recursion
+if a link points to a parent directory of itself. \function{walk()} does not
+keep track of the directories it visited already.
+\end{notice}
+
+\begin{notice}
If you pass a relative pathname, don't change the current working
directory between resumptions of \function{walk()}. \function{walk()}
never changes the current directory, and assumes that its caller
doesn't either.
\end{notice}
-\begin{notice}
-On systems that support symbolic links, links to subdirectories appear
-in \var{dirnames} lists, but \function{walk()} will not visit them
-(infinite loops are hard to avoid when following symbolic links).
-To visit linked directories, you can identify them with
-\code{os.path.islink(\var{path})}, and invoke \code{walk(\var{path})}
-on each directly.
-\end{notice}
-
This example displays the number of bytes taken by non-directory files
in each directory under the starting directory, except that it doesn't
look under any CVS subdirectory:
diff --git a/Lib/os.py b/Lib/os.py
index c8cc274ec8..991716ed1f 100644
--- a/Lib/os.py
+++ b/Lib/os.py
@@ -221,7 +221,7 @@ def renames(old, new):
__all__.extend(["makedirs", "removedirs", "renames"])
-def walk(top, topdown=True, onerror=None):
+def walk(top, topdown=True, onerror=None, followlinks=False):
"""Directory tree generator.
For each directory in the directory tree rooted at top (including top
@@ -257,6 +257,10 @@ def walk(top, topdown=True, onerror=None):
to abort the walk. Note that the filename is available as the
filename attribute of the exception object.
+ By default, os.walk does not follow symbolic links to subdirectories on
+ systems that support them. In order to get this functionality, set the
+ optional argument 'followlinks' to true.
+
Caution: if you pass a relative pathname for top, don't change the
current working directory between resumptions of walk. walk never
changes the current directory, and assumes that the client doesn't
@@ -300,8 +304,8 @@ def walk(top, topdown=True, onerror=None):
yield top, dirs, nondirs
for name in dirs:
path = join(top, name)
- if not islink(path):
- for x in walk(path, topdown, onerror):
+ if followlinks or not islink(path):
+ for x in walk(path, topdown, onerror, followlinks):
yield x
if not topdown:
yield top, dirs, nondirs
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 9dcdb1808f..6d7fe8ec9b 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -272,65 +272,89 @@ class WalkTests(unittest.TestCase):
from os.path import join
# Build:
- # TESTFN/ a file kid and two directory kids
+ # TESTFN/
+ # TEST1/ a file kid and two directory kids
# tmp1
# SUB1/ a file kid and a directory kid
- # tmp2
- # SUB11/ no kids
- # SUB2/ just a file kid
- # tmp3
- sub1_path = join(test_support.TESTFN, "SUB1")
+ # tmp2
+ # SUB11/ no kids
+ # SUB2/ a file kid and a dirsymlink kid
+ # tmp3
+ # link/ a symlink to TESTFN.2
+ # TEST2/
+ # tmp4 a lone file
+ walk_path = join(test_support.TESTFN, "TEST1")
+ sub1_path = join(walk_path, "SUB1")
sub11_path = join(sub1_path, "SUB11")
- sub2_path = join(test_support.TESTFN, "SUB2")
- tmp1_path = join(test_support.TESTFN, "tmp1")
+ sub2_path = join(walk_path, "SUB2")
+ tmp1_path = join(walk_path, "tmp1")
tmp2_path = join(sub1_path, "tmp2")
tmp3_path = join(sub2_path, "tmp3")
+ link_path = join(sub2_path, "link")
+ t2_path = join(test_support.TESTFN, "TEST2")
+ tmp4_path = join(test_support.TESTFN, "TEST2", "tmp4")
# Create stuff.
os.makedirs(sub11_path)
os.makedirs(sub2_path)
- for path in tmp1_path, tmp2_path, tmp3_path:
+ os.makedirs(t2_path)
+ for path in tmp1_path, tmp2_path, tmp3_path, tmp4_path:
f = file(path, "w")
f.write("I'm " + path + " and proud of it. Blame test_os.\n")
f.close()
+ if hasattr(os, "symlink"):
+ os.symlink(os.path.abspath(t2_path), link_path)
+ else:
+ # it must be a directory because the test expects that
+ os.mkdir(link_path)
# Walk top-down.
- all = list(os.walk(test_support.TESTFN))
+ all = list(os.walk(walk_path))
self.assertEqual(len(all), 4)
# We can't know which order SUB1 and SUB2 will appear in.
# Not flipped: TESTFN, SUB1, SUB11, SUB2
# flipped: TESTFN, SUB2, SUB1, SUB11
flipped = all[0][1][0] != "SUB1"
all[0][1].sort()
- self.assertEqual(all[0], (test_support.TESTFN, ["SUB1", "SUB2"], ["tmp1"]))
+ self.assertEqual(all[0], (walk_path, ["SUB1", "SUB2"], ["tmp1"]))
self.assertEqual(all[1 + flipped], (sub1_path, ["SUB11"], ["tmp2"]))
self.assertEqual(all[2 + flipped], (sub11_path, [], []))
- self.assertEqual(all[3 - 2 * flipped], (sub2_path, [], ["tmp3"]))
+ self.assertEqual(all[3 - 2 * flipped], (sub2_path, ["link"], ["tmp3"]))
# Prune the search.
all = []
- for root, dirs, files in os.walk(test_support.TESTFN):
+ for root, dirs, files in os.walk(walk_path):
all.append((root, dirs, files))
# Don't descend into SUB1.
if 'SUB1' in dirs:
# Note that this also mutates the dirs we appended to all!
dirs.remove('SUB1')
self.assertEqual(len(all), 2)
- self.assertEqual(all[0], (test_support.TESTFN, ["SUB2"], ["tmp1"]))
- self.assertEqual(all[1], (sub2_path, [], ["tmp3"]))
+ self.assertEqual(all[0], (walk_path, ["SUB2"], ["tmp1"]))
+ self.assertEqual(all[1], (sub2_path, ["link"], ["tmp3"]))
# Walk bottom-up.
- all = list(os.walk(test_support.TESTFN, topdown=False))
+ all = list(os.walk(walk_path, topdown=False))
self.assertEqual(len(all), 4)
# We can't know which order SUB1 and SUB2 will appear in.
# Not flipped: SUB11, SUB1, SUB2, TESTFN
# flipped: SUB2, SUB11, SUB1, TESTFN
flipped = all[3][1][0] != "SUB1"
all[3][1].sort()
- self.assertEqual(all[3], (test_support.TESTFN, ["SUB1", "SUB2"], ["tmp1"]))
+ self.assertEqual(all[3], (walk_path, ["SUB1", "SUB2"], ["tmp1"]))
self.assertEqual(all[flipped], (sub11_path, [], []))
self.assertEqual(all[flipped + 1], (sub1_path, ["SUB11"], ["tmp2"]))
- self.assertEqual(all[2 - 2 * flipped], (sub2_path, [], ["tmp3"]))
+ self.assertEqual(all[2 - 2 * flipped], (sub2_path, ["link"], ["tmp3"]))
+
+ # Walk, following symlinks.
+ for root, dirs, files in os.walk(walk_path, followlinks=True):
+ if root == link_path:
+ self.assertEqual(dirs, [])
+ self.assertEqual(files, ["tmp4"])
+ break
+ else:
+ self.fail("Didn't follow symlink with followlinks=True")
+
# Tear everything down. This is a decent use for bottom-up on
# Windows, which doesn't have a recursive delete command. The
@@ -340,7 +364,11 @@ class WalkTests(unittest.TestCase):
for name in files:
os.remove(join(root, name))
for name in dirs:
- os.rmdir(join(root, name))
+ dirname = join(root, name)
+ if not os.path.islink(dirname):
+ os.rmdir(dirname)
+ else:
+ os.remove(dirname)
os.rmdir(test_support.TESTFN)
class MakedirTests (unittest.TestCase):
diff --git a/Misc/NEWS b/Misc/NEWS
index a9d05e059d..a2ec9a7e74 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -194,6 +194,10 @@ Library
- Patch #1630118: add a SpooledTemporaryFile class to tempfile.py.
+- Patch #1273829: os.walk() now has a "followlinks" parameter. If set to
+ True (which is not the default), it visits symlinks pointing to
+ directories.
+
- Bug #1681228: the webbrowser module now correctly uses the default
GNOME or KDE browser, depending on whether there is a session of one
of those present. Also, it tries the Windows default browser before