Skip to content

Commit d973897

Browse files
authored
Merge branch '3.15' into backport-6b58f9a-3.15
2 parents 3effeec + 6d7c362 commit d973897

46 files changed

Lines changed: 614 additions & 89 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎Doc/howto/annotations.rst‎

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,10 +243,22 @@ If you use a class with a custom metaclass and access ``__annotations__``
243243
on the class, you may observe unexpected behavior; see
244244
:pep:`749 <749#pep749-metaclasses>` for some examples. You can avoid these
245245
quirks by using :func:`annotationlib.get_annotations` on Python 3.14+ or
246-
:func:`inspect.get_annotations` on Python 3.10+. On earlier versions of
247-
Python, you can avoid these bugs by accessing the annotations from the
248-
class's :attr:`~type.__dict__`
249-
(for example, ``cls.__dict__.get('__annotations__', None)``).
246+
:func:`inspect.get_annotations` on Python 3.10+.
247+
248+
.. note::
249+
250+
On Python 3.14 and later, annotations are evaluated lazily (see
251+
:pep:`649` and :pep:`749`), and how the annotations dict is stored in
252+
the class's :attr:`~type.__dict__` is a private implementation detail.
253+
Reading ``cls.__dict__.get('__annotations__', None)`` may therefore
254+
return ``None`` even when the class has annotations. On these versions,
255+
always use :func:`annotationlib.get_annotations` (or
256+
:func:`inspect.get_annotations`) rather than accessing
257+
:attr:`~type.__dict__` directly.
258+
259+
Only on Python 3.9 and earlier, where neither function is available, can you
260+
avoid these bugs by reading the annotations from the class's
261+
:attr:`~type.__dict__` (for example, ``cls.__dict__.get('__annotations__', None)``).
250262

251263
In some versions of Python, instances of classes may have an ``__annotations__``
252264
attribute. However, this is not supported functionality. If you need the

‎Lib/codecs.py‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -400,9 +400,10 @@ def reset(self):
400400
pass
401401

402402
def seek(self, offset, whence=0):
403-
self.stream.seek(offset, whence)
403+
# reset() may write to the stream: call it at the old position.
404404
if whence == 0 and offset == 0:
405405
self.reset()
406+
self.stream.seek(offset, whence)
406407

407408
def __getattr__(self, name,
408409
getattr=getattr):
@@ -739,10 +740,11 @@ def reset(self):
739740
self.writer.reset()
740741

741742
def seek(self, offset, whence=0):
742-
self.stream.seek(offset, whence)
743-
self.reader.reset()
743+
# writer.reset() may write to the stream: call it at the old position.
744744
if whence == 0 and offset == 0:
745745
self.writer.reset()
746+
self.stream.seek(offset, whence)
747+
self.reader.reset()
746748

747749
def __getattr__(self, name,
748750
getattr=getattr):
@@ -862,8 +864,8 @@ def reset(self):
862864
def seek(self, offset, whence=0):
863865
# Seeks must be propagated to both the readers and writers
864866
# as they might need to reset their internal buffers.
865-
self.reader.seek(offset, whence)
866867
self.writer.seek(offset, whence)
868+
self.reader.seek(offset, whence)
867869

868870
def __getattr__(self, name,
869871
getattr=getattr):

‎Lib/configparser.py‎

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -923,12 +923,20 @@ def popitem(self):
923923
924924
The section DEFAULT is never returned because it cannot be removed.
925925
"""
926-
for key in self.sections():
926+
for key in self._sections:
927927
value = self[key]
928928
del self[key]
929929
return key, value
930930
raise KeyError
931931

932+
def clear(self):
933+
"""Remove all sections from the parser.
934+
935+
The section DEFAULT is left intact because it cannot be removed.
936+
"""
937+
for section in self.sections():
938+
del self[section]
939+
932940
def optionxform(self, optionstr):
933941
return optionstr.lower()
934942

@@ -1341,6 +1349,39 @@ def _options(self):
13411349
else:
13421350
return self._parser.defaults()
13431351

1352+
# MutableMapping.popitem() and MutableMapping.clear() create a new
1353+
# iterator for every removed option, and __iter__() copies the names
1354+
# of all options. Emptying a section that way takes quadratic time.
1355+
1356+
def popitem(self):
1357+
"""Remove an option from the section and return it as
1358+
an (option_name, value) tuple. If no option is present, raise KeyError.
1359+
1360+
Options of the section DEFAULT are only returned for that section
1361+
because they cannot be removed from other sections.
1362+
"""
1363+
if self._name != self._parser.default_section:
1364+
try:
1365+
options = self._parser._sections[self._name]
1366+
except KeyError:
1367+
raise NoSectionError(self._name) from None
1368+
else:
1369+
options = self._parser.defaults()
1370+
for key in options:
1371+
value = self[key]
1372+
del self[key]
1373+
return key, value
1374+
raise KeyError
1375+
1376+
def clear(self):
1377+
"""Remove all options from the section.
1378+
1379+
Options of the section DEFAULT are left intact for other sections
1380+
because they cannot be removed from them.
1381+
"""
1382+
for key in list(self._options()):
1383+
self._parser.remove_option(self._name, key)
1384+
13441385
@property
13451386
def parser(self):
13461387
# The parser object of the proxy is read-only.

‎Lib/encodings/idna.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,9 @@ def _buffer_decode(self, input, errors, final):
348348
result = []
349349
size = 0
350350
for label in labels:
351+
if result:
352+
# Join with U+002E
353+
size += 1
351354
try:
352355
u_label = ToUnicode(label)
353356
except (UnicodeEncodeError, UnicodeDecodeError) as exc:
@@ -360,8 +363,6 @@ def _buffer_decode(self, input, errors, final):
360363
)
361364
else:
362365
result.append(u_label)
363-
if size:
364-
size += 1
365366
size += len(label)
366367

367368
result = ".".join(result) + trailing_dot

‎Lib/gzip.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,7 @@ def main():
762762
compresslevel=compresslevel)
763763
else:
764764
f = builtins.open(arg, "rb")
765-
g = open(arg + ".gz", "wb")
765+
g = open(arg + ".gz", "wb", compresslevel=compresslevel)
766766
while True:
767767
chunk = f.read(READ_BUFFER_SIZE)
768768
if not chunk:

‎Lib/ipaddress.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2272,7 +2272,7 @@ def with_hostmask(self):
22722272

22732273
@property
22742274
def is_unspecified(self):
2275-
return self._ip == 0 and self.network.is_unspecified
2275+
return super().is_unspecified and self.network.is_unspecified
22762276

22772277
@property
22782278
def is_loopback(self):

‎Lib/multiprocessing/shared_memory.py‎

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -305,14 +305,21 @@ def _extract_recreation_code(value):
305305
else:
306306
return 3 # NoneType
307307

308+
@staticmethod
309+
def _encode_value(value):
310+
if not isinstance(value, str):
311+
return value
312+
else:
313+
return value.encode(_encoding)
314+
308315
def __init__(self, sequence=None, *, name=None):
309316
if name is None or sequence is not None:
310317
sequence = sequence or ()
311318
_formats = [
312319
self._types_mapping[type(item)]
313320
if not isinstance(item, (str, bytes))
314321
else self._types_mapping[type(item)] % (
315-
self._alignment * (len(item) // self._alignment + 1),
322+
self._alignment * ((len(self._encode_value(item)) - 1) // self._alignment + 1),
316323
)
317324
for item in sequence
318325
]
@@ -342,31 +349,40 @@ def __init__(self, sequence=None, *, name=None):
342349

343350
if sequence is not None:
344351
_enc = _encoding
345-
struct.pack_into(
346-
"q" + self._format_size_metainfo,
347-
self.shm.buf,
348-
0,
349-
self._list_len,
350-
*(self._allocated_offsets)
351-
)
352-
struct.pack_into(
353-
"".join(_formats),
354-
self.shm.buf,
355-
self._offset_data_start,
356-
*(v.encode(_enc) if isinstance(v, str) else v for v in sequence)
357-
)
358-
struct.pack_into(
359-
self._format_packing_metainfo,
360-
self.shm.buf,
361-
self._offset_packing_formats,
362-
*(v.encode(_enc) for v in _formats)
363-
)
364-
struct.pack_into(
365-
self._format_back_transform_codes,
366-
self.shm.buf,
367-
self._offset_back_transform_codes,
368-
*(_recreation_codes)
369-
)
352+
try:
353+
struct.pack_into(
354+
"q" + self._format_size_metainfo,
355+
self.shm.buf,
356+
0,
357+
self._list_len,
358+
*(self._allocated_offsets)
359+
)
360+
struct.pack_into(
361+
"".join(_formats),
362+
self.shm.buf,
363+
self._offset_data_start,
364+
*(self._encode_value(v) for v in sequence)
365+
)
366+
struct.pack_into(
367+
self._format_packing_metainfo,
368+
self.shm.buf,
369+
self._offset_packing_formats,
370+
*(v.encode(_enc) for v in _formats)
371+
)
372+
struct.pack_into(
373+
self._format_back_transform_codes,
374+
self.shm.buf,
375+
self._offset_back_transform_codes,
376+
*(_recreation_codes)
377+
)
378+
except BaseException:
379+
# A value of a supported type may still be unpackable, such as
380+
# an int outside the signed 64-bit range. The block was created
381+
# above and the caller gets no object to release it with, so
382+
# destroy it here.
383+
self.shm.close()
384+
self.shm.unlink()
385+
raise
370386

371387
else:
372388
self._list_len = len(self) # Obtains size from offset 0 in buffer.
@@ -463,8 +479,7 @@ def __setitem__(self, position, value):
463479
else:
464480
allocated_length = self._allocated_offsets[position + 1] - item_offset
465481

466-
encoded_value = (value.encode(_encoding)
467-
if isinstance(value, str) else value)
482+
encoded_value = self._encode_value(value)
468483
if len(encoded_value) > allocated_length:
469484
raise ValueError("bytes/str item exceeds available storage")
470485
if current_format[-1] == "s":

‎Lib/plistlib.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ def write(self, value):
699699
top_object = self._getrefnum(value)
700700
offset_table_offset = self._fp.tell()
701701
offset_size = _count_to_size(offset_table_offset)
702-
offset_format = '>' + _BINARY_FORMAT[offset_size] * num_objects
702+
offset_format = f'>{num_objects}{_BINARY_FORMAT[offset_size]}'
703703
self._fp.write(struct.pack(offset_format, *self._object_offsets))
704704

705705
# Write trailer
@@ -846,7 +846,7 @@ def _write_object(self, value):
846846
refs = [self._getrefnum(o) for o in value]
847847
s = len(refs)
848848
self._write_size(0xA0, s)
849-
self._fp.write(struct.pack('>' + self._ref_format * s, *refs))
849+
self._fp.write(struct.pack(f'>{s}{self._ref_format}', *refs))
850850

851851
elif isinstance(value, (dict, frozendict)):
852852
keyRefs, valRefs = [], []
@@ -862,8 +862,8 @@ def _write_object(self, value):
862862

863863
s = len(keyRefs)
864864
self._write_size(0xD0, s)
865-
self._fp.write(struct.pack('>' + self._ref_format * s, *keyRefs))
866-
self._fp.write(struct.pack('>' + self._ref_format * s, *valRefs))
865+
self._fp.write(struct.pack(f'>{s}{self._ref_format}', *keyRefs))
866+
self._fp.write(struct.pack(f'>{s}{self._ref_format}', *valRefs))
867867

868868
else:
869869
raise TypeError(value)

‎Lib/shutil.py‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,7 @@ def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0,
11011101
if root_dir is not None:
11021102
base_dir = os.path.join(root_dir, base_dir)
11031103
base_dir = os.path.normpath(base_dir)
1104+
zip_stat = os.stat(zip_filename)
11041105
if arcname != os.curdir:
11051106
zf.write(base_dir, arcname)
11061107
if logger is not None:
@@ -1119,7 +1120,13 @@ def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0,
11191120
for name in filenames:
11201121
path = os.path.join(dirpath, name)
11211122
path = os.path.normpath(path)
1122-
if os.path.isfile(path):
1123+
try:
1124+
st = os.stat(path)
1125+
except OSError:
1126+
continue
1127+
# Do not add the archive to itself.
1128+
if (stat.S_ISREG(st.st_mode) and
1129+
not os.path.samestat(st, zip_stat)):
11231130
arcname = os.path.join(arcdirpath, name)
11241131
zf.write(path, arcname)
11251132
if logger is not None:

‎Lib/tarfile.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2722,6 +2722,13 @@ def _extract_member(self, tarinfo, targetpath, set_attrs=True,
27222722
else:
27232723
self.makefile(tarinfo, targetpath)
27242724

2725+
if tarinfo.islnk() and os.path.islink(targetpath):
2726+
# makelink_with_filter() fell back to extracting a copy of a
2727+
# symlink member, which already has its own attributes set.
2728+
# Setting the hard link's attributes here would follow the
2729+
# symlink, possibly to a file outside the destination directory.
2730+
set_attrs = False
2731+
27252732
if set_attrs:
27262733
self.chown(tarinfo, targetpath, numeric_owner)
27272734
if not tarinfo.issym():

0 commit comments

Comments
 (0)