8000 Merge branch '3.13' into backport-548a11d-3.13 · python/cpython@921bf2f · GitHub
[go: up one dir, main page]

Skip to content

Commit 921bf2f

Browse files
Merge branch '3.13' into backport-548a11d-3.13
2 parents 36aa3f5 + af57832 commit 921bf2f

31 files changed

+186
-108
lines changed

Lib/_pyrepl/simple_interact.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ def runsource(self, source, filename="<input>", symbol="single"):
9595
the_symbol = symbol if stmt is last_stmt else "exec"
9696
item = wrapper([stmt])
9797
try:
98-
code = compile(item, filename, the_symbol)
99-
except (OverflowError, ValueError):
98+
code = compile(item, filename, the_symbol, dont_inherit=True)
99+
except (OverflowError, ValueError, SyntaxError):
100100
self.showsyntaxerror(filename)
101101
return False
102102

Lib/glob.py

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -523,43 +523,6 @@ def select_exists(self, path, exists=False):
523523
except OSError:
524524
pass
525525

526-
@classmethod
527-
def walk(cls, root, top_down, on_error, follow_symlinks):
528-
"""Walk the directory tree from the given root, similar to os.walk().
529-
"""
530-
paths = [root]
531-
while paths:
532-
path = paths.pop()
533-
if isinstance(path, tuple):
534-
yield path
535-
continue
536-
try:
537-
with cls.scandir(path) as scandir_it:
538-
dirnames = []
539-
filenames = []
540-
if not top_down:
541-
paths.append((path, dirnames, filenames))
542-
for entry in scandir_it:
543-
name = entry.name
544-
try:
545-
if entry.is_dir(follow_symlinks=follow_symlinks):
546-
if not top_down:
547-
paths.append(cls.parse_entry(entry))
548-
dirnames.append(name)
549-
else:
550-
filenames.append(name)
551-
except OSError:
552-
filenames.append(name)
553-
except OSError as error:
554-
if on_error is not None:
555-
on_error(error)
556-
else:
557-
if top_down:
558-
yield path, dirnames, filenames
559-
if dirnames:
560-
prefix = cls.add_slash(path)
561-
paths += [cls.concat_path(prefix, d) for d in reversed(dirnames)]
562-
563526

564527
class _StringGlobber(_Globber):
565528
lstat = staticmethod(os.lstat)

Lib/os.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,10 @@ def renames(old, new):
281281

282282
__all__.extend(["makedirs", "removedirs", "renames"])
283283

284+
# Private sentinel that makes walk() classify all symlinks and junctions as
285+
# regular files.
286+
_walk_symlinks_as_files = object()
287+
284288
def walk(top, topdown=True, onerror=None, followlinks=False):
285289
"""Directory tree generator.
286290
@@ -382,7 +386,10 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
382386
break
383387

384388
try:
385-
is_dir = entry.is_dir()
389+
if followlinks is _walk_symlinks_as_files:
390+
is_dir = entry.is_dir(follow_symlinks=False) and not entry.is_junction()
391+
else:
392+
is_dir = entry.is_dir()
386393
except OSError:
387394
# If is_dir() raises an OSError, consider the entry not to
388395
# be a directory, same behaviour as os.path.isdir().

Lib/pathlib/_abc.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,37 @@ def rglob(self, pattern, *, case_sensitive=None, recurse_symlinks=True):
696696

697697
def walk(self, top_down=True, on_error=None, follow_symlinks=False):
698698
"""Walk the directory tree from this directory, similar to os.walk()."""
699-
return self._globber.walk(self, top_down, on_error, follow_symlinks)
699+
paths = [self]
700+
while paths:
701+
path = paths.pop()
702+
if isinstance(path, tuple):
703+
yield path
704+
continue
705+
dirnames = []
706+
filenames = []
707+
if not top_down:
708+
paths.append((path, dirnames, filenames))
709+
try:
710+
for child in path.iterdir():
711+
try:
712+
if child.is_dir(follow_symlinks=follow_symlinks):
713+
if not top_down:
714+
paths.append(child)
715+
dirnames.append(child.name)
716+
else:
717+
filenames.append(child.name)
718+
except OSError:
719+
filenames.append(child.name)
720+
except OSError as error:
721+
if on_error is not None:
722+
on_error(error)
723+
if not top_down:
724+
while not isinstance(paths.pop(), tuple):
725+
pass
726+
continue
727+
if top_down:
728+
yield path, dirnames, filenames
729+
paths += [path.joinpath(d) for d in reversed(dirnames)]
700730

701731
def absolute(self):
702732
"""Return an absolute version of this path

Lib/pathlib/_local.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,9 @@ def walk(self, top_down=True, on_error=None, follow_symlinks=False):
623623
"""Walk the directory tree from this directory, similar to os.walk()."""
624624
sys.audit("pathlib.Path.walk", self, on_error, follow_symlinks)
625625
root_dir = str(self)
626-
results = self._globber.walk(root_dir, top_down, on_error, follow_symlinks)
626+
if not follow_symlinks:
627+
follow_symlinks = os._walk_symlinks_as_files
628+
results = os.walk(root_dir, top_down, on_error, follow_symlinks)
627629
for path_str, dirnames, filenames in results:
628630
if root_dir == '.':
629631
path_str = path_str[2:]

Lib/shutil.py

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -606,37 +606,21 @@ def _rmtree_islink(st):
606606

607607
# version vulnerable to race conditions
608608
def _rmtree_unsafe(path, onexc):
609-
try:
610-
with os.scandir(path) as scandir_it:
611-
entries = list(scandir_it)
612-
except FileNotFoundError:
613-
return
614-
except OSError as err:
615-
onexc(os.scandir, path, err)
616-
entries = []
617-
for entry in entries:
618-
fullname = entry.path
619-
try:
620-
is_dir = entry.is_dir(follow_symlinks=False)
621-
except FileNotFoundError:
622-
continue
623-
except OSError:
624-
is_dir = False
625-
626-
if is_dir and not entry.is_junction():
609+
def onerror(err):
610+
if not isinstance(err, FileNotFoundError):
611+
onexc(os.scandir, err.filename, err)
612+
results = os.walk(path, topdown=False, onerror=onerror, followlinks=os._walk_symlinks_as_files)
613+
for dirpath, dirnames, filenames in results:
614+
for name in dirnames:
615+
fullname = os.path.join(dirpath, name)
627616
try:
628-
if entry.is_symlink():
629-
# This can only happen if someone replaces
630-
# a directory with a symlink after the call to
631-
# os.scandir or entry.is_dir above.
632-
raise OSError("Cannot call rmtree on a symbolic link")
617+
os.rmdir(fullname)
633618
except FileNotFoundError:
634619
continue
635620
except OSError as err:
636-
onexc(os.path.islink, fullname, err)
637-
continue
638-
_rmtree_unsafe(fullname, onexc)
639-
else:
621+
onexc(os.rmdir, fullname, err)
622+
for name in filenames:
623+
fullname = os.path.join(dirpath, name)
640624
try:
641625
os.unlink(fullname)
642626
except FileNotFoundError:

Lib/test/datetimetester.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,25 @@
4747
pass
4848
#
4949

50+
# This is copied from test_import/__init__.py.
51+
def no_rerun(reason):
52+
"""Skip rerunning for a particular test.
53+
54+
WARNING: Use this decorator with care; skipping rerunning makes it
55+
impossible to find reference leaks. Provide a clear reason for skipping the
56+
test using the 'reason' parameter.
57+
"""
58+
def deco(func):
59+
_has_run = False
60+
def wrapper(self):
61+
nonlocal _has_run
62+
if _has_run:
63+
self.skipTest(reason)
64+
func(self)
65+
_has_run = True
66+
return wrapper
67+
return deco
68+
5069
pickle_loads = {pickle.loads, pickle._loads}
5170

5271
pickle_choices = [(pickle, pickle, proto)
@@ -6383,6 +6402,7 @@ class IranTest(ZoneInfoTest):
63836402

63846403

63856404
@unittest.skipIf(_testcapi is None, 'need _testcapi module')
6405+
@no_rerun("the encapsulated datetime C API does not support reloading")
63866406
class CapiTest(unittest.TestCase):
63876407
def setUp(self):
63886408
# Since the C API is not present in the _Pure tests, skip all tests

Lib/test/test_launcher.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,3 +764,11 @@ def test_shebang_command_in_venv(self):
764764
with self.script(f'#! /usr/bin/env {exe.stem} arg1') as script:
765765
data = self.run_py([script], env=env)
766766
self.assertEqual(data["stdout"].strip(), f"{quote(exe)} arg1 {quote(script)}")
767+
768+
def test_shebang_executable_extension(self):
769+
with self.script('#! /usr/bin/env python3.12') as script:
770+
data = self.run_py([script])
771+
expect = "# Search PATH for python3.12.exe"
772+
actual = [line.strip() for line in data["stderr"].splitlines()
773+
if line.startswith("# Search PATH")]
774+
self.assertEqual([expect], actual)

Lib/test/test_pyrepl/test_interact.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,20 @@ def test_runsource_shows_syntax_error_for_failed_compilation(self):
9494
with patch.object(console, "showsyntaxerror") as mock_showsyntaxerror:
9595
console.runsource(source)
9696
mock_showsyntaxerror.assert_called_once()
97+
source = dedent("""\
98+
match 1:
99+
case {0: _, 0j: _}:
100+
pass
101+
""")
102+
with patch.object(console, "showsyntaxerror") as mock_showsyntaxerror:
103+
console.runsource(source)
104+
mock_showsyntaxerror.assert_called_once()
105+
106+
def test_no_active_future(self):
107+
console = InteractiveColoredConsole()
108+
source = "x: int = 1; print(__annotations__)"
109+
f = io.StringIO()
110+
with contextlib.redirect_stdout(f):
111+
result = console.runsource(source)
112+
self.assertFalse(result)
113+
self.assertEqual(f.getvalue(), "{'x': <class 'int'>}\n")

Lib/test/test_shutil.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,17 @@ def _onexc(fn, path, exc):
741741
shutil.rmtree(TESTFN)
742742
raise
743743

744+
@unittest.skipIf(shutil._use_fd_functions, "fd-based functions remain unfixed (GH-89727)")
745+
def test_rmtree_above_recursion_limit(self):
746+
recursion_limit = 40
747+
# directory_depth > recursion_limit
748+
directory_depth = recursion_limit + 10
749+
base = os.path.join(TESTFN, *(['d'] * directory_depth))
750+
os.makedirs(base)
751+
752+
with support.infinite_recursion(recursion_limit):
753+
shutil.rmtree(TESTFN)
754+
744755

745756
class TestCopyTree(BaseTest, unittest.TestCase):
746757

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix deadlock involving ``_PyType_Lookup()`` cache in the free-threaded build
2+
when the GIL is dynamically enabled at runtime.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The interactive REPL no longer runs with ``from __future__ import
2+
annotations`` enabled. Patch by Jelle Zijlstra.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Catch :exc:`SyntaxError` from :func:`compile` in the runsource() method of
2+
the InteractiveColoredConsole. Patch by Sergey B Kirpichev.
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Partially fix issue with :func:`shutil.rmtree` where a :exc:`RecursionError`
2+
is raised on deep directory trees. A recursion error is no longer raised
3+
when :data:`!rmtree.avoids_symlink_attacks` is false.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixes ``py.exe`` handling of shebangs like ``/usr/bin/env python3.12``,
2+
which were previously interpreted as ``python3.exe`` instead of
3+
``python3.12.exe``.

Modules/_ctypes/clinic/_ctypes.c.h

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/_datetimemodule.c

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7089,30 +7089,26 @@ _datetime_exec(PyObject *module)
70897089
}
70907090
#undef DATETIME_ADD_MACRO
70917091

7092-
static struct PyModuleDef datetimemodule = {
7092+
static PyModuleDef_Slot module_slots[] = {
7093+
{Py_mod_exec, _datetime_exec},
7094+
{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},
7095+
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
7096+
{0, NULL},
7097+
};
7098+
7099+
static PyModuleDef datetimemodule = {
70937100
.m_base = PyModuleDef_HEAD_INIT,
70947101
.m_name = "_datetime",
70957102
.m_doc = "Fast implementation of the datetime type.",
7096-
.m_size = -1,
7103+
.m_size = 0,
70977104
.m_methods = module_methods,
7105+
.m_slots = module_slots,
70987106
};
70997107

71007108
PyMODINIT_FUNC
71017109
PyInit__datetime(void)
71027110
{
7103-
PyObject *mod = PyModule_Create(&datetimemodule);
7104-
if (mod == NULL)
7105-
return NULL;
7106-
#ifdef Py_GIL_DISABLED
7107-
PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED);
7108-
#endif
7109-
7110-
if (_datetime_exec(mod) < 0) {
7111-
Py_DECREF(mod);
7112-
return NULL;
7113-
}
7114-
7115-
return mod;
7111+
return PyModuleDef_Init(&datetimemodule);
71167112
}
71177113

71187114
/* ---------------------------------------------------------------------------

Modules/_io/clinic/bufferedio.c.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/_io/clinic/iobase.c.h

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/_io/clinic/textio.c.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/clinic/_curses_panel.c.h

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
0