Skip to content

Commit 06a9ec1

Browse files
authored
Merge branch 'main' into branch-unique-reference-tracking
2 parents e476061 + 706fd4e commit 06a9ec1

File tree

14 files changed

+176
-42
lines changed

14 files changed

+176
-42
lines changed

.github/CODEOWNERS

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ Tools/c-analyzer/ @ericsnowcurrently
132132
Tools/check-c-api-docs/ @ZeroIntensity
133133

134134
# Fuzzing
135-
Modules/_xxtestfuzz/ @ammaraskar
135+
Modules/_xxtestfuzz/ @python/fuzzers
136+
Lib/test/test_xxtestfuzz.py @python/fuzzers
137+
.github/workflows/reusable-cifuzz.yml @python/fuzzers
136138

137139
# Limited C API & Stable ABI
138140
Doc/c-api/stable.rst @encukou

Doc/c-api/memory.rst

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,11 @@ The following function sets, modeled after the ANSI C standard, but specifying
204204
behavior when requesting zero bytes, are available for allocating and releasing
205205
memory from the Python heap.
206206
207-
The :ref:`default memory allocator <default-memory-allocators>` uses the
208-
:ref:`pymalloc memory allocator <pymalloc>`.
207+
In the GIL-enabled build (default build) the
208+
:ref:`default memory allocator <default-memory-allocators>` uses the
209+
:ref:`pymalloc memory allocator <pymalloc>`, whereas in the
210+
:term:`free-threaded build`, the default is the
211+
:ref:`mimalloc memory allocator <mimalloc>` instead.
209212
210213
.. warning::
211214
@@ -215,6 +218,11 @@ The :ref:`default memory allocator <default-memory-allocators>` uses the
215218
216219
The default allocator is now pymalloc instead of system :c:func:`malloc`.
217220
221+
.. versionchanged:: 3.13
222+
223+
In the :term:`free-threaded <free threading>` build, the default allocator
224+
is now :ref:`mimalloc <mimalloc>`.
225+
218226
.. c:function:: void* PyMem_Malloc(size_t n)
219227
220228
Allocates *n* bytes and returns a pointer of type :c:expr:`void*` to the
@@ -340,7 +348,9 @@ memory from the Python heap.
340348
the :ref:`Customize Memory Allocators <customize-memory-allocators>` section.
341349
342350
The :ref:`default object allocator <default-memory-allocators>` uses the
343-
:ref:`pymalloc memory allocator <pymalloc>`.
351+
:ref:`pymalloc memory allocator <pymalloc>`. In the
352+
:term:`free-threaded <free threading>` build, the default is the
353+
:ref:`mimalloc memory allocator <mimalloc>` instead.
344354
345355
.. warning::
346356
@@ -420,23 +430,24 @@ Default Memory Allocators
420430
421431
Default memory allocators:
422432
423-
=============================== ==================== ================== ===================== ====================
424-
Configuration Name PyMem_RawMalloc PyMem_Malloc PyObject_Malloc
425-
=============================== ==================== ================== ===================== ====================
426-
Release build ``"pymalloc"`` ``malloc`` ``pymalloc`` ``pymalloc``
427-
Debug build ``"pymalloc_debug"`` ``malloc`` + debug ``pymalloc`` + debug ``pymalloc`` + debug
428-
Release build, without pymalloc ``"malloc"`` ``malloc`` ``malloc`` ``malloc``
429-
Debug build, without pymalloc ``"malloc_debug"`` ``malloc`` + debug ``malloc`` + debug ``malloc`` + debug
430-
=============================== ==================== ================== ===================== ====================
433+
=================================== ======================= ==================== ====================== ======================
434+
Configuration Name PyMem_RawMalloc PyMem_Malloc PyObject_Malloc
435+
=================================== ======================= ==================== ====================== ======================
436+
Release build ``"pymalloc"`` ``malloc`` ``pymalloc`` ``pymalloc``
437+
Debug build ``"pymalloc_debug"`` ``malloc`` + debug ``pymalloc`` + debug ``pymalloc`` + debug
438+
Release build, without pymalloc ``"malloc"`` ``malloc`` ``malloc`` ``malloc``
439+
Debug build, without pymalloc ``"malloc_debug"`` ``malloc`` + debug ``malloc`` + debug ``malloc`` + debug
440+
Free-threaded build ``"mimalloc"`` ``mimalloc`` ``mimalloc`` ``mimalloc``
441+
Free-threaded debug build ``"mimalloc_debug"`` ``mimalloc`` + debug ``mimalloc`` + debug ``mimalloc`` + debug
442+
=================================== ======================= ==================== ====================== ======================
431443
432444
Legend:
433445
434446
* Name: value for :envvar:`PYTHONMALLOC` environment variable.
435447
* ``malloc``: system allocators from the standard C library, C functions:
436448
:c:func:`malloc`, :c:func:`calloc`, :c:func:`realloc` and :c:func:`free`.
437449
* ``pymalloc``: :ref:`pymalloc memory allocator <pymalloc>`.
438-
* ``mimalloc``: :ref:`mimalloc memory allocator <mimalloc>`. The pymalloc
439-
allocator will be used if mimalloc support isn't available.
450+
* ``mimalloc``: :ref:`mimalloc memory allocator <mimalloc>`.
440451
* "+ debug": with :ref:`debug hooks on the Python memory allocators
441452
<pymem-debug-hooks>`.
442453
* "Debug build": :ref:`Python build in debug mode <debug-build>`.
@@ -733,9 +744,27 @@ The mimalloc allocator
733744
734745
.. versionadded:: 3.13
735746
736-
Python supports the mimalloc allocator when the underlying platform support is available.
737-
mimalloc "is a general purpose allocator with excellent performance characteristics.
738-
Initially developed by Daan Leijen for the runtime systems of the Koka and Lean languages."
747+
Python supports the `mimalloc <https://github.com/microsoft/mimalloc/>`__
748+
allocator when the underlying platform support is available.
749+
mimalloc is a general purpose allocator with excellent performance
750+
characteristics, initially developed by Daan Leijen for the runtime systems
751+
of the Koka and Lean languages.
752+
753+
Unlike :ref:`pymalloc <pymalloc>`, which is optimized for small objects (512
754+
bytes or fewer), mimalloc handles allocations of any size.
755+
756+
In the :term:`free-threaded <free threading>` build, mimalloc is the default
757+
and **required** allocator for the :c:macro:`PYMEM_DOMAIN_MEM` and
758+
:c:macro:`PYMEM_DOMAIN_OBJ` domains. It cannot be disabled in free-threaded
759+
builds. The free-threaded build uses per-thread mimalloc heaps, which allows
760+
allocation and deallocation to proceed without locking in most cases.
761+
762+
In the default (non-free-threaded) build, mimalloc is available but not the
763+
default allocator. It can be selected at runtime using
764+
:envvar:`PYTHONMALLOC`\ ``=mimalloc`` (or ``mimalloc_debug`` to include
765+
:ref:`debug hooks <pymem-debug-hooks>`). It can be disabled at build time
766+
using the :option:`--without-mimalloc` configure option, but this option
767+
cannot be combined with :option:`--disable-gil`.
739768
740769
tracemalloc C API
741770
=================

Doc/using/cmdline.rst

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,13 @@ conflict.
10851085
* ``pymalloc_debug``: same as ``pymalloc`` but also install debug hooks.
10861086
* ``mimalloc_debug``: same as ``mimalloc`` but also install debug hooks.
10871087

1088+
.. note::
1089+
1090+
In the :term:`free-threaded <free threading>` build, the ``malloc``,
1091+
``malloc_debug``, ``pymalloc``, and ``pymalloc_debug`` values are not
1092+
supported. Only ``default``, ``debug``, ``mimalloc``, and
1093+
``mimalloc_debug`` are accepted.
1094+
10881095
.. versionadded:: 3.6
10891096

10901097
.. versionchanged:: 3.7
@@ -1094,12 +1101,13 @@ conflict.
10941101
.. envvar:: PYTHONMALLOCSTATS
10951102

10961103
If set to a non-empty string, Python will print statistics of the
1097-
:ref:`pymalloc memory allocator <pymalloc>` every time a new pymalloc object
1098-
arena is created, and on shutdown.
1104+
:ref:`pymalloc memory allocator <pymalloc>` or the
1105+
:ref:`mimalloc memory allocator <mimalloc>` (whichever is in use)
1106+
every time a new object arena is created, and on shutdown.
10991107

11001108
This variable is ignored if the :envvar:`PYTHONMALLOC` environment variable
11011109
is used to force the :c:func:`malloc` allocator of the C library, or if
1102-
Python is configured without ``pymalloc`` support.
1110+
Python is configured without both ``pymalloc`` and ``mimalloc`` support.
11031111

11041112
.. versionchanged:: 3.6
11051113
This variable can now also be used on Python compiled in release mode.

Doc/using/configure.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,9 @@ also be used to improve performance.
774774
Disable the fast :ref:`mimalloc <mimalloc>` allocator
775775
(enabled by default).
776776

777+
This option cannot be used together with :option:`--disable-gil`
778+
because the :term:`free-threaded <free threading>` build requires mimalloc.
779+
777780
See also :envvar:`PYTHONMALLOC` environment variable.
778781

779782
.. option:: --without-pymalloc

Include/cpython/pystate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ struct _ts {
198198
_PyStackChunk *datastack_chunk;
199199
PyObject **datastack_top;
200200
PyObject **datastack_limit;
201+
_PyStackChunk *datastack_cached_chunk;
201202
/* XXX signal handlers should also be here */
202203

203204
/* The following fields are here to avoid allocation during init.

Lib/tarfile.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,20 @@ def _create_pax_generic_header(cls, pax_headers, type, encoding):
12761276
@classmethod
12771277
def frombuf(cls, buf, encoding, errors):
12781278
"""Construct a TarInfo object from a 512 byte bytes object.
1279+
1280+
To support the old v7 tar format AREGTYPE headers are
1281+
transformed to DIRTYPE headers if their name ends in '/'.
1282+
"""
1283+
return cls._frombuf(buf, encoding, errors)
1284+
1285+
@classmethod
1286+
def _frombuf(cls, buf, encoding, errors, *, dircheck=True):
1287+
"""Construct a TarInfo object from a 512 byte bytes object.
1288+
1289+
If ``dircheck`` is set to ``True`` then ``AREGTYPE`` headers will
1290+
be normalized to ``DIRTYPE`` if the name ends in a trailing slash.
1291+
``dircheck`` must be set to ``False`` if this function is called
1292+
on a follow-up header such as ``GNUTYPE_LONGNAME``.
12791293
"""
12801294
if len(buf) == 0:
12811295
raise EmptyHeaderError("empty header")
@@ -1306,7 +1320,7 @@ def frombuf(cls, buf, encoding, errors):
13061320

13071321
# Old V7 tar format represents a directory as a regular
13081322
# file with a trailing slash.
1309-
if obj.type == AREGTYPE and obj.name.endswith("/"):
1323+
if dircheck and obj.type == AREGTYPE and obj.name.endswith("/"):
13101324
obj.type = DIRTYPE
13111325

13121326
# The old GNU sparse format occupies some of the unused
@@ -1341,8 +1355,15 @@ def fromtarfile(cls, tarfile):
13411355
"""Return the next TarInfo object from TarFile object
13421356
tarfile.
13431357
"""
1358+
return cls._fromtarfile(tarfile)
1359+
1360+
@classmethod
1361+
def _fromtarfile(cls, tarfile, *, dircheck=True):
1362+
"""
1363+
See dircheck documentation in _frombuf().
1364+
"""
13441365
buf = tarfile.fileobj.read(BLOCKSIZE)
1345-
obj = cls.frombuf(buf, tarfile.encoding, tarfile.errors)
1366+
obj = cls._frombuf(buf, tarfile.encoding, tarfile.errors, dircheck=dircheck)
13461367
obj.offset = tarfile.fileobj.tell() - BLOCKSIZE
13471368
return obj._proc_member(tarfile)
13481369

@@ -1400,7 +1421,7 @@ def _proc_gnulong(self, tarfile):
14001421

14011422
# Fetch the next header and process it.
14021423
try:
1403-
next = self.fromtarfile(tarfile)
1424+
next = self._fromtarfile(tarfile, dircheck=False)
14041425
except HeaderError as e:
14051426
raise SubsequentHeaderError(str(e)) from None
14061427

@@ -1535,7 +1556,7 @@ def _proc_pax(self, tarfile):
15351556

15361557
# Fetch the next header.
15371558
try:
1538-
next = self.fromtarfile(tarfile)
1559+
next = self._fromtarfile(tarfile, dircheck=False)
15391560
except HeaderError as e:
15401561
raise SubsequentHeaderError(str(e)) from None
15411562

Lib/test/test_tarfile.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,6 +1234,25 @@ def test_longname_directory(self):
12341234
self.assertIsNotNone(tar.getmember(longdir))
12351235
self.assertIsNotNone(tar.getmember(longdir.removesuffix('/')))
12361236

1237+
def test_longname_file_not_directory(self):
1238+
# Test reading a longname file and ensure it is not handled as a directory
1239+
# Issue #141707
1240+
buf = io.BytesIO()
1241+
with tarfile.open(mode='w', fileobj=buf, format=self.format) as tar:
1242+
ti = tarfile.TarInfo()
1243+
ti.type = tarfile.AREGTYPE
1244+
ti.name = ('a' * 99) + '/' + ('b' * 3)
1245+
tar.addfile(ti)
1246+
1247+
expected = {t.name: t.type for t in tar.getmembers()}
1248+
1249+
buf.seek(0)
1250+
with tarfile.open(mode='r', fileobj=buf) as tar:
1251+
actual = {t.name: t.type for t in tar.getmembers()}
1252+
1253+
self.assertEqual(expected, actual)
1254+
1255+
12371256
class GNUReadTest(LongnameTest, ReadTest, unittest.TestCase):
12381257

12391258
subdir = "gnu"

Lib/test/test_traceback.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4213,6 +4213,27 @@ def method(self, name):
42134213
self.assertIn("'._bluch'", self.get_suggestion(partial(B().method, '_luch')))
42144214
self.assertIn("'._bluch'", self.get_suggestion(partial(B().method, 'bluch')))
42154215

4216+
def test_suggestions_with_custom___dir__(self):
4217+
class M(type):
4218+
def __dir__(cls):
4219+
return [None, "fox"]
4220+
4221+
class C0:
4222+
def __dir__(self):
4223+
return [..., "bluch"]
4224+
4225+
class C1(C0, metaclass=M):
4226+
pass
4227+
4228+
self.assertNotIn("'.bluch'", self.get_suggestion(C0, "blach"))
4229+
self.assertIn("'.bluch'", self.get_suggestion(C0(), "blach"))
4230+
4231+
self.assertIn("'.fox'", self.get_suggestion(C1, "foo"))
4232+
self.assertNotIn("'.fox'", self.get_suggestion(C1(), "foo"))
4233+
4234+
self.assertNotIn("'.bluch'", self.get_suggestion(C1, "blach"))
4235+
self.assertIn("'.bluch'", self.get_suggestion(C1(), "blach"))
4236+
42164237

42174238
def test_do_not_trigger_for_long_attributes(self):
42184239
class A:

Lib/traceback.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,6 +1698,19 @@ def _check_for_nested_attribute(obj, wrong_name, attrs):
16981698
return None
16991699

17001700

1701+
def _get_safe___dir__(obj):
1702+
# Use obj.__dir__() to avoid a TypeError when calling dir(obj).
1703+
# See gh-131001 and gh-139933.
1704+
# Also filters out lazy imports to avoid triggering module loading.
1705+
try:
1706+
d = obj.__dir__()
1707+
except TypeError: # when obj is a class
1708+
d = type(obj).__dir__(obj)
1709+
return sorted(
1710+
x for x in d if isinstance(x, str) and not _is_lazy_import(obj, x)
1711+
)
1712+
1713+
17011714
def _compute_suggestion_error(exc_value, tb, wrong_name):
17021715
if wrong_name is None or not isinstance(wrong_name, str):
17031716
return None
@@ -1711,13 +1724,7 @@ def _compute_suggestion_error(exc_value, tb, wrong_name):
17111724
if isinstance(exc_value, AttributeError):
17121725
obj = exc_value.obj
17131726
try:
1714-
try:
1715-
d = dir(obj)
1716-
except TypeError: # Attributes are unsortable, e.g. int and str
1717-
d = list(obj.__class__.__dict__.keys()) + list(obj.__dict__.keys())
1718-
d = sorted([x for x in d if isinstance(x, str)])
1719-
# Filter out lazy imports to avoid triggering module loading
1720-
d = [x for x in d if not _is_lazy_import(obj, x)]
1727+
d = _get_safe___dir__(obj)
17211728
hide_underscored = (wrong_name[:1] != '_')
17221729
if hide_underscored and tb is not None:
17231730
while tb.tb_next is not None:
@@ -1744,13 +1751,7 @@ def _compute_suggestion_error(exc_value, tb, wrong_name):
17441751
elif isinstance(exc_value, ImportError):
17451752
try:
17461753
mod = __import__(exc_value.name)
1747-
try:
1748-
d = dir(mod)
1749-
except TypeError: # Attributes are unsortable, e.g. int and str
1750-
d = list(mod.__dict__.keys())
1751-
d = sorted([x for x in d if isinstance(x, str)])
1752-
# Filter out lazy imports to avoid triggering module loading
1753-
d = [x for x in d if not _is_lazy_import(mod, x)]
1754+
d = _get_safe___dir__(mod)
17541755
if wrong_name[:1] != '_':
17551756
d = [x for x in d if x[:1] != '_']
17561757
except Exception:

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,6 +1557,7 @@ Ashwin Ramaswami
15571557
Jeff Ramnani
15581558
Grant Ramsay
15591559
Bayard Randel
1560+
Eashwar Ranganathan
15601561
Varpu Rantala
15611562
Brodie Rao
15621563
Rémi Rampin

0 commit comments

Comments
 (0)