Repository navigation
pathlib.Path.glob does not follow symlinks #77609
Description
Activity
BrianMSheldon commented
on May 5, 2018 BrianMSheldonmannequinMannequinAuthorMore actionsGiven a
pathlib.Paththat contains symlinked subfolders,Path.glob(and.rglob) do not follow symlinks. This is not consistent withglob.globwhich does.For example given the following:
C:\Folder
C:\Folder\Subfolder -> D:\Subfolder
D:\Subfolder\File.txtpathlib.Path('C:/Folder').glob('**/*')yields the following paths:
WindowsPath('C:/Folder/Subfolder')glob.glob('C:/Folder/**/*')yields the following paths:
'C:/Folder\Subfolder'
'C:/Folder\Subfolder\File.txt'Notice how the contents of Subfolder are present in the
glob.globresults but not forPath.glob.I would expect
Path.globto be consistent withglob.glob. This is not the only inconsistency (e.g. bpo-22276, bpo-31202) and perhapsPath.globshould be re-implemented usingglob.glob.- addedstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on May 5, 2018 This looks like an issue specific to Windows? I can't replicate on Mac, and given Windows' method of implementing "symlinks" as junctions.
BrianMSheldon commented
on May 17, 2018 BrianMSheldonmannequinMannequinAuthorMore actionsWindows does not implement symlinks as junctions. Windows has hardlinks, symlinks and junctions which are all distinctly different in behaviour.
I don't doubt that this is a Windows-specific issue, although I have not tested other platforms. Path.glob and .rglob does work for junctions and hardlinks but glob.glob works consistently for all three.
DanyaAlexeyevsky commented
on Apr 29, 2020 DanyaAlexeyevskymannequinMannequinMore actionsI can reproduce the bug with Linux and python 3.7.5:
Python 3.7.5 (default, Apr 19 2020, 20:18:17) [GCC 9.2.1 20191008] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from pathlib import Path >>> Path('a/b').mkdir(parents=True) >>> Path('c/d').mkdir(parents=True) >>> Path('a/c').symlink_to('../c') >>> Path('e').symlink_to('c') >>> list(Path('.').rglob('*')) [PosixPath('e'), PosixPath('c'), PosixPath('a'), PosixPath('c/d'), PosixPath('a/c'), PosixPath('a/b')]
Expected result:
[PosixPath('e'), PosixPath('e/d'), PosixPath('c'), PosixPath('a'), PosixPath('c/d'), PosixPath('a/c'), PosixPath('a/c/d'), PosixPath('a/b')]Reacted by Yuta Kawabe and glowinthedarkFollowing symlinks was disabled on purpose as a fix for #70200:
Line 319 in 69f6cc7
if entry_is_dir and not entry.is_symlink(): To re-enable it, we'd have to come up with a different mitigation for the symlink loops problem.
Or possibly, if hidden behind a
follow_symlinksarg which would beFalseby default, it might be enough to just document it as a limitation whenfollow_symlinks=True? I don't know.Or possibly, if hidden behind a follow_symlinks arg which would be False by default, it might be enough to just document it as a limitation when follow_symlinks=True? I don't know.
glob()still follows symlinks when matching segments likefoo/,*/and so forth; it only refuses to follow symlinks when it encounters a**wildcard. So the current behaviour is roughlyfollow_symlinks=Sometimes.I realised this because I've been trying to implement an iterative version of
rglob(), building uponwalk()and filtering paths with a compiledre.Patternobject.walk()has its ownfollow_symlinksargument that acceptsTrueandFalse, but neither option aligns with the existing behaviour, so it seems like a dead end unless this "bug" is "fixed".Reacted by glowinthedarkzsh has a
***wildcard that recurses into symlinks to directories, see Recursive Globbing here: https://linux.die.net/man/1/zshexpnReacted by Gregory P. SmithHaving thought about this some more, I'd like to propose that we add follow_symlinks arguments to
glob()andrglob(), where:follow_symlinks=Falsetreats symlinks as files (default)follow_symlinks=Truefollows symlinks to directories
Note that the default behaviour would therefore change: a pattern like
foo/baror*/barwould no longer matchfoo/bariffoois a symlink. This is consistent with how**/barworks today.With that in place, we could implement
glob()iteratively atopwalk(), which would address #89727 and substantially improve performance!Thoughts?
Reacted by Dr. Juan Miguel Cejuela24 remaining items
Lets change the new
follow_symlinks=Nonetofollow_symlinks=NotRecursive(or some other explicitly named sentinel value defined in the pathlib module).People reading code that explicitly specifies
follow_symlinks=Noneas would be needed if we ever decide to change the default would not be able to reason about what that does and why=Noneisn't the same behavior as=Falseor=0. Seeing a named constant makes it much more clear to those not intimately familiar with the API.This is good even if we never decide to work towards a default change.
Reacted by Barney Gale and Dr. Juan Miguel Cejuela- added a commit that references this issue
on Apr 5, 2024 Re-resolving - the argument is now
recurse_symlinksand accepts a boolean.Reacted by Edgar Ramírez Mondragón and Dr. Juan Miguel Cejuela
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsDone
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
Linked PRs
pathlib.Path.glob()#102616pathlib.Path.glob()#104176pathlib.Path.glob()#117311