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 94f30c7

Browse filesBrowse files
authored
GH-90208: Suppress OSError exceptions from pathlib.Path.glob() (GH-104141)
`pathlib.Path.glob()` now suppresses all OSError exceptions, except those raised from calling `is_dir()` on the top-level path. Previously, `glob()` suppressed ENOENT, ENOTDIR, EBADF and ELOOP errors and their Windows equivalents. PermissionError was also suppressed unless it occurred when calling `is_dir()` on the top-level path. However, the selector would abort prematurely if a PermissionError was raised, and so `glob()` could return incomplete results.
1 parent 373bca0 commit 94f30c7
Copy full SHA for 94f30c7

File tree

Expand file treeCollapse file tree

3 files changed

+29
-46
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+29
-46
lines changed

‎Lib/pathlib.py

Copy file name to clipboardExpand all lines: Lib/pathlib.py
+13-20Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -142,25 +142,21 @@ def _select_from(self, parent_path, scandir):
142142
# avoid exhausting file descriptors when globbing deep trees.
143143
with scandir(parent_path) as scandir_it:
144144
entries = list(scandir_it)
145+
except OSError:
146+
pass
147+
else:
145148
for entry in entries:
146149
if self.dironly:
147150
try:
148-
# "entry.is_dir()" can raise PermissionError
149-
# in some cases (see bpo-38894), which is not
150-
# among the errors ignored by _ignore_error()
151151
if not entry.is_dir():
152152
continue
153-
except OSError as e:
154-
if not _ignore_error(e):
155-
raise
153+
except OSError:
156154
continue
157155
name = entry.name
158156
if self.match(name):
159157
path = parent_path._make_child_relpath(name)
160158
for p in self.successor._select_from(path, scandir):
161159
yield p
162-
except PermissionError:
163-
return
164160

165161

166162
class _RecursiveWildcardSelector(_Selector):
@@ -175,28 +171,25 @@ def _iterate_directories(self, parent_path, scandir):
175171
# avoid exhausting file descriptors when globbing deep trees.
176172
with scandir(parent_path) as scandir_it:
177173
entries = list(scandir_it)
174+
except OSError:
175+
pass
176+
else:
178177
for entry in entries:
179178
entry_is_dir = False
180179
try:
181180
entry_is_dir = entry.is_dir(follow_symlinks=False)
182-
except OSError as e:
183-
if not _ignore_error(e):
184-
raise
181+
except OSError:
182+
pass
185183
if entry_is_dir:
186184
path = parent_path._make_child_relpath(entry.name)
187185
for p in self._iterate_directories(path, scandir):
188186
yield p
189-
except PermissionError:
190-
return
191187

192188
def _select_from(self, parent_path, scandir):
193-
try:
194-
successor_select = self.successor._select_from
195-
for starting_point in self._iterate_directories(parent_path, scandir):
196-
for p in successor_select(starting_point, scandir):
197-
yield p
198-
except PermissionError:
199-
return
189+
successor_select = self.successor._select_from
190+
for starting_point in self._iterate_directories(parent_path, scandir):
191+
for p in successor_select(starting_point, scandir):
192+
yield p
200193

201194

202195
class _DoubleRecursiveWildcardSelector(_RecursiveWildcardSelector):

‎Lib/test/test_pathlib.py

Copy file name to clipboardExpand all lines: Lib/test/test_pathlib.py
+12-26Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1949,33 +1949,19 @@ def test_glob_permissions(self):
19491949
P = self.cls
19501950
base = P(BASE) / 'permissions'
19511951
base.mkdir()
1952+
self.addCleanup(os_helper.rmtree, base)
19521953

1953-
file1 = base / "file1"
1954-
file1.touch()
1955-
file2 = base / "file2"
1956-
file2.touch()
1957-
1958-
subdir = base / "subdir"
1959-
1960-
file3 = base / "file3"
1961-
file3.symlink_to(subdir / "other")
1962-
1963-
# Patching is needed to avoid relying on the filesystem
1964-
# to return the order of the files as the error will not
1965-
# happen if the symlink is the last item.
1966-
real_scandir = os.scandir
1967-
def my_scandir(path):
1968-
with real_scandir(path) as scandir_it:
1969-
entries = list(scandir_it)
1970-
entries.sort(key=lambda entry: entry.name)
1971-
return contextlib.nullcontext(entries)
1972-
1973-
with mock.patch("os.scandir", my_scandir):
1974-
self.assertEqual(len(set(base.glob("*"))), 3)
1975-
subdir.mkdir()
1976-
self.assertEqual(len(set(base.glob("*"))), 4)
1977-
subdir.chmod(000)
1978-
self.assertEqual(len(set(base.glob("*"))), 4)
1954+
for i in range(100):
1955+
link = base / f"link{i}"
1956+
if i % 2:
1957+
link.symlink_to(P(BASE, "dirE", "nonexistent"))
1958+
else:
1959+
link.symlink_to(P(BASE, "dirC"))
1960+
1961+
self.assertEqual(len(set(base.glob("*"))), 100)
1962+
self.assertEqual(len(set(base.glob("*/"))), 50)
1963+
self.assertEqual(len(set(base.glob("*/fileC"))), 50)
1964+
self.assertEqual(len(set(base.glob("*/file*"))), 50)
19791965

19801966
@os_helper.skip_unless_symlink
19811967
def test_glob_long_symlink(self):
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fixed issue where :meth:`pathlib.Path.glob` returned incomplete results when
2+
it encountered a :exc:`PermissionError`. This method now suppresses all
3+
:exc:`OSError` exceptions, except those raised from calling
4+
:meth:`~pathlib.Path.is_dir` on the top-level path.

0 commit comments

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