Skip to content

Commit 991741e

Browse files
authored
Merge branch 'main' into fix-zoneinfo-159715
2 parents b9374b4 + 7641c5d commit 991741e

31 files changed

Lines changed: 478 additions & 58 deletions

‎Doc/library/ctypes.rst‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2052,7 +2052,11 @@ See :ref:`ctypes-callback-functions` for examples.
20522052
.. function:: PYFUNCTYPE(restype, *argtypes)
20532053

20542054
The returned function prototype creates functions that use the Python calling
2055-
convention. The function will *not* release the GIL during the call.
2055+
convention. Unlike :func:`CFUNCTYPE` and :func:`WINFUNCTYPE`, ctypes does
2056+
*not* release the GIL before calling the foreign function, so the function
2057+
may safely use the Python C API. Note that this does not prevent the GIL
2058+
from being released *during* the call.
2059+
20562060

20572061
Function prototypes created by these factory functions can be instantiated in
20582062
different ways, depending on the type and number of the parameters in the call:

‎Doc/library/gc.rst‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ The :mod:`!gc` module provides the following functions:
7575
returned. If *generation* is not ``None``, return only the objects tracked by
7676
the collector that are in that generation.
7777

78+
.. warning::
79+
Care must be taken when using objects returned by :func:`get_objects` because
80+
some of them could still be under construction and hence in a temporarily
81+
invalid state. Avoid using :func:`get_objects` for any purpose other than
82+
debugging.
83+
7884
.. versionchanged:: 3.8
7985
New *generation* parameter.
8086

‎Include/internal/pycore_global_objects_fini_generated.h‎

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

‎Include/internal/pycore_global_strings.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,7 @@ struct _Py_global_strings {
685685
STRUCT_FOR_ID(nlocals)
686686
STRUCT_FOR_ID(node_depth)
687687
STRUCT_FOR_ID(node_offset)
688+
STRUCT_FOR_ID(nothing)
688689
STRUCT_FOR_ID(ns)
689690
STRUCT_FOR_ID(nstype)
690691
STRUCT_FOR_ID(nt)

‎Include/internal/pycore_runtime_init_generated.h‎

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

‎Include/internal/pycore_unicodeobject_generated.h‎

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

‎Lib/enum.py‎

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ class member(object):
3434
def __init__(self, value):
3535
self.value = value
3636

37+
def _hashable(obj):
38+
"""
39+
Returns True if obj is hashable, False otherwise.
40+
"""
41+
try:
42+
hash(obj)
43+
return True
44+
except TypeError:
45+
return False
46+
3747
def _is_descriptor(obj):
3848
"""
3949
Returns True if obj is a descriptor, False otherwise.
@@ -313,14 +323,13 @@ def __set_name__(self, enum_class, member_name):
313323

314324
enum_class._add_member_(member_name, enum_member)
315325
try:
316-
# This may fail if value is not hashable. We can't add the value
317-
# to the map, and by-value lookups for this value will be
318-
# linear.
326+
# This may fail if value is not hashable.
319327
enum_class._value2member_map_.setdefault(value, enum_member)
320-
if value not in enum_class._hashable_values_:
321-
enum_class._hashable_values_.append(value)
328+
enum_class._hashable_values_.add(value)
322329
except TypeError:
323-
# keep track of the value in a list so containment checks are quick
330+
# We can't add the value to the map; by-value lookups for this
331+
# value will be linear. keep track of the value in a list so
332+
# containment checks are quicker
324333
enum_class._unhashable_values_.append(value)
325334
enum_class._unhashable_values_map_.setdefault(member_name, []).append(value)
326335

@@ -530,7 +539,7 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k
530539
classdict['_member_names_'] = []
531540
classdict['_member_map_'] = {}
532541
classdict['_value2member_map_'] = {}
533-
classdict['_hashable_values_'] = [] # for comparing with non-hashable types
542+
classdict['_hashable_values_'] = set() # for comparing with non-hashable types
534543
classdict['_unhashable_values_'] = [] # e.g. frozenset() with set()
535544
classdict['_unhashable_values_map_'] = {}
536545
classdict['_member_type_'] = member_type
@@ -767,10 +776,10 @@ def __contains__(cls, value):
767776
return isinstance(result, cls)
768777
except ValueError:
769778
pass
770-
return (
771-
value in cls._unhashable_values_ # both structures are lists
772-
or value in cls._hashable_values_
773-
)
779+
try:
780+
return value in cls._hashable_values_ # a set
781+
except TypeError:
782+
return value in cls._unhashable_values_ # a list
774783

775784
def __delattr__(cls, attr):
776785
# nicer error message when someone tries to delete an attribute
@@ -1261,7 +1270,7 @@ def _add_value_alias_(self, value):
12611270
# to the map, and by-value lookups for this value will be
12621271
# linear.
12631272
cls._value2member_map_.setdefault(value, self)
1264-
cls._hashable_values_.append(value)
1273+
cls._hashable_values_.add(value)
12651274
except TypeError:
12661275
# keep track of the value in a list so containment checks are quick
12671276
cls._unhashable_values_.append(value)
@@ -1802,7 +1811,7 @@ def convert_class(cls):
18021811
body['_member_names_'] = member_names = []
18031812
body['_member_map_'] = member_map = {}
18041813
body['_value2member_map_'] = value2member_map = {}
1805-
body['_hashable_values_'] = hashable_values = []
1814+
body['_hashable_values_'] = hashable_values = set()
18061815
body['_unhashable_values_'] = unhashable_values = []
18071816
body['_unhashable_values_map_'] = {}
18081817
body['_member_type_'] = member_type = etype._member_type_
@@ -1866,7 +1875,7 @@ def convert_class(cls):
18661875
contained = value2member_map.get(member._value_)
18671876
except TypeError:
18681877
contained = None
1869-
if member._value_ in unhashable_values or member.value in hashable_values:
1878+
if member._value_ in hashable_values or member.value in unhashable_values:
18701879
for m in enum_class:
18711880
if m._value_ == member._value_:
18721881
contained = m
@@ -1886,7 +1895,7 @@ def convert_class(cls):
18861895
else:
18871896
enum_class._add_member_(name, member)
18881897
value2member_map[value] = member
1889-
hashable_values.append(value)
1898+
hashable_values.add(value)
18901899
if _is_single_bit(value):
18911900
# not a multi-bit alias, record in _member_names_ and _flag_mask_
18921901
member_names.append(name)
@@ -1923,7 +1932,9 @@ def convert_class(cls):
19231932
contained = value2member_map.get(member._value_)
19241933
except TypeError:
19251934
contained = None
1926-
if member._value_ in unhashable_values or member._value_ in hashable_values:
1935+
if (_hashable(member._value_) and member._value_ in hashable_values
1936+
or member._value_ in unhashable_values
1937+
):
19271938
for m in enum_class:
19281939
if m._value_ == member._value_:
19291940
contained = m
@@ -1949,8 +1960,7 @@ def convert_class(cls):
19491960
# to the map, and by-value lookups for this value will be
19501961
# linear.
19511962
enum_class._value2member_map_.setdefault(value, member)
1952-
if value not in hashable_values:
1953-
hashable_values.append(value)
1963+
hashable_values.add(value)
19541964
except TypeError:
19551965
# keep track of the value in a list so containment checks are quick
19561966
enum_class._unhashable_values_.append(value)

‎Lib/hmac.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,9 @@ def digest(key, msg, digest):
267267

268268

269269
def _compute_digest_fallback(key, msg, digest):
270+
if not isinstance(key, (bytes, bytearray)):
271+
raise TypeError(f"key: expected bytes or bytearray, "
272+
f"but got {type(key).__name__!r}")
270273
digest_cons = _get_digest_constructor(digest)
271274
if _is_shake_constructor(digest_cons):
272275
raise ValueError(f"unsupported hash algorithm {digest}")

‎Lib/tarfile.py‎

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -665,13 +665,15 @@ class _FileInFile(object):
665665
object.
666666
"""
667667

668-
def __init__(self, fileobj, offset, size, name, blockinfo=None):
668+
def __init__(self, fileobj, offset, size, name, blockinfo=None,
669+
exception=OSError):
669670
self.fileobj = fileobj
670671
self.offset = offset
671672
self.size = size
672673
self.position = 0
673674
self.name = name
674675
self.closed = False
676+
self.exception = exception
675677

676678
if blockinfo is None:
677679
blockinfo = [(0, size)]
@@ -748,7 +750,10 @@ def read(self, size=None):
748750
length = min(size, stop - self.position)
749751
if data:
750752
self.fileobj.seek(offset + (self.position - start))
751-
b = self.fileobj.read(length)
753+
try:
754+
b = self.fileobj.read(length)
755+
except self.exception as e:
756+
raise ReadError(f"invalid compressed data: {e}") from e
752757
if len(b) != length:
753758
raise ReadError("unexpected end of data")
754759
buf += b
@@ -771,7 +776,8 @@ class ExFileObject(io.BufferedReader):
771776

772777
def __init__(self, tarfile, tarinfo):
773778
fileobj = _FileInFile(tarfile.fileobj, tarinfo.offset_data,
774-
tarinfo.size, tarinfo.name, tarinfo.sparse)
779+
tarinfo.size, tarinfo.name, tarinfo.sparse,
780+
exception=tarfile.exception)
775781
super().__init__(fileobj)
776782
#class ExFileObject
777783

@@ -1133,7 +1139,7 @@ def create_pax_header(self, info, encoding):
11331139
# be represented in ASCII encoding.
11341140
for name, hname, length in (
11351141
("name", "path", LENGTH_NAME), ("linkname", "linkpath", LENGTH_LINK),
1136-
("uname", "uname", 32), ("gname", "gname", 32)):
1142+
("uname", "uname", 31), ("gname", "gname", 31)):
11371143

11381144
if hname in pax_headers:
11391145
# The pax header has priority.
@@ -1177,6 +1183,9 @@ def create_pax_header(self, info, encoding):
11771183
else:
11781184
buf = b""
11791185

1186+
# The owner names in the base header must be NUL-terminated.
1187+
info["uname"] = info["uname"][:31]
1188+
info["gname"] = info["gname"][:31]
11801189
return buf + self._create_header(info, USTAR_FORMAT, "ascii", "replace")
11811190

11821191
@classmethod
@@ -1801,6 +1810,12 @@ class TarFile(object):
18011810

18021811
fileobject = ExFileObject # The file-object for extractfile().
18031812

1813+
exception = OSError # The exception raised by the underlying
1814+
# fileobj.read() on corrupt compressed
1815+
# data past the header (see gzopen() etc.
1816+
# for the more specific exception types
1817+
# used by each compression format).
1818+
18041819
extraction_filter = None # The default filter for extraction.
18051820

18061821
def __init__(self, name=None, mode="r", fileobj=None, format=None,
@@ -2046,6 +2061,7 @@ def gzopen(cls, name, mode="r", fileobj=None, compresslevel=6, **kwargs):
20462061

20472062
try:
20482063
from gzip import GzipFile
2064+
import zlib
20492065
except ImportError:
20502066
raise CompressionError("gzip module is not available") from None
20512067

@@ -2068,6 +2084,7 @@ def gzopen(cls, name, mode="r", fileobj=None, compresslevel=6, **kwargs):
20682084
fileobj.close()
20692085
raise
20702086
t._extfileobj = False
2087+
t.exception = zlib.error
20712088
return t
20722089

20732090
@classmethod
@@ -2124,6 +2141,7 @@ def xzopen(cls, name, mode="r", fileobj=None, preset=None, **kwargs):
21242141
fileobj.close()
21252142
raise
21262143
t._extfileobj = False
2144+
t.exception = LZMAError
21272145
return t
21282146

21292147
@classmethod
@@ -2159,6 +2177,7 @@ def zstopen(cls, name, mode="r", fileobj=None, level=None, options=None,
21592177
fileobj.close()
21602178
raise
21612179
t._extfileobj = False
2180+
t.exception = ZstdError
21622181
return t
21632182

21642183
# All *open() methods are registered here.

‎Lib/test/test_free_threading/test_monitoring.py‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Tests monitoring, sys.settrace, and sys.setprofile in a multi-threaded
22
environment to verify things are thread-safe in a free-threaded build"""
33

4+
import dis
45
import sys
56
import threading
67
import time
@@ -251,6 +252,62 @@ def append(self, trace):
251252

252253
@threading_helper.requires_working_threading()
253254
class MonitoringMisc(MonitoringTestMixin, TestCase):
255+
def test_disable_line_keeps_instruction_events(self):
256+
def func(x):
257+
a = x + 1
258+
b = a * 2
259+
return b
260+
261+
code = func.__code__
262+
expected = [instr.offset for instr in dis.get_instructions(func)
263+
if instr.opname != "RESUME"]
264+
# Two threads, each with two lists of instruction offsets (one per call).
265+
# Check that disabling LINE preserves INSTRUCTION events in every
266+
# thread-local bytecode copy, including on subsequent calls.
267+
records = [[[], []] for _ in range(2)]
268+
local = threading.local()
269+
ready = Barrier(len(records) + 1)
270+
start = threading.Event()
271+
272+
def worker(calls):
273+
# Create each thread's bytecode copy before enabling monitoring.
274+
func(1)
275+
ready.wait()
276+
start.wait()
277+
for instructions in calls:
278+
local.instructions = instructions
279+
func(1)
280+
281+
def instruction(code, offset):
282+
local.instructions.append(offset)
283+
284+
def line(code, lineno):
285+
return monitoring.DISABLE
286+
287+
threads = [Thread(target=worker, args=(calls,)) for calls in records]
288+
try:
289+
with threading_helper.start_threads(threads, unlock=start.set):
290+
ready.wait()
291+
monitoring.register_callback(
292+
self.tool_id, monitoring.events.INSTRUCTION, instruction)
293+
monitoring.register_callback(
294+
self.tool_id, monitoring.events.LINE, line)
295+
monitoring.set_local_events(
296+
self.tool_id, code,
297+
monitoring.events.INSTRUCTION | monitoring.events.LINE)
298+
start.set()
299+
finally:
300+
monitoring.set_local_events(self.tool_id, code, 0)
301+
monitoring.register_callback(
302+
self.tool_id, monitoring.events.INSTRUCTION, None)
303+
monitoring.register_callback(
304+
self.tool_id, monitoring.events.LINE, None)
305+
monitoring.restart_events()
306+
307+
for calls in records:
308+
for instructions in calls:
309+
self.assertEqual(instructions, expected)
310+
254311
def register_callback(self, barrier):
255312
barrier.wait()
256313

0 commit comments

Comments
 (0)