Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
33021c5
bpo-37834: Enable os.readlink and os.path.is_link to recognize app ex…
zooba Aug 12, 2019
6b5d22d
Add reparse_tag to stat structure and add test
zooba Aug 12, 2019
658a355
Revert readlink change and only traverse actual symlinks
zooba Aug 12, 2019
75d039e
Treat junctions as links. Update tests
zooba Aug 13, 2019
3a64166
Fix NEWS item
zooba Aug 13, 2019
ec43378
Revert getfinalpathname change, add test fixes until bpo-9949 is fixed
zooba Aug 13, 2019
ebcbca2
Add link target resolution and revert test changes
zooba Aug 13, 2019
1aba497
Revert shutil test
zooba Aug 13, 2019
b65f06f
[WIP] bpo-9949: Enable symlink traversal for ntpath.realpath
zooba Aug 14, 2019
df54868
Fixes link resolution and test file creation
zooba Aug 14, 2019
f82129b
Fix x-plat handling in test_ntpath and additional error code
zooba Aug 14, 2019
8ee4046
Fix use of os.path in ntpath tests
zooba Aug 14, 2019
b93c9e8
Fix additional uses of os.path
zooba Aug 14, 2019
5f9816f
Validate that the path prefix is unnecessary before removing
zooba Aug 14, 2019
d1d0726
Don't need second call to _getfinalpathname
zooba Aug 14, 2019
c178e45
Rewrite Windows stat() implementation to let the OS traverse symlinks…
zooba Aug 14, 2019
8ed0cf6
Fix st_mode when unable to read path
zooba Aug 14, 2019
863e8c4
Fix some uninitialized values. Remove bad changes
zooba Aug 14, 2019
9bc4192
Fix test discovery mocks
zooba Aug 14, 2019
541dcf8
Remove get_target_path and fix tests
zooba Aug 15, 2019
f2d2e31
Fix special handling for non-traversable reparse points
zooba Aug 15, 2019
d93caf7
Merge branch 'bpo-37834' into bpo-9949-37834
zooba Aug 15, 2019
9dc0608
Avoid traversing a symlink cycle
zooba Aug 15, 2019
7bc2568
Merge branch 'bpo-9949' into bpo-9949-37834
zooba Aug 15, 2019
2f84d8c
Treat all unfollowable links equally.
zooba Aug 16, 2019
6139abf
Merge branch 'bpo-37834' into bpo-9949-37834
zooba Aug 16, 2019
4cb450e
Fix isdir test
zooba Aug 16, 2019
79a3f54
Merge branch 'bpo-37834' into bpo-9949-37834
zooba Aug 16, 2019
28c9bb4
Simplify and document cycle handling in os.path.realpath
zooba Aug 16, 2019
203869f
Merge branch 'bpo-9949' into bpo-9949-37834
zooba Aug 16, 2019
4ee15a8
Update docs
zooba Aug 16, 2019
ab485a5
Update whatsnew
zooba Aug 16, 2019
11e8092
Merge branch 'bpo-9949' into bpo-9949-37834
zooba Aug 16, 2019
25511ea
Merge branch 'bpo-37834' into bpo-9949-37834
zooba Aug 16, 2019
327cf57
Clarify docs
zooba Aug 16, 2019
39bec11
Update patch with implementation from Eryk Sun.
zooba Aug 19, 2019
d73d077
Swap order of tests to avoid unnecessary failed API calls
zooba Aug 19, 2019
51c338e
Merge branch 'bpo-37834' into bpo-9949-37834
zooba Aug 19, 2019
57cc198
Remove workaround for bpo20541 as NUL now exists
zooba Aug 20, 2019
0b926e1
Merge branch 'bpo-37834' into bpo-9949-37834
zooba Aug 20, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Simplify and document cycle handling in os.path.realpath
  • Loading branch information
zooba committed Aug 16, 2019
commit 28c9bb47ebe71f6e7ba3effab61836d900a49d7c
4 changes: 4 additions & 0 deletions Doc/library/os.path.rst
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,10 @@ the :mod:`glob` module.)
links encountered in the path (if they are supported by the operating
system).

.. note::
When symbolic link cycles occur, the returned path will be one member of
the cycle, but no guarantee is made about which member that will be.

.. versionchanged:: 3.6
Accepts a :term:`path-like object`.

Expand Down
21 changes: 8 additions & 13 deletions Lib/ntpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -520,29 +520,25 @@ def abspath(path):
return _abspath_fallback(path)

try:
from nt import _getfinalpathname
from nt import _getfinalpathname, readlink as _nt_readlink
except ImportError:
# realpath is a no-op on systems without _getfinalpathname support.
realpath = abspath
else:
def _readlink_deep(path, seen=None, *, _readlink=os.readlink):
def _readlink_deep(path, seen=None):
if seen is None:
seen = set()

while True:
while normcase(path) not in seen:
seen.add(normcase(path))
try:
next_path = _readlink(path)
# Break out _before_ traversing the cycle
if normcase(next_path) in seen:
break
path = next_path
path = _nt_readlink(path)
except OSError as ex:
# Stop on file (2) or directory (3) not found, or
# paths that are not reparse points (4390)
if ex.winerror not in (2, 3, 4390):
raise
break
if ex.winerror in (2, 3, 4390):
break
raise
except ValueError:
# Stop on reparse points that are not symlinks
break
Expand All @@ -567,8 +563,7 @@ def _getfinalpathname_nonstrict(path):
raise
path, name = split(path)
if path and not name:
tail = path + tail
break
return abspath(path + tail)
tail = join(name, tail) if tail else name
return abspath(tail)

Expand Down
16 changes: 10 additions & 6 deletions Lib/test/test_ntpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,12 +320,16 @@ def test_realpath_symlink_loops(self):
os.symlink(ABSTFN, ABSTFN)
self.assertEqual(ntpath.realpath(ABSTFN), P + ABSTFN)

# cycles are non-deterministic as to which path is returned, but
# it will always be the fully resolved path of one member of the cycle
os.symlink(ABSTFN + "1", ABSTFN + "2")
os.symlink(ABSTFN + "2", ABSTFN + "1")
self.assertEqual(ntpath.realpath(ABSTFN + "1"), P + ABSTFN + "2")
self.assertEqual(ntpath.realpath(ABSTFN + "2"), P + ABSTFN + "1")
expected = (P + ABSTFN + "1", P + ABSTFN + "2")
self.assertIn(ntpath.realpath(ABSTFN + "1"), expected)
self.assertIn(ntpath.realpath(ABSTFN + "2"), expected)

self.assertEqual(ntpath.realpath(ABSTFN + "1\\x"), P + ABSTFN + "2\\x")
self.assertIn(ntpath.realpath(ABSTFN + "1\\x"),
(ntpath.join(r, "x") for r in expected))
self.assertEqual(ntpath.realpath(ABSTFN + "1\\.."),
ntpath.dirname(ABSTFN))
self.assertEqual(ntpath.realpath(ABSTFN + "1\\..\\x"),
Expand All @@ -334,9 +338,9 @@ def test_realpath_symlink_loops(self):
self.assertEqual(ntpath.realpath(ABSTFN + "1\\..\\"
+ ntpath.basename(ABSTFN) + "y"),
P + ABSTFN + "x")
self.assertEqual(ntpath.realpath(ABSTFN + "1\\..\\"
+ ntpath.basename(ABSTFN) + "1"),
P + ABSTFN + "1")
self.assertIn(ntpath.realpath(ABSTFN + "1\\..\\"
+ ntpath.basename(ABSTFN) + "1"),
expected)

os.symlink(ntpath.basename(ABSTFN) + "a\\b", ABSTFN + "a")
self.assertEqual(ntpath.realpath(ABSTFN + "a"), P + ABSTFN + "a")
Expand Down