aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--Doc/library/argparse.rst2
-rw-r--r--Doc/library/shutil.rst7
-rw-r--r--Doc/using/cmdline.rst17
-rw-r--r--Doc/whatsnew/3.14.rst37
-rw-r--r--Lib/pprint.py68
-rw-r--r--Lib/reprlib.py17
-rw-r--r--Lib/test/libregrtest/main.py16
-rw-r--r--Lib/test/test__interpreters.py15
-rw-r--r--Lib/test/test_hashlib.py10
-rw-r--r--Lib/test/test_regrtest.py11
-rw-r--r--Lib/test/test_reprlib.py40
-rw-r--r--Misc/NEWS.d/next/Core_and_Builtins/2025-06-18-16-45-36.gh-issue-135106.cpl6Aq.rst2
-rw-r--r--Misc/NEWS.d/next/Library/2025-06-14-12-06-55.gh-issue-135487.KdVFff.rst2
-rw-r--r--Misc/NEWS.d/next/Library/2025-06-20-17-06-59.gh-issue-90117.GYWVrn.rst1
-rw-r--r--Misc/NEWS.d/next/Library/2025-06-23-10-19-11.gh-issue-135855.-J0AGF.rst3
-rw-r--r--Misc/NEWS.d/next/Library/2025-06-24-14-43-24.gh-issue-135878.Db4roX.rst3
-rw-r--r--Misc/NEWS.d/next/Tests/2025-06-19-15-29-38.gh-issue-135494.FVl9a0.rst2
-rw-r--r--Modules/_interpretersmodule.c22
-rw-r--r--Modules/blake2module.c8
-rw-r--r--Modules/clinic/blake2module.c.h14
-rw-r--r--Modules/clinic/sha3module.c.h14
-rw-r--r--Modules/sha3module.c9
-rw-r--r--Objects/namespaceobject.c7
-rw-r--r--Objects/object.c76
-rw-r--r--Python/crossinterp.c1
-rw-r--r--Tools/wasm/emscripten/__main__.py3
26 files changed, 270 insertions, 137 deletions
diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst
index 17f126cc065..a03d88092db 100644
--- a/Doc/library/argparse.rst
+++ b/Doc/library/argparse.rst
@@ -955,7 +955,7 @@ See also :ref:`specifying-ambiguous-arguments`. The supported values are:
.. index:: single: + (plus); in argparse module
-* ``'+'``. Just like ``'*'``, all command-line args present are gathered into a
+* ``'+'``. Just like ``'*'``, all command-line arguments present are gathered into a
list. Additionally, an error message will be generated if there wasn't at
least one command-line argument present. For example::
diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst
index e7c4c4f46bd..2dde40c9d92 100644
--- a/Doc/library/shutil.rst
+++ b/Doc/library/shutil.rst
@@ -47,6 +47,13 @@ Directory and files operations
0, only the contents from the current file position to the end of the file will
be copied.
+ :func:`copyfileobj` will *not* guarantee that the destination stream has
+ been flushed on completion of the copy. If you want to read from the
+ destination at the completion of the copy operation (for example, reading
+ the contents of a temporary file that has been copied from a HTTP stream),
+ you must ensure that you have called :func:`~io.IOBase.flush` or
+ :func:`~io.IOBase.close` on the file-like object before attempting to read
+ the destination file.
.. function:: copyfile(src, dst, *, follow_symlinks=True)
diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst
index 40a46a62031..a5867b489e0 100644
--- a/Doc/using/cmdline.rst
+++ b/Doc/using/cmdline.rst
@@ -669,6 +669,13 @@ Miscellaneous options
.. versionadded:: 3.14
+ * :samp:`-X tlbc={0,1}` enables (1, the default) or disables (0) thread-local
+ bytecode in builds configured with :option:`--disable-gil`. When disabled,
+ this also disables the specializing interpreter. See also
+ :envvar:`PYTHON_TLBC`.
+
+ .. versionadded:: 3.14
+
It also allows passing arbitrary values and retrieving them through the
:data:`sys._xoptions` dictionary.
@@ -1302,6 +1309,16 @@ conflict.
.. versionadded:: 3.13
+.. envvar:: PYTHON_TLBC
+
+ If set to ``1`` enables thread-local bytecode. If set to ``0`` thread-local
+ bytecode and the specializing interpreter are disabled. Only applies to
+ builds configured with :option:`--disable-gil`.
+
+ See also the :option:`-X tlbc <-X>` command-line option.
+
+ .. versionadded:: 3.14
+
Debug-mode variables
~~~~~~~~~~~~~~~~~~~~
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index f0a87a9ada7..cbca720b75e 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -278,7 +278,7 @@ As another example, generating HTML attributes from data:
attributes = {"src": "shrubbery.jpg", "alt": "looks nice"}
template = t"<img {attributes}>"
- assert html(template) == '<img src="shrubbery.jpg" alt="looks nice" class="looks-nice">'
+ assert html(template) == '<img src="shrubbery.jpg" alt="looks nice" />'
Compared to using an f-string, the ``html`` function has access to template attributes
containing the original information: static strings, interpolations, and values
@@ -1757,6 +1757,16 @@ os
(Contributed by Cody Maloney in :gh:`129205`.)
+os.path
+-------
+
+* The *strict* parameter to :func:`os.path.realpath` accepts a new value,
+ :data:`os.path.ALLOW_MISSING`.
+ If used, errors other than :exc:`FileNotFoundError` will be re-raised;
+ the resulting path can be missing but it will be free of symlinks.
+ (Contributed by Petr Viktorin for :cve:`2025-4517`.)
+
+
pathlib
-------
@@ -1945,6 +1955,28 @@ sysconfig
(Contributed by Xuehai Pan in :gh:`131799`.)
+tarfile
+-------
+
+* :func:`~tarfile.data_filter` now normalizes symbolic link targets in order to
+ avoid path traversal attacks.
+ (Contributed by Petr Viktorin in :gh:`127987` and :cve:`2025-4138`.)
+* :func:`~tarfile.TarFile.extractall` now skips fixing up directory attributes
+ when a directory was removed or replaced by another kind of file.
+ (Contributed by Petr Viktorin in :gh:`127987` and :cve:`2024-12718`.)
+* :func:`~tarfile.TarFile.extract` and :func:`~tarfile.TarFile.extractall`
+ now (re-)apply the extraction filter when substituting a link (hard or
+ symbolic) with a copy of another archive member, and when fixing up
+ directory attributes.
+ The former raises a new exception, :exc:`~tarfile.LinkFallbackError`.
+ (Contributed by Petr Viktorin for :cve:`2025-4330` and :cve:`2024-12718`.)
+* :func:`~tarfile.TarFile.extract` and :func:`~tarfile.TarFile.extractall`
+ no longer extract rejected members when
+ :func:`~tarfile.TarFile.errorlevel` is zero.
+ (Contributed by Matt Prodani and Petr Viktorin in :gh:`112887`
+ and :cve:`2025-4435`.)
+
+
threading
---------
@@ -2700,6 +2732,7 @@ New features
* :c:func:`PyUnicodeWriter_Discard`
* :c:func:`PyUnicodeWriter_Finish`
* :c:func:`PyUnicodeWriter_Format`
+ * :c:func:`PyUnicodeWriter_WriteASCII`
* :c:func:`PyUnicodeWriter_WriteChar`
* :c:func:`PyUnicodeWriter_WriteRepr`
* :c:func:`PyUnicodeWriter_WriteStr`
@@ -2976,7 +3009,7 @@ Deprecated
:c:func:`PyUnicodeWriter_WriteSubstring(writer, str, start, end) <PyUnicodeWriter_WriteSubstring>`.
* :c:func:`!_PyUnicodeWriter_WriteASCIIString`:
replace ``_PyUnicodeWriter_WriteASCIIString(&writer, str)`` with
- :c:func:`PyUnicodeWriter_WriteUTF8(writer, str) <PyUnicodeWriter_WriteUTF8>`.
+ :c:func:`PyUnicodeWriter_WriteASCII(writer, str) <PyUnicodeWriter_WriteASCII>`.
* :c:func:`!_PyUnicodeWriter_WriteLatin1String`:
replace ``_PyUnicodeWriter_WriteLatin1String(&writer, str)`` with
:c:func:`PyUnicodeWriter_WriteUTF8(writer, str) <PyUnicodeWriter_WriteUTF8>`.
diff --git a/Lib/pprint.py b/Lib/pprint.py
index 1e611481b51..92a2c543ac2 100644
--- a/Lib/pprint.py
+++ b/Lib/pprint.py
@@ -653,6 +653,40 @@ class PrettyPrinter:
del context[objid]
return "{%s}" % ", ".join(components), readable, recursive
+ if (issubclass(typ, list) and r is list.__repr__) or \
+ (issubclass(typ, tuple) and r is tuple.__repr__):
+ if issubclass(typ, list):
+ if not object:
+ return "[]", True, False
+ format = "[%s]"
+ elif len(object) == 1:
+ format = "(%s,)"
+ else:
+ if not object:
+ return "()", True, False
+ format = "(%s)"
+ objid = id(object)
+ if maxlevels and level >= maxlevels:
+ return format % "...", False, objid in context
+ if objid in context:
+ return _recursion(object), False, True
+ context[objid] = 1
+ readable = True
+ recursive = False
+ components = []
+ append = components.append
+ level += 1
+ for o in object:
+ orepr, oreadable, orecur = self.format(
+ o, context, maxlevels, level)
+ append(orepr)
+ if not oreadable:
+ readable = False
+ if orecur:
+ recursive = True
+ del context[objid]
+ return format % ", ".join(components), readable, recursive
+
if issubclass(typ, _collections.abc.MappingView) and r in self._view_reprs:
objid = id(object)
if maxlevels and level >= maxlevels:
@@ -689,40 +723,6 @@ class PrettyPrinter:
del context[objid]
return typ.__name__ + '([%s])' % ", ".join(components), readable, recursive
- if (issubclass(typ, list) and r is list.__repr__) or \
- (issubclass(typ, tuple) and r is tuple.__repr__):
- if issubclass(typ, list):
- if not object:
- return "[]", True, False
- format = "[%s]"
- elif len(object) == 1:
- format = "(%s,)"
- else:
- if not object:
- return "()", True, False
- format = "(%s)"
- objid = id(object)
- if maxlevels and level >= maxlevels:
- return format % "...", False, objid in context
- if objid in context:
- return _recursion(object), False, True
- context[objid] = 1
- readable = True
- recursive = False
- components = []
- append = components.append
- level += 1
- for o in object:
- orepr, oreadable, orecur = self.format(
- o, context, maxlevels, level)
- append(orepr)
- if not oreadable:
- readable = False
- if orecur:
- recursive = True
- del context[objid]
- return format % ", ".join(components), readable, recursive
-
rep = repr(object)
return rep, (rep and not rep.startswith('<')), False
diff --git a/Lib/reprlib.py b/Lib/reprlib.py
index 441d1be4bde..ab18247682b 100644
--- a/Lib/reprlib.py
+++ b/Lib/reprlib.py
@@ -181,7 +181,22 @@ class Repr:
return s
def repr_int(self, x, level):
- s = builtins.repr(x) # XXX Hope this isn't too slow...
+ try:
+ s = builtins.repr(x)
+ except ValueError as exc:
+ assert 'sys.set_int_max_str_digits()' in str(exc)
+ # Those imports must be deferred due to Python's build system
+ # where the reprlib module is imported before the math module.
+ import math, sys
+ # Integers with more than sys.get_int_max_str_digits() digits
+ # are rendered differently as their repr() raises a ValueError.
+ # See https://github.com/python/cpython/issues/135487.
+ k = 1 + int(math.log10(abs(x)))
+ # Note: math.log10(abs(x)) may be overestimated or underestimated,
+ # but for simplicity, we do not compute the exact number of digits.
+ max_digits = sys.get_int_max_str_digits()
+ return (f'<{x.__class__.__name__} instance with roughly {k} '
+ f'digits (limit at {max_digits}) at 0x{id(x):x}>')
if len(s) > self.maxlong:
i = max(0, (self.maxlong-3)//2)
j = max(0, self.maxlong-3-i)
diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py
index 0d9c059a938..a2d01b157ac 100644
--- a/Lib/test/libregrtest/main.py
+++ b/Lib/test/libregrtest/main.py
@@ -190,6 +190,12 @@ class Regrtest:
strip_py_suffix(tests)
+ exclude_tests = set()
+ if self.exclude:
+ for arg in self.cmdline_args:
+ exclude_tests.add(arg)
+ self.cmdline_args = []
+
if self.pgo:
# add default PGO tests if no tests are specified
setup_pgo_tests(self.cmdline_args, self.pgo_extended)
@@ -200,17 +206,15 @@ class Regrtest:
if self.tsan_parallel:
setup_tsan_parallel_tests(self.cmdline_args)
- exclude_tests = set()
- if self.exclude:
- for arg in self.cmdline_args:
- exclude_tests.add(arg)
- self.cmdline_args = []
-
alltests = findtests(testdir=self.test_dir,
exclude=exclude_tests)
if not self.fromfile:
selected = tests or self.cmdline_args
+ if exclude_tests:
+ # Support "--pgo/--tsan -x test_xxx" command
+ selected = [name for name in selected
+ if name not in exclude_tests]
if selected:
selected = split_test_packages(selected)
else:
diff --git a/Lib/test/test__interpreters.py b/Lib/test/test__interpreters.py
index ad3ebbfdff6..a32d5d81d2b 100644
--- a/Lib/test/test__interpreters.py
+++ b/Lib/test/test__interpreters.py
@@ -485,6 +485,21 @@ class CommonTests(TestBase):
msg = r'_interpreters.run_func\(\) argument 3 must be dict, not int'
with self.assertRaisesRegex(TypeError, msg):
_interpreters.run_func(self.id, lambda: None, shared=1)
+ # See https://github.com/python/cpython/issues/135855
+ msg = r'_interpreters.set___main___attrs\(\) argument 2 must be dict, not int'
+ with self.assertRaisesRegex(TypeError, msg):
+ _interpreters.set___main___attrs(self.id, 1)
+
+ def test_invalid_shared_none(self):
+ msg = r'must be dict, not None'
+ with self.assertRaisesRegex(TypeError, msg):
+ _interpreters.exec(self.id, 'a', shared=None)
+ with self.assertRaisesRegex(TypeError, msg):
+ _interpreters.run_string(self.id, 'a', shared=None)
+ with self.assertRaisesRegex(TypeError, msg):
+ _interpreters.run_func(self.id, lambda: None, shared=None)
+ with self.assertRaisesRegex(TypeError, msg):
+ _interpreters.set___main___attrs(self.id, None)
def test_invalid_shared_encoding(self):
# See https://github.com/python/cpython/issues/127196
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index 6e4e41e4fbb..7b378c45e71 100644
--- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py
@@ -371,6 +371,16 @@ class HashLibTestCase(unittest.TestCase):
self.assertIs(constructor, _md5.md5)
self.assertEqual(sorted(builtin_constructor_cache), ['MD5', 'md5'])
+ def test_copy(self):
+ for cons in self.hash_constructors:
+ h1 = cons(os.urandom(16), usedforsecurity=False)
+ h2 = h1.copy()
+ self.assertIs(type(h1), type(h2))
+ self.assertEqual(h1.name, h2.name)
+ size = (16,) if h1.name in self.shakes else ()
+ self.assertEqual(h1.digest(*size), h2.digest(*size))
+ self.assertEqual(h1.hexdigest(*size), h2.hexdigest(*size))
+
def test_hexdigest(self):
for cons in self.hash_constructors:
h = cons(usedforsecurity=False)
diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py
index a43d2678ebd..5bc3c5924b0 100644
--- a/Lib/test/test_regrtest.py
+++ b/Lib/test/test_regrtest.py
@@ -2346,6 +2346,17 @@ class ArgsTestCase(BaseTestCase):
output = self.run_tests('-j1', '-v', testname, env=env, isolated=False)
check(output)
+ def test_pgo_exclude(self):
+ # Get PGO tests
+ output = self.run_tests('--pgo', '--list-tests')
+ pgo_tests = output.strip().split()
+
+ # Exclude test_re
+ output = self.run_tests('--pgo', '--list-tests', '-x', 'test_re')
+ tests = output.strip().split()
+ self.assertNotIn('test_re', tests)
+ self.assertEqual(len(tests), len(pgo_tests) - 1)
+
class TestUtils(unittest.TestCase):
def test_format_duration(self):
diff --git a/Lib/test/test_reprlib.py b/Lib/test/test_reprlib.py
index 16623654c29..d5631efcdb7 100644
--- a/Lib/test/test_reprlib.py
+++ b/Lib/test/test_reprlib.py
@@ -151,14 +151,38 @@ class ReprTests(unittest.TestCase):
eq(r(frozenset({1, 2, 3, 4, 5, 6, 7})), "frozenset({1, 2, 3, 4, 5, 6, ...})")
def test_numbers(self):
- eq = self.assertEqual
- eq(r(123), repr(123))
- eq(r(123), repr(123))
- eq(r(1.0/3), repr(1.0/3))
-
- n = 10**100
- expected = repr(n)[:18] + "..." + repr(n)[-19:]
- eq(r(n), expected)
+ for x in [123, 1.0 / 3]:
+ self.assertEqual(r(x), repr(x))
+
+ max_digits = sys.get_int_max_str_digits()
+ for k in [100, max_digits - 1]:
+ with self.subTest(f'10 ** {k}', k=k):
+ n = 10 ** k
+ expected = repr(n)[:18] + "..." + repr(n)[-19:]
+ self.assertEqual(r(n), expected)
+
+ def re_msg(n, d):
+ return (rf'<{n.__class__.__name__} instance with roughly {d} '
+ rf'digits \(limit at {max_digits}\) at 0x[a-f0-9]+>')
+
+ k = max_digits
+ with self.subTest(f'10 ** {k}', k=k):
+ n = 10 ** k
+ self.assertRaises(ValueError, repr, n)
+ self.assertRegex(r(n), re_msg(n, k + 1))
+
+ for k in [max_digits + 1, 2 * max_digits]:
+ self.assertGreater(k, 100)
+ with self.subTest(f'10 ** {k}', k=k):
+ n = 10 ** k
+ self.assertRaises(ValueError, repr, n)
+ self.assertRegex(r(n), re_msg(n, k + 1))
+ with self.subTest(f'10 ** {k} - 1', k=k):
+ n = 10 ** k - 1
+ # Here, since math.log10(n) == math.log10(n-1),
+ # the number of digits of n - 1 is overestimated.
+ self.assertRaises(ValueError, repr, n)
+ self.assertRegex(r(n), re_msg(n, k + 1))
def test_instance(self):
eq = self.assertEqual
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-18-16-45-36.gh-issue-135106.cpl6Aq.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-18-16-45-36.gh-issue-135106.cpl6Aq.rst
new file mode 100644
index 00000000000..b6e953a7719
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-18-16-45-36.gh-issue-135106.cpl6Aq.rst
@@ -0,0 +1,2 @@
+Restrict the trashcan mechanism to GC'ed objects and untrack them while in
+the trashcan to prevent the GC and trashcan mechanisms conflicting.
diff --git a/Misc/NEWS.d/next/Library/2025-06-14-12-06-55.gh-issue-135487.KdVFff.rst b/Misc/NEWS.d/next/Library/2025-06-14-12-06-55.gh-issue-135487.KdVFff.rst
new file mode 100644
index 00000000000..3ef51fa31df
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-06-14-12-06-55.gh-issue-135487.KdVFff.rst
@@ -0,0 +1,2 @@
+Fix :meth:`!reprlib.Repr.repr_int` when given integers with more than
+:func:`sys.get_int_max_str_digits` digits. Patch by Bénédikt Tran.
diff --git a/Misc/NEWS.d/next/Library/2025-06-20-17-06-59.gh-issue-90117.GYWVrn.rst b/Misc/NEWS.d/next/Library/2025-06-20-17-06-59.gh-issue-90117.GYWVrn.rst
new file mode 100644
index 00000000000..2bb15cb6d9c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-06-20-17-06-59.gh-issue-90117.GYWVrn.rst
@@ -0,0 +1 @@
+Speed up :mod:`pprint` for :class:`list` and :class:`tuple`.
diff --git a/Misc/NEWS.d/next/Library/2025-06-23-10-19-11.gh-issue-135855.-J0AGF.rst b/Misc/NEWS.d/next/Library/2025-06-23-10-19-11.gh-issue-135855.-J0AGF.rst
new file mode 100644
index 00000000000..fcf495bdceb
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-06-23-10-19-11.gh-issue-135855.-J0AGF.rst
@@ -0,0 +1,3 @@
+Raise :exc:`TypeError` instead of :exc:`SystemError` when
+:func:`!_interpreters.set___main___attrs` is passed a non-dict object.
+Patch by Brian Schubert.
diff --git a/Misc/NEWS.d/next/Library/2025-06-24-14-43-24.gh-issue-135878.Db4roX.rst b/Misc/NEWS.d/next/Library/2025-06-24-14-43-24.gh-issue-135878.Db4roX.rst
new file mode 100644
index 00000000000..969cf2dfa40
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-06-24-14-43-24.gh-issue-135878.Db4roX.rst
@@ -0,0 +1,3 @@
+Fixes a crash of :class:`types.SimpleNamespace` on :term:`free threading` builds,
+when several threads were calling its :meth:`~object.__repr__` method at the
+same time.
diff --git a/Misc/NEWS.d/next/Tests/2025-06-19-15-29-38.gh-issue-135494.FVl9a0.rst b/Misc/NEWS.d/next/Tests/2025-06-19-15-29-38.gh-issue-135494.FVl9a0.rst
new file mode 100644
index 00000000000..832d1fe033e
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2025-06-19-15-29-38.gh-issue-135494.FVl9a0.rst
@@ -0,0 +1,2 @@
+Fix regrtest to support excluding tests from ``--pgo`` tests. Patch by
+Victor Stinner.
diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c
index e201ccd53da..b920c32474f 100644
--- a/Modules/_interpretersmodule.c
+++ b/Modules/_interpretersmodule.c
@@ -1039,8 +1039,8 @@ interp_set___main___attrs(PyObject *self, PyObject *args, PyObject *kwargs)
PyObject *id, *updates;
int restricted = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
- "OO|$p:" MODULE_NAME_STR ".set___main___attrs",
- kwlist, &id, &updates, &restricted))
+ "OO!|$p:" MODULE_NAME_STR ".set___main___attrs",
+ kwlist, &id, &PyDict_Type, &updates, &restricted))
{
return NULL;
}
@@ -1054,16 +1054,14 @@ interp_set___main___attrs(PyObject *self, PyObject *args, PyObject *kwargs)
}
// Check the updates.
- if (updates != Py_None) {
- Py_ssize_t size = PyObject_Size(updates);
- if (size < 0) {
- return NULL;
- }
- if (size == 0) {
- PyErr_SetString(PyExc_ValueError,
- "arg 2 must be a non-empty mapping");
- return NULL;
- }
+ Py_ssize_t size = PyDict_Size(updates);
+ if (size < 0) {
+ return NULL;
+ }
+ if (size == 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "arg 2 must be a non-empty dict");
+ return NULL;
}
_PyXI_session *session = _PyXI_NewSession();
diff --git a/Modules/blake2module.c b/Modules/blake2module.c
index 6c4349ac06b..9e279e11b51 100644
--- a/Modules/blake2module.c
+++ b/Modules/blake2module.c
@@ -787,17 +787,19 @@ error:
/*[clinic input]
_blake2.blake2b.copy
+ cls: defining_class
+
Return a copy of the hash object.
[clinic start generated code]*/
static PyObject *
-_blake2_blake2b_copy_impl(Blake2Object *self)
-/*[clinic end generated code: output=622d1c56b91c50d8 input=e383c2d199fd8a2e]*/
+_blake2_blake2b_copy_impl(Blake2Object *self, PyTypeObject *cls)
+/*[clinic end generated code: output=5f8ea31c56c52287 input=f38f3475e9aec98d]*/
{
int rc;
Blake2Object *cpy;
- if ((cpy = new_Blake2Object(Py_TYPE(self))) == NULL) {
+ if ((cpy = new_Blake2Object(cls)) == NULL) {
return NULL;
}
diff --git a/Modules/clinic/blake2module.c.h b/Modules/clinic/blake2module.c.h
index 9e9cd56e569..97d010d03a4 100644
--- a/Modules/clinic/blake2module.c.h
+++ b/Modules/clinic/blake2module.c.h
@@ -434,15 +434,19 @@ PyDoc_STRVAR(_blake2_blake2b_copy__doc__,
"Return a copy of the hash object.");
#define _BLAKE2_BLAKE2B_COPY_METHODDEF \
- {"copy", (PyCFunction)_blake2_blake2b_copy, METH_NOARGS, _blake2_blake2b_copy__doc__},
+ {"copy", _PyCFunction_CAST(_blake2_blake2b_copy), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _blake2_blake2b_copy__doc__},
static PyObject *
-_blake2_blake2b_copy_impl(Blake2Object *self);
+_blake2_blake2b_copy_impl(Blake2Object *self, PyTypeObject *cls);
static PyObject *
-_blake2_blake2b_copy(PyObject *self, PyObject *Py_UNUSED(ignored))
+_blake2_blake2b_copy(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
- return _blake2_blake2b_copy_impl((Blake2Object *)self);
+ if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) {
+ PyErr_SetString(PyExc_TypeError, "copy() takes no arguments");
+ return NULL;
+ }
+ return _blake2_blake2b_copy_impl((Blake2Object *)self, cls);
}
PyDoc_STRVAR(_blake2_blake2b_update__doc__,
@@ -502,4 +506,4 @@ _blake2_blake2b_hexdigest(PyObject *self, PyObject *Py_UNUSED(ignored))
{
return _blake2_blake2b_hexdigest_impl((Blake2Object *)self);
}
-/*[clinic end generated code: output=eed18dcfaf6f7731 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=60a4abbcb8950fe5 input=a9049054013a1b77]*/
diff --git a/Modules/clinic/sha3module.c.h b/Modules/clinic/sha3module.c.h
index d6d44bf4c51..1f631ff406e 100644
--- a/Modules/clinic/sha3module.c.h
+++ b/Modules/clinic/sha3module.c.h
@@ -100,15 +100,19 @@ PyDoc_STRVAR(_sha3_sha3_224_copy__doc__,
"Return a copy of the hash object.");
#define _SHA3_SHA3_224_COPY_METHODDEF \
- {"copy", (PyCFunction)_sha3_sha3_224_copy, METH_NOARGS, _sha3_sha3_224_copy__doc__},
+ {"copy", _PyCFunction_CAST(_sha3_sha3_224_copy), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _sha3_sha3_224_copy__doc__},
static PyObject *
-_sha3_sha3_224_copy_impl(SHA3object *self);
+_sha3_sha3_224_copy_impl(SHA3object *self, PyTypeObject *cls);
static PyObject *
-_sha3_sha3_224_copy(PyObject *self, PyObject *Py_UNUSED(ignored))
+_sha3_sha3_224_copy(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
- return _sha3_sha3_224_copy_impl((SHA3object *)self);
+ if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) {
+ PyErr_SetString(PyExc_TypeError, "copy() takes no arguments");
+ return NULL;
+ }
+ return _sha3_sha3_224_copy_impl((SHA3object *)self, cls);
}
PyDoc_STRVAR(_sha3_sha3_224_digest__doc__,
@@ -306,4 +310,4 @@ _sha3_shake_128_hexdigest(PyObject *self, PyObject *const *args, Py_ssize_t narg
exit:
return return_value;
}
-/*[clinic end generated code: output=c17e3ec670afe253 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=48be77f8a31e8a3e input=a9049054013a1b77]*/
diff --git a/Modules/sha3module.c b/Modules/sha3module.c
index face90a6094..5764556bb68 100644
--- a/Modules/sha3module.c
+++ b/Modules/sha3module.c
@@ -239,16 +239,17 @@ SHA3_traverse(PyObject *self, visitproc visit, void *arg)
/*[clinic input]
_sha3.sha3_224.copy
+ cls: defining_class
+
Return a copy of the hash object.
[clinic start generated code]*/
static PyObject *
-_sha3_sha3_224_copy_impl(SHA3object *self)
-/*[clinic end generated code: output=6c537411ecdcda4c input=93a44aaebea51ba8]*/
+_sha3_sha3_224_copy_impl(SHA3object *self, PyTypeObject *cls)
+/*[clinic end generated code: output=13958b44c244013e input=7134b4dc0a2fbcac]*/
{
SHA3object *newobj;
-
- if ((newobj = newSHA3object(Py_TYPE(self))) == NULL) {
+ if ((newobj = newSHA3object(cls)) == NULL) {
return NULL;
}
HASHLIB_ACQUIRE_LOCK(self);
diff --git a/Objects/namespaceobject.c b/Objects/namespaceobject.c
index caebe6bf543..0fc2bcea4cb 100644
--- a/Objects/namespaceobject.c
+++ b/Objects/namespaceobject.c
@@ -124,9 +124,10 @@ namespace_repr(PyObject *ns)
if (PyUnicode_Check(key) && PyUnicode_GET_LENGTH(key) > 0) {
PyObject *value, *item;
- value = PyDict_GetItemWithError(d, key);
- if (value != NULL) {
+ int has_key = PyDict_GetItemRef(d, key, &value);
+ if (has_key == 1) {
item = PyUnicode_FromFormat("%U=%R", key, value);
+ Py_DECREF(value);
if (item == NULL) {
loop_error = 1;
}
@@ -135,7 +136,7 @@ namespace_repr(PyObject *ns)
Py_DECREF(item);
}
}
- else if (PyErr_Occurred()) {
+ else if (has_key < 0) {
loop_error = 1;
}
}
diff --git a/Objects/object.c b/Objects/object.c
index 6a581a19604..4d60128b092 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -3034,57 +3034,28 @@ finally:
/* Trashcan support. */
-#ifndef Py_GIL_DISABLED
-/* We need to store a pointer in the refcount field of
- * an object. It is important that we never store 0 (NULL).
-* It is also important to not make the object appear immortal,
-* or it might be untracked by the cycle GC. */
-static uintptr_t
-pointer_to_safe_refcount(void *ptr)
-{
- uintptr_t full = (uintptr_t)ptr;
- assert((full & 3) == 0);
-#if SIZEOF_VOID_P > 4
- uint32_t refcnt = (uint32_t)full;
- if (refcnt >= (uint32_t)_Py_IMMORTAL_MINIMUM_REFCNT) {
- full = full - ((uintptr_t)_Py_IMMORTAL_MINIMUM_REFCNT) + 1;
- }
- return full + 2;
-#else
- // Make the top two bits 0, so it appears mortal.
- return (full >> 2) + 1;
-#endif
-}
-
-static void *
-safe_refcount_to_pointer(uintptr_t refcnt)
-{
-#if SIZEOF_VOID_P > 4
- if (refcnt & 1) {
- refcnt += _Py_IMMORTAL_MINIMUM_REFCNT - 1;
- }
- return (void *)(refcnt - 2);
-#else
- return (void *)((refcnt -1) << 2);
-#endif
-}
-#endif
-
/* Add op to the gcstate->trash_delete_later list. Called when the current
- * call-stack depth gets large. op must be a currently untracked gc'ed
- * object, with refcount 0. Py_DECREF must already have been called on it.
+ * call-stack depth gets large. op must be a gc'ed object, with refcount 0.
+ * Py_DECREF must already have been called on it.
*/
void
_PyTrash_thread_deposit_object(PyThreadState *tstate, PyObject *op)
{
_PyObject_ASSERT(op, Py_REFCNT(op) == 0);
+ PyTypeObject *tp = Py_TYPE(op);
+ assert(tp->tp_flags & Py_TPFLAGS_HAVE_GC);
+ int tracked = 0;
+ if (tp->tp_is_gc == NULL || tp->tp_is_gc(op)) {
+ tracked = _PyObject_GC_IS_TRACKED(op);
+ if (tracked) {
+ _PyObject_GC_UNTRACK(op);
+ }
+ }
+ uintptr_t tagged_ptr = ((uintptr_t)tstate->delete_later) | tracked;
#ifdef Py_GIL_DISABLED
- op->ob_tid = (uintptr_t)tstate->delete_later;
+ op->ob_tid = tagged_ptr;
#else
- /* Store the delete_later pointer in the refcnt field. */
- uintptr_t refcnt = pointer_to_safe_refcount(tstate->delete_later);
- *((uintptr_t*)op) = refcnt;
- assert(!_Py_IsImmortal(op));
+ _Py_AS_GC(op)->_gc_next = tagged_ptr;
#endif
tstate->delete_later = op;
}
@@ -3099,17 +3070,17 @@ _PyTrash_thread_destroy_chain(PyThreadState *tstate)
destructor dealloc = Py_TYPE(op)->tp_dealloc;
#ifdef Py_GIL_DISABLED
- tstate->delete_later = (PyObject*) op->ob_tid;
+ uintptr_t tagged_ptr = op->ob_tid;
op->ob_tid = 0;
_Py_atomic_store_ssize_relaxed(&op->ob_ref_shared, _Py_REF_MERGED);
#else
- /* Get the delete_later pointer from the refcnt field.
- * See _PyTrash_thread_deposit_object(). */
- uintptr_t refcnt = *((uintptr_t*)op);
- tstate->delete_later = safe_refcount_to_pointer(refcnt);
- op->ob_refcnt = 0;
+ uintptr_t tagged_ptr = _Py_AS_GC(op)->_gc_next;
+ _Py_AS_GC(op)->_gc_next = 0;
#endif
-
+ tstate->delete_later = (PyObject *)(tagged_ptr & ~1);
+ if (tagged_ptr & 1) {
+ _PyObject_GC_TRACK(op);
+ }
/* Call the deallocator directly. This used to try to
* fool Py_DECREF into calling it indirectly, but
* Py_DECREF was already called on this object, and in
@@ -3183,10 +3154,11 @@ void
_Py_Dealloc(PyObject *op)
{
PyTypeObject *type = Py_TYPE(op);
+ unsigned long gc_flag = type->tp_flags & Py_TPFLAGS_HAVE_GC;
destructor dealloc = type->tp_dealloc;
PyThreadState *tstate = _PyThreadState_GET();
intptr_t margin = _Py_RecursionLimit_GetMargin(tstate);
- if (margin < 2) {
+ if (margin < 2 && gc_flag) {
_PyTrash_thread_deposit_object(tstate, (PyObject *)op);
return;
}
@@ -3232,7 +3204,7 @@ _Py_Dealloc(PyObject *op)
Py_XDECREF(old_exc);
Py_DECREF(type);
#endif
- if (tstate->delete_later && margin >= 4) {
+ if (tstate->delete_later && margin >= 4 && gc_flag) {
_PyTrash_thread_destroy_chain(tstate);
}
}
diff --git a/Python/crossinterp.c b/Python/crossinterp.c
index c44bcd55900..16a23f0351c 100644
--- a/Python/crossinterp.c
+++ b/Python/crossinterp.c
@@ -2617,6 +2617,7 @@ _PyXI_Enter(_PyXI_session *session,
// Convert the attrs for cross-interpreter use.
_PyXI_namespace *sharedns = NULL;
if (nsupdates != NULL) {
+ assert(PyDict_Check(nsupdates));
Py_ssize_t len = PyDict_Size(nsupdates);
if (len < 0) {
if (result != NULL) {
diff --git a/Tools/wasm/emscripten/__main__.py b/Tools/wasm/emscripten/__main__.py
index 849bd5de44e..c0d58aeaadd 100644
--- a/Tools/wasm/emscripten/__main__.py
+++ b/Tools/wasm/emscripten/__main__.py
@@ -167,11 +167,12 @@ def make_build_python(context, working_dir):
@subdir(HOST_BUILD_DIR, clean_ok=True)
def make_emscripten_libffi(context, working_dir):
shutil.rmtree(working_dir / "libffi-3.4.6", ignore_errors=True)
- with tempfile.NamedTemporaryFile(suffix=".tar.gz") as tmp_file:
+ with tempfile.NamedTemporaryFile(suffix=".tar.gz", delete_on_close=False) as tmp_file:
with urlopen(
"https://github.com/libffi/libffi/releases/download/v3.4.6/libffi-3.4.6.tar.gz"
) as response:
shutil.copyfileobj(response, tmp_file)
+ tmp_file.close()
shutil.unpack_archive(tmp_file.name, working_dir)
call(
[EMSCRIPTEN_DIR / "make_libffi.sh"],