Skip to content

Commit 4456dd6

Browse files
committed
Merge branch 'main' into fixloadsuperspec
* main: pythongh-87849: fix SEND specialization family definition (pythonGH-104268) pythongh-101819: Adapt _io.IOBase.seek and _io.IOBase.truncate to Argument Clinic (python#104384) pythongh-101819: Adapt _io._Buffered* methods to Argument Clinic (python#104367) pythongh-101819: Refactor `_io` futher in preparation for module isolation (python#104369) pythongh-101819: Adapt _io.TextIOBase methods to Argument Clinic (python#104383) pythongh-101117: Improve accuracy of sqlite3.Cursor.rowcount docs (python#104287) pythonGH-92184: Convert os.altsep to '/' in filenames when creating ZipInfo objects (python#92185) pythongh-104357: fix inlined comprehensions that close over iteration var (python#104368) pythonGH-90208: Suppress OSError exceptions from `pathlib.Path.glob()` (pythonGH-104141)
2 parents 5976df9 + 1670729 commit 4456dd6

21 files changed

+1203
-464
lines changed

Doc/library/sqlite3.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1694,7 +1694,10 @@ Cursor objects
16941694
``INSERT``, ``UPDATE``, ``DELETE``, and ``REPLACE`` statements;
16951695
is ``-1`` for other statements,
16961696
including :abbr:`CTE (Common Table Expression)` queries.
1697-
It is only updated by the :meth:`execute` and :meth:`executemany` methods.
1697+
It is only updated by the :meth:`execute` and :meth:`executemany` methods,
1698+
after the statement has run to completion.
1699+
This means that any resulting rows must be fetched in order for
1700+
:attr:`!rowcount` to be updated.
16981701

16991702
.. attribute:: row_factory
17001703

Lib/pathlib.py

Lines 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_listcomps.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,16 @@ def test_inner_cell_shadows_outer(self):
163163
outputs = {"y": [4, 4, 4, 4, 4], "i": 20}
164164
self._check_in_scopes(code, outputs)
165165

166+
def test_inner_cell_shadows_outer_no_store(self):
167+
code = """
168+
def f(x):
169+
return [lambda: x for x in range(x)], x
170+
fns, x = f(2)
171+
y = [fn() for fn in fns]
172+
"""
173+
outputs = {"y": [1, 1], "x": 2}
174+
self._check_in_scopes(code, outputs)
175+
166176
def test_closure_can_jump_over_comp_scope(self):
167177
code = """
168178
items = [(lambda: y) for i in range(5)]

Lib/test/test_pathlib.py

Lines 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):

Lib/zipfile/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,8 @@ def _sanitize_filename(filename):
352352
# ZIP format specification.
353353
if os.sep != "/" and os.sep in filename:
354354
filename = filename.replace(os.sep, "/")
355+
if os.altsep and os.altsep != "/" and os.altsep in filename:
356+
filename = filename.replace(os.altsep, "/")
355357
return filename
356358

357359

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
When creating zip files using :mod:`zipfile`, ``os.altsep``, if not ``None``,
2+
will always be treated as a path separator even when it is not ``/``.
3+
Patch by Carey Metcalfe.
Lines 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)