Skip to content

Commit 7112293

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix-pulldom-resource-leak-148428
2 parents 9984c8f + 3daa7f8 commit 7112293

15 files changed

Lines changed: 374 additions & 21 deletions

Doc/library/concurrent.interpreters.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ objects are either directly shared or copied efficiently. For example:
191191
* :class:`float`
192192
* :class:`tuple` (of similarly supported objects)
193193

194-
There is a small number of Python types that actually share mutable
194+
There are a small number of Python types that actually share mutable
195195
data between interpreters:
196196

197197
* :class:`memoryview`
@@ -274,7 +274,7 @@ Interpreter objects
274274

275275
.. method:: call(callable, /, *args, **kwargs)
276276

277-
Return the result of calling running the given function in the
277+
Return the result of running the given function in the
278278
interpreter (in the current thread).
279279

280280
.. _interp-call-in-thread:

Doc/library/xml.etree.elementtree.rst

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -711,16 +711,16 @@ Functions
711711

712712
.. function:: tostring(element, encoding="us-ascii", method="xml", *, \
713713
xml_declaration=None, default_namespace=None, \
714-
short_empty_elements=True)
714+
short_empty_elements=True, standalone=None)
715715

716716
Generates a string representation of an XML element, including all
717717
subelements. *element* is an :class:`Element` instance. *encoding* [1]_ is
718718
the output encoding (default is US-ASCII). Use ``encoding="unicode"`` to
719719
generate a Unicode string (otherwise, a bytestring is generated). *method*
720720
is either ``"xml"``, ``"html"`` or ``"text"`` (default is ``"xml"``).
721-
*xml_declaration*, *default_namespace* and *short_empty_elements* has the same
722-
meaning as in :meth:`ElementTree.write`. Returns an (optionally) encoded string
723-
containing the XML data.
721+
*xml_declaration*, *default_namespace*, *short_empty_elements* and
722+
*standalone* has the same meaning as in :meth:`ElementTree.write`.
723+
Returns an (optionally) encoded string containing the XML data.
724724

725725
.. versionchanged:: 3.4
726726
Added the *short_empty_elements* parameter.
@@ -732,19 +732,23 @@ Functions
732732
The :func:`tostring` function now preserves the attribute order
733733
specified by the user.
734734

735+
.. versionchanged:: next
736+
Added the *standalone* parameter.
737+
735738

736739
.. function:: tostringlist(element, encoding="us-ascii", method="xml", *, \
737740
xml_declaration=None, default_namespace=None, \
738-
short_empty_elements=True)
741+
short_empty_elements=True, standalone=None)
739742

740743
Generates a string representation of an XML element, including all
741744
subelements. *element* is an :class:`Element` instance. *encoding* [1]_ is
742745
the output encoding (default is US-ASCII). Use ``encoding="unicode"`` to
743746
generate a Unicode string (otherwise, a bytestring is generated). *method*
744747
is either ``"xml"``, ``"html"`` or ``"text"`` (default is ``"xml"``).
745-
*xml_declaration*, *default_namespace* and *short_empty_elements* has the same
746-
meaning as in :meth:`ElementTree.write`. Returns a list of (optionally) encoded
747-
strings containing the XML data. It does not guarantee any specific sequence,
748+
*xml_declaration*, *default_namespace*, *short_empty_elements* and
749+
*standalone* has the same meaning as in :meth:`ElementTree.write`.
750+
Returns a list of (optionally) encoded strings containing the XML data.
751+
It does not guarantee any specific sequence,
748752
except that ``b"".join(tostringlist(element)) == tostring(element)``.
749753

750754
.. versionadded:: 3.2
@@ -759,6 +763,9 @@ Functions
759763
The :func:`tostringlist` function now preserves the attribute order
760764
specified by the user.
761765

766+
.. versionchanged:: next
767+
Added the *standalone* parameter.
768+
762769

763770
.. function:: XML(text, parser=None)
764771

@@ -1186,7 +1193,7 @@ ElementTree Objects
11861193

11871194
.. method:: write(file, encoding="us-ascii", xml_declaration=None, \
11881195
default_namespace=None, method="xml", *, \
1189-
short_empty_elements=True)
1196+
short_empty_elements=True, standalone=None)
11901197
11911198
Writes the element tree to a file, as XML. *file* is a file name, or a
11921199
:term:`file object` opened for writing. *encoding* [1]_ is the output
@@ -1202,6 +1209,13 @@ ElementTree Objects
12021209
emitted as a single self-closed tag, otherwise they are emitted as a pair
12031210
of start/end tags.
12041211

1212+
The keyword-only *standalone* parameter is the value of the standalone
1213+
document declaration in the XML declaration.
1214+
Use ``True`` for ``standalone="yes"``, ``False`` for ``standalone="no"``,
1215+
and ``None`` (the default) to omit it.
1216+
An XML declaration is written if *standalone* is not ``None``;
1217+
combining it with ``xml_declaration=False`` raises a :exc:`ValueError`.
1218+
12051219
The output is either a string (:class:`str`) or binary (:class:`bytes`).
12061220
This is controlled by the *encoding* argument. If *encoding* is
12071221
``"unicode"``, the output is a string; otherwise, it's binary. Note that
@@ -1216,6 +1230,9 @@ ElementTree Objects
12161230
The :meth:`write` method now preserves the attribute order specified
12171231
by the user.
12181232

1233+
.. versionchanged:: next
1234+
Added the *standalone* parameter.
1235+
12191236

12201237
This is the XML file that is going to be manipulated::
12211238

Lib/tarfile.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1601,6 +1601,14 @@ def _proc_pax(self, tarfile):
16011601
# Fetch the next header.
16021602
try:
16031603
next = self._fromtarfile(tarfile, dircheck=False)
1604+
except EOFHeaderError:
1605+
if self.type == XGLTYPE:
1606+
# If this is a global header at the end of the archive
1607+
# (no regular members follow), let the EOFHeaderError
1608+
# propagate so the caller handles end-of-archive normally.
1609+
tarfile.offset = tarfile.fileobj.tell() - BLOCKSIZE
1610+
raise
1611+
raise SubsequentHeaderError("end of file header") from None
16041612
except HeaderError as e:
16051613
raise SubsequentHeaderError(str(e)) from None
16061614

Lib/test/test_builtin.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,10 @@ async def sleep(delay, result=None):
542542
'''a = [x async for x in (x async for x in arange(5))][1]''',
543543
'''a, = [1 for x in {x async for x in arange(1)}]''',
544544
'''a = [await sleep(0, x) async for x in arange(2)][1]''',
545+
'''a = [await sleep(0, 1) for _ in [0]][0]''',
546+
'''a = {await sleep(0, 1) for _ in [0]}.pop()''',
547+
'''a = {0: await sleep(0, 1) for _ in [0]}[0]''',
548+
'''a = (lambda x=[await sleep(0, 1) for _ in [0]]: x)()[0]''',
545549
# gh-121637: Make sure we correctly handle the case where the
546550
# async code is optimized away
547551
'''assert not await sleep(0); a = 1''',
@@ -610,7 +614,26 @@ async def __aexit__(self, *exc_info):
610614
'''def f():
611615
async with Lock() as l:
612616
a = 1
613-
'''
617+
''',
618+
'''class C:
619+
[await x for x in y]
620+
''',
621+
'''class C:
622+
[x async for x in arange(10)]
623+
''',
624+
'''async def f():
625+
class C:
626+
[await x for x in y]
627+
''',
628+
'''lambda: [await x for x in y]''',
629+
'''class C:
630+
def f(self, x=[await y for y in z]):
631+
pass
632+
''',
633+
'''type T = [await x for x in y]''',
634+
'''async def f[T=[await x for x in y]]():
635+
pass
636+
''',
614637
]
615638
for mode, code_sample in product(modes, code_samples):
616639
source = dedent(code_sample)

Lib/test/test_capi/test_object.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,5 +342,20 @@ def test_pyobject_dump(self):
342342
self.assertRegex(output, r'<object at .* is freed>')
343343

344344

345+
class RefTracerTest(unittest.TestCase):
346+
@support.requires_resource('cpu')
347+
@support.skip_emscripten_stack_overflow()
348+
@support.skip_wasi_stack_overflow()
349+
def test_destroy_traced_for_trashcan_deferred_objects(self):
350+
depth = 200_000
351+
chain = None
352+
for _ in range(depth):
353+
chain = [chain]
354+
_testcapi.start_counting_list_destroys()
355+
del chain
356+
destroys = _testcapi.stop_counting_list_destroys()
357+
self.assertEqual(destroys, depth)
358+
359+
345360
if __name__ == "__main__":
346361
unittest.main()

Lib/test/test_coroutines.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import ast
12
import contextlib
23
import copy
34
import inspect
@@ -407,6 +408,118 @@ async def bar():
407408
with self.subTest(code=code), self.assertRaises(SyntaxError):
408409
compile(code, "<test>", "exec")
409410

411+
def test_async_comprehension_scope(self):
412+
# List/set/dict comprehensions with await or async for are allowed
413+
# only in async functions, or at module level with top-level await.
414+
allowed = [
415+
"async def f():\n [await x for x in y]",
416+
"async def f():\n {await x for x in y}",
417+
"async def f():\n {k: await x for k, x in y}",
418+
"async def f():\n [x async for x in y]",
419+
"async def f():\n {x async for x in y}",
420+
"async def f():\n {k: x async for k, x in y}",
421+
"async def f():\n [[await x for x in y] for y in z]",
422+
# Defaults, bases, and genexp iterables are evaluated in the
423+
# enclosing scope.
424+
"async def outer():\n async def f(x=[await y for y in z]): pass",
425+
"async def f():\n class C([await x for x in y]): pass",
426+
"async def f():\n (x for x in [await y for y in z])",
427+
"async def f():\n (x for x in [y async for y in z])",
428+
]
429+
for code in allowed:
430+
with self.subTest(code=code):
431+
compile(code, "<test>", "exec")
432+
433+
# Generator expressions with await are async genexps and may appear
434+
# outside async functions. A listcomp nested in a genexp body is also
435+
# allowed (the genexp becomes an async generator).
436+
for code in [
437+
"(await x for x in y)",
438+
"def f():\n (await x for x in y)",
439+
"class C:\n (await x for x in y)",
440+
"lambda: (await x for x in y)",
441+
"([await x for x in y] for y in z)",
442+
"def f():\n ([await x for x in y] for y in z)",
443+
"class C:\n ([await x for x in y] for y in z)",
444+
"async def f():\n ([await x for x in y] for y in z)",
445+
]:
446+
with self.subTest(code=code):
447+
compile(code, "<test>", "exec")
448+
449+
err = "asynchronous comprehension outside of an asynchronous function"
450+
invalid = [
451+
"[await x for x in y]",
452+
"{await x for x in y}",
453+
"{k: await x for k, x in y}",
454+
"[x async for x in y]",
455+
"{x async for x in y}",
456+
"{k: x async for k, x in y}",
457+
"[[await x for x in y] for y in z]",
458+
"[[x async for x in y] for y in z]",
459+
"def f():\n [await x for x in y]",
460+
"def f():\n [x async for x in y]",
461+
"async def f():\n def g():\n [await x for x in y]",
462+
"class C:\n [await x for x in y]",
463+
"class C:\n {await x for x in y}",
464+
"class C:\n {k: await x for k, x in y}",
465+
"class C:\n [x async for x in y]",
466+
"class C:\n [[await x for x in y] for y in z]",
467+
"async def f():\n class C:\n x = [await y for y in z]",
468+
"async def f():\n class C:\n x = [y async for y in z]",
469+
# Lambdas are never async, even inside async def.
470+
"lambda: [await x for x in y]",
471+
"async def f():\n lambda: [await x for x in y]",
472+
"class C:\n f = lambda: [await x for x in y]",
473+
# Defaults, bases, and genexp iterables run in the enclosing scope.
474+
"(x for x in [await y for y in z])",
475+
"(x for x in [y async for y in z])",
476+
"def f():\n (x for x in [await y for y in z])",
477+
"async def f(x=[await y for y in z]): pass",
478+
"def f(x=[await y for y in z]): pass",
479+
"class C:\n def f(self, x=[await y for y in z]): pass",
480+
"class C([await x for x in y]): pass",
481+
# Type aliases and type-parameter scopes.
482+
"type T = [await x for x in y]",
483+
"type T = [x async for x in y]",
484+
"def f[T=[await x for x in y]](): pass",
485+
"def f[T: [await x for x in y]](): pass",
486+
"async def f[T=[await x for x in y]](): pass",
487+
"async def f(x: [await y for y in z]): pass",
488+
]
489+
for code in invalid:
490+
with self.subTest(code=code):
491+
support.check_syntax_error(self, code, err)
492+
493+
support.check_syntax_error(
494+
self, "await x", "'await' outside function")
495+
support.check_syntax_error(
496+
self, "class C:\n await x", "'await' outside function")
497+
support.check_syntax_error(
498+
self, "def f():\n await x", "'await' outside async function")
499+
500+
flags = ast.PyCF_ALLOW_TOP_LEVEL_AWAIT
501+
for code in [
502+
"[await x for x in y]",
503+
"async def f(x=[await y for y in z]): pass",
504+
"class C([await x for x in y]): pass",
505+
"f'{[await x for x in y]}'",
506+
"(x for x in [await y for y in z])",
507+
"(x for x in [y async for y in z])",
508+
]:
509+
with self.subTest(code=code, tla=True):
510+
compile(code, "<test>", "exec", flags=flags)
511+
still_invalid = [
512+
"lambda: [await x for x in y]",
513+
"def f():\n (x for x in [await y for y in z])",
514+
"class C:\n def f(self, x=[await y for y in z]): pass",
515+
"type T = [await x for x in y]",
516+
"async def f[T=[await x for x in y]](): pass",
517+
]
518+
for code in still_invalid:
519+
with self.subTest(code=code, tla=True):
520+
with self.assertRaisesRegex(SyntaxError, err):
521+
compile(code, "<test>", "exec", flags=flags)
522+
410523
def test_badsyntax_2(self):
411524
samples = [
412525
"""def foo():

Lib/test/test_tarfile.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2499,6 +2499,35 @@ def test_pax_global_header(self):
24992499
finally:
25002500
tar.close()
25012501

2502+
def test_pax_global_header_empty_archive(self):
2503+
# An archive that contains only a global header and no regular
2504+
# members should be opened successfully (gh-149578).
2505+
pax_headers = {"foo": "bar"}
2506+
2507+
# Create a PAX archive with global headers but no file entries.
2508+
with tarfile.open(tmpname, "w", format=tarfile.PAX_FORMAT,
2509+
pax_headers=pax_headers):
2510+
pass
2511+
2512+
# Reading the archive should work and preserve global headers.
2513+
with tarfile.open(tmpname) as tar:
2514+
self.assertEqual(tar.pax_headers, pax_headers)
2515+
self.assertEqual(tar.getmembers(), [])
2516+
2517+
# Appending to the archive should work.
2518+
with tarfile.open(tmpname, "a") as tar:
2519+
self.assertEqual(tar.pax_headers, pax_headers)
2520+
self.assertEqual(tar.getmembers(), [])
2521+
tar.addfile(tarfile.TarInfo("test"))
2522+
2523+
# Verify the appended member is present and global headers
2524+
# are preserved.
2525+
with tarfile.open(tmpname) as tar:
2526+
self.assertEqual(tar.pax_headers, pax_headers)
2527+
members = tar.getmembers()
2528+
self.assertEqual(len(members), 1)
2529+
self.assertEqual(members[0].name, "test")
2530+
25022531
def test_pax_extended_header(self):
25032532
# The fields from the pax header have priority over the
25042533
# TarInfo.

0 commit comments

Comments
 (0)