Kaydet (Commit) ae882f79 authored tarafından Johannes Gijsbers's avatar Johannes Gijsbers

Patch #941486: add os.path.lexists(). Also fix bug #940578 by using lexists in glob.glob.

üst d3f61a2d
...@@ -21,6 +21,7 @@ which must be a string containing a path specification. ...@@ -21,6 +21,7 @@ which must be a string containing a path specification.
\var{pathname} can be either absolute (like \var{pathname} can be either absolute (like
\file{/usr/src/Python-1.5/Makefile}) or relative (like \file{/usr/src/Python-1.5/Makefile}) or relative (like
\file{../../Tools/*/*.gif}), and can contain shell-style wildcards. \file{../../Tools/*/*.gif}), and can contain shell-style wildcards.
Broken symlinks are included in the results (as in the shell).
\end{funcdesc} \end{funcdesc}
For example, consider a directory containing only the following files: For example, consider a directory containing only the following files:
......
...@@ -43,6 +43,15 @@ half of the pair returned by \code{split(\var{path})}. ...@@ -43,6 +43,15 @@ half of the pair returned by \code{split(\var{path})}.
\begin{funcdesc}{exists}{path} \begin{funcdesc}{exists}{path}
Return \code{True} if \var{path} refers to an existing path. Return \code{True} if \var{path} refers to an existing path.
Returns \code{False} for broken symbolic links.
\end{funcdesc}
\begin{funcdesc}{lexists}{path}
Return \code{True} if \var{path} refers to an existing path.
Returns \code{True} for broken symbolic links.
Equivalent to \function{exists()} on platforms lacking
\function{os.lstat()}.
\versionadded{2.4}
\end{funcdesc} \end{funcdesc}
\begin{funcdesc}{expanduser}{path} \begin{funcdesc}{expanduser}{path}
......
...@@ -13,7 +13,7 @@ def glob(pathname): ...@@ -13,7 +13,7 @@ def glob(pathname):
""" """
if not has_magic(pathname): if not has_magic(pathname):
if os.path.exists(pathname): if os.path.lexists(pathname):
return [pathname] return [pathname]
else: else:
return [] return []
...@@ -29,7 +29,7 @@ def glob(pathname): ...@@ -29,7 +29,7 @@ def glob(pathname):
for dirname in list: for dirname in list:
if basename or os.path.isdir(dirname): if basename or os.path.isdir(dirname):
name = os.path.join(dirname, basename) name = os.path.join(dirname, basename)
if os.path.exists(name): if os.path.lexists(name):
result.append(name) result.append(name)
else: else:
result = [] result = []
......
...@@ -150,7 +150,7 @@ def getctime(filename): ...@@ -150,7 +150,7 @@ def getctime(filename):
return os.stat(filename).st_ctime return os.stat(filename).st_ctime
def exists(s): def exists(s):
"""Return True if the pathname refers to an existing file or directory.""" """Test whether a path exists. Returns False for broken symbolic links"""
try: try:
st = os.stat(s) st = os.stat(s)
...@@ -158,6 +158,18 @@ def exists(s): ...@@ -158,6 +158,18 @@ def exists(s):
return False return False
return True return True
# Is `stat`/`lstat` a meaningful difference on the Mac? This is safe in any
# case.
def lexists(path):
"""Test whether a path exists. Returns True for broken symbolic links"""
try:
st = os.lstat(path)
except os.error:
return False
return True
# Return the longest prefix of all list elements. # Return the longest prefix of all list elements.
def commonprefix(m): def commonprefix(m):
......
...@@ -249,7 +249,6 @@ def islink(path): ...@@ -249,7 +249,6 @@ def islink(path):
# Does a path exist? # Does a path exist?
# This is false for dangling symbolic links.
def exists(path): def exists(path):
"""Test whether a path exists""" """Test whether a path exists"""
...@@ -259,6 +258,8 @@ def exists(path): ...@@ -259,6 +258,8 @@ def exists(path):
return False return False
return True return True
lexists = exists
# Is a path a dos directory? # Is a path a dos directory?
# This follows symbolic links, so both islink() and isdir() can be true # This follows symbolic links, so both islink() and isdir() can be true
......
...@@ -220,6 +220,8 @@ def exists(path): ...@@ -220,6 +220,8 @@ def exists(path):
return False return False
return True return True
lexists = exists
# Is a path a directory? # Is a path a directory?
......
...@@ -218,6 +218,8 @@ def exists(p): ...@@ -218,6 +218,8 @@ def exists(p):
except swi.error: except swi.error:
return 0 return 0
lexists = exists
def isdir(p): def isdir(p):
""" """
......
...@@ -174,6 +174,17 @@ def exists(path): ...@@ -174,6 +174,17 @@ def exists(path):
return True return True
# Being true for dangling symbolic links is also useful.
def lexists(path):
"""Test whether a path exists. Returns True for broken symbolic links"""
try:
st = os.lstat(path)
except os.error:
return False
return True
# Is a path a directory? # Is a path a directory?
# This follows symbolic links, so both islink() and isdir() can be true # This follows symbolic links, so both islink() and isdir() can be true
# for the same path. # for the same path.
......
...@@ -48,6 +48,9 @@ class GlobTests(unittest.TestCase): ...@@ -48,6 +48,9 @@ class GlobTests(unittest.TestCase):
self.mktemp('ZZZ') self.mktemp('ZZZ')
self.mktemp('a', 'bcd', 'EF') self.mktemp('a', 'bcd', 'EF')
self.mktemp('a', 'bcd', 'efg', 'ha') self.mktemp('a', 'bcd', 'efg', 'ha')
if hasattr(os, 'symlink'):
os.symlink(self.norm('broken'), self.norm('sym1'))
os.symlink(self.norm('broken'), self.norm('sym2'))
def tearDown(self): def tearDown(self):
deltree(self.tempdir) deltree(self.tempdir)
...@@ -98,6 +101,13 @@ class GlobTests(unittest.TestCase): ...@@ -98,6 +101,13 @@ class GlobTests(unittest.TestCase):
eq(self.glob('?a?', '*F'), map(self.norm, [os.path.join('aaa', 'zzzF'), eq(self.glob('?a?', '*F'), map(self.norm, [os.path.join('aaa', 'zzzF'),
os.path.join('aab', 'F')])) os.path.join('aab', 'F')]))
def test_glob_broken_symlinks(self):
if hasattr(os, 'symlink'):
eq = self.assertSequencesEqual_noorder
eq(self.glob('sym*'), [self.norm('sym1'), self.norm('sym2')])
eq(self.glob('sym1'), [self.norm('sym1')])
eq(self.glob('sym2'), [self.norm('sym2')])
def test_main(): def test_main():
run_unittest(GlobTests) run_unittest(GlobTests)
......
...@@ -150,6 +150,7 @@ class PosixPathTest(unittest.TestCase): ...@@ -150,6 +150,7 @@ class PosixPathTest(unittest.TestCase):
os.remove(test_support.TESTFN + "1") os.remove(test_support.TESTFN + "1")
self.assertIs(posixpath.islink(test_support.TESTFN + "2"), True) self.assertIs(posixpath.islink(test_support.TESTFN + "2"), True)
self.assertIs(posixpath.exists(test_support.TESTFN + "2"), False) self.assertIs(posixpath.exists(test_support.TESTFN + "2"), False)
self.assertIs(posixpath.lexists(test_support.TESTFN + "2"), True)
finally: finally:
if not f.close(): if not f.close():
f.close() f.close()
...@@ -171,6 +172,7 @@ class PosixPathTest(unittest.TestCase): ...@@ -171,6 +172,7 @@ class PosixPathTest(unittest.TestCase):
f.write("foo") f.write("foo")
f.close() f.close()
self.assertIs(posixpath.exists(test_support.TESTFN), True) self.assertIs(posixpath.exists(test_support.TESTFN), True)
self.assertIs(posixpath.lexists(test_support.TESTFN), True)
finally: finally:
if not f.close(): if not f.close():
f.close() f.close()
......
...@@ -79,6 +79,9 @@ Extension modules ...@@ -79,6 +79,9 @@ Extension modules
Library Library
------- -------
- Patch #941486: added os.path.lexists(), which returns True for broken
symlinks, unlike os.path.exists().
- the random module now uses os.urandom() for seeding if it is available. - the random module now uses os.urandom() for seeding if it is available.
Added a new generator based on os.urandom(). Added a new generator based on os.urandom().
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment