Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit da1980a

Browse filesBrowse files
authored
GH-104114: Fix pathlib.WindowsPath.glob() use of literal pattern segment case (GH-104116)
We now use `_WildcardSelector` to evaluate literal pattern segments, which allows us to retrieve the real filesystem case. This change is necessary in order to implement a *case_sensitive* argument (see GH-81079) and a *follow_symlinks* argument (see GH-77609).
1 parent 38dc3f2 commit da1980a
Copy full SHA for da1980a

File tree

Expand file treeCollapse file tree

3 files changed

+18
-41
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+18
-41
lines changed

‎Lib/pathlib.py

Copy file name to clipboardExpand all lines: Lib/pathlib.py
+13-39Lines changed: 13 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,6 @@ def _ignore_error(exception):
5454
getattr(exception, 'winerror', None) in _IGNORED_WINERRORS)
5555

5656

57-
def _is_wildcard_pattern(pat):
58-
# Whether this pattern needs actual matching using fnmatch, or can
59-
# be looked up directly as a file.
60-
return "*" in pat or "?" in pat or "[" in pat
61-
6257
def _is_case_sensitive(flavour):
6358
return flavour.normcase('Aa') == 'Aa'
6459

@@ -78,10 +73,8 @@ def _make_selector(pattern_parts, flavour):
7873
cls = _ParentSelector
7974
elif '**' in pat:
8075
raise ValueError("Invalid pattern: '**' can only be an entire path component")
81-
elif _is_wildcard_pattern(pat):
82-
cls = _WildcardSelector
8376
else:
84-
cls = _PreciseSelector
77+
cls = _WildcardSelector
8578
return cls(pat, child_parts, flavour)
8679

8780

@@ -102,55 +95,36 @@ def select_from(self, parent_path):
10295
"""Iterate over all child paths of `parent_path` matched by this
10396
selector. This can contain parent_path itself."""
10497
path_cls = type(parent_path)
105-
is_dir = path_cls.is_dir
106-
exists = path_cls.exists
10798
scandir = path_cls._scandir
108-
if not is_dir(parent_path):
99+
if not parent_path.is_dir():
109100
return iter([])
110-
return self._select_from(parent_path, is_dir, exists, scandir)
101+
return self._select_from(parent_path, scandir)
111102

112103

113104
class _TerminatingSelector:
114105

115-
def _select_from(self, parent_path, is_dir, exists, scandir):
106+
def _select_from(self, parent_path, scandir):
116107
yield parent_path
117108

118109

119110
class _ParentSelector(_Selector):
120111
def __init__(self, name, child_parts, flavour):
121112
_Selector.__init__(self, child_parts, flavour)
122113

123-
def _select_from(self, parent_path, is_dir, exists, scandir):
114+
def _select_from(self, parent_path, scandir):
124115
path = parent_path._make_child_relpath('..')
125-
for p in self.successor._select_from(path, is_dir, exists, scandir):
116+
for p in self.successor._select_from(path, scandir):
126117
yield p
127118

128119

129-
class _PreciseSelector(_Selector):
130-
131-
def __init__(self, name, child_parts, flavour):
132-
self.name = name
133-
_Selector.__init__(self, child_parts, flavour)
134-
135-
def _select_from(self, parent_path, is_dir, exists, scandir):
136-
try:
137-
path = parent_path._make_child_relpath(self.name)
138-
follow = is_dir(path) if self.dironly else exists(path, follow_symlinks=False)
139-
if follow:
140-
for p in self.successor._select_from(path, is_dir, exists, scandir):
141-
yield p
142-
except PermissionError:
143-
return
144-
145-
146120
class _WildcardSelector(_Selector):
147121

148122
def __init__(self, pat, child_parts, flavour):
149123
flags = re.NOFLAG if _is_case_sensitive(flavour) else re.IGNORECASE
150124
self.match = re.compile(fnmatch.translate(pat), flags=flags).fullmatch
151125
_Selector.__init__(self, child_parts, flavour)
152126

153-
def _select_from(self, parent_path, is_dir, exists, scandir):
127+
def _select_from(self, parent_path, scandir):
154128
try:
155129
# We must close the scandir() object before proceeding to
156130
# avoid exhausting file descriptors when globbing deep trees.
@@ -171,7 +145,7 @@ def _select_from(self, parent_path, is_dir, exists, scandir):
171145
name = entry.name
172146
if self.match(name):
173147
path = parent_path._make_child_relpath(name)
174-
for p in self.successor._select_from(path, is_dir, exists, scandir):
148+
for p in self.successor._select_from(path, scandir):
175149
yield p
176150
except PermissionError:
177151
return
@@ -182,7 +156,7 @@ class _RecursiveWildcardSelector(_Selector):
182156
def __init__(self, pat, child_parts, flavour):
183157
_Selector.__init__(self, child_parts, flavour)
184158

185-
def _iterate_directories(self, parent_path, is_dir, scandir):
159+
def _iterate_directories(self, parent_path, scandir):
186160
yield parent_path
187161
try:
188162
# We must close the scandir() object before proceeding to
@@ -198,18 +172,18 @@ def _iterate_directories(self, parent_path, is_dir, scandir):
198172
raise
199173
if entry_is_dir and not entry.is_symlink():
200174
path = parent_path._make_child_relpath(entry.name)
201-
for p in self._iterate_directories(path, is_dir, scandir):
175+
for p in self._iterate_directories(path, scandir):
202176
yield p
203177
except PermissionError:
204178
return
205179

206-
def _select_from(self, parent_path, is_dir, exists, scandir):
180+
def _select_from(self, parent_path, scandir):
207181
try:
208182
yielded = set()
209183
try:
210184
successor_select = self.successor._select_from
211-
for starting_point in self._iterate_directories(parent_path, is_dir, scandir):
212-
for p in successor_select(starting_point, is_dir, exists, scandir):
185+
for starting_point in self._iterate_directories(parent_path, scandir):
186+
for p in successor_select(starting_point, scandir):
213187
if p not in yielded:
214188
yield p
215189
yielded.add(p)

‎Lib/test/test_pathlib.py

Copy file name to clipboardExpand all lines: Lib/test/test_pathlib.py
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3122,15 +3122,15 @@ def test_glob(self):
31223122
self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") })
31233123
self.assertEqual(set(p.glob("*a\\")), { P(BASE, "dirA") })
31243124
self.assertEqual(set(p.glob("F*a")), { P(BASE, "fileA") })
3125-
self.assertEqual(set(map(str, p.glob("FILEa"))), {f"{p}\\FILEa"})
3125+
self.assertEqual(set(map(str, p.glob("FILEa"))), {f"{p}\\fileA"})
31263126
self.assertEqual(set(map(str, p.glob("F*a"))), {f"{p}\\fileA"})
31273127

31283128
def test_rglob(self):
31293129
P = self.cls
31303130
p = P(BASE, "dirC")
31313131
self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") })
31323132
self.assertEqual(set(p.rglob("*\\")), { P(BASE, "dirC/dirD") })
3133-
self.assertEqual(set(map(str, p.rglob("FILEd"))), {f"{p}\\dirD\\FILEd"})
3133+
self.assertEqual(set(map(str, p.rglob("FILEd"))), {f"{p}\\dirD\\fileD"})
31343134

31353135
def test_expanduser(self):
31363136
P = self.cls
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix issue where :meth:`pathlib.Path.glob` returns paths using the case of
2+
non-wildcard segments for corresponding path segments, rather than the real
3+
filesystem case.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.