Skip to content

Commit 0475b56

Browse files
authored
Merge branch 'main' into call-len
2 parents 251007d + 08d7687 commit 0475b56

33 files changed

+258
-126
lines changed

Doc/library/asyncio-eventloop.rst

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ Creating Futures and Tasks
361361

362362
.. versionadded:: 3.5.2
363363

364-
.. method:: loop.create_task(coro, *, name=None, context=None)
364+
.. method:: loop.create_task(coro, *, name=None, context=None, eager_start=None)
365365

366366
Schedule the execution of :ref:`coroutine <coroutine>` *coro*.
367367
Return a :class:`Task` object.
@@ -377,12 +377,20 @@ Creating Futures and Tasks
377377
custom :class:`contextvars.Context` for the *coro* to run in.
378378
The current context copy is created when no *context* is provided.
379379

380+
An optional keyword-only *eager_start* argument allows specifying
381+
if the task should execute eagerly during the call to create_task,
382+
or be scheduled later. If *eager_start* is not passed the mode set
383+
by :meth:`loop.set_task_factory` will be used.
384+
380385
.. versionchanged:: 3.8
381386
Added the *name* parameter.
382387

383388
.. versionchanged:: 3.11
384389
Added the *context* parameter.
385390

391+
.. versionchanged:: next
392+
Added the *eager_start* parameter.
393+
386394
.. method:: loop.set_task_factory(factory)
387395

388396
Set a task factory that will be used by

Doc/library/subprocess.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1525,6 +1525,24 @@ handling consistency are valid for these functions.
15251525
Notes
15261526
-----
15271527

1528+
.. _subprocess-timeout-behavior:
1529+
1530+
Timeout Behavior
1531+
^^^^^^^^^^^^^^^^
1532+
1533+
When using the ``timeout`` parameter in functions like :func:`run`,
1534+
:meth:`Popen.wait`, or :meth:`Popen.communicate`,
1535+
users should be aware of the following behaviors:
1536+
1537+
1. **Process Creation Delay**: The initial process creation itself cannot be interrupted
1538+
on many platform APIs. This means that even when specifying a timeout, you are not
1539+
guaranteed to see a timeout exception until at least after however long process
1540+
creation takes.
1541+
1542+
2. **Extremely Small Timeout Values**: Setting very small timeout values (such as a few
1543+
milliseconds) may result in almost immediate :exc:`TimeoutExpired` exceptions because
1544+
process creation and system scheduling inherently require time.
1545+
15281546
.. _converting-argument-sequence:
15291547

15301548
Converting an argument sequence to a string on Windows

Include/internal/pycore_magic_number.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ Known values:
276276
Python 3.14a7 3621 (Optimize LOAD_FAST opcodes into LOAD_FAST_BORROW)
277277
Python 3.14a7 3622 (Store annotations in different class dict keys)
278278
Python 3.14a7 3623 (Add BUILD_INTERPOLATION & BUILD_TEMPLATE opcodes)
279+
Python 3.14b1 3624 (Don't optimize LOAD_FAST when local is killed by DELETE_FAST)
279280
280281
Python 3.15 will start with 3650
281282
@@ -288,7 +289,7 @@ PC/launcher.c must also be updated.
288289
289290
*/
290291

291-
#define PYC_MAGIC_NUMBER 3623
292+
#define PYC_MAGIC_NUMBER 3624
292293
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
293294
(little-endian) and then appending b'\r\n'. */
294295
#define PYC_MAGIC_NUMBER_TOKEN \

Lib/asyncio/base_events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ def create_future(self):
459459
return futures.Future(loop=self)
460460

461461
def create_task(self, coro, **kwargs):
462-
"""Schedule a coroutine object.
462+
"""Schedule or begin executing a coroutine object.
463463
464464
Return a task object.
465465
"""

Lib/asyncio/taskgroups.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ async def _aexit(self, et, exc):
179179
exc = None
180180

181181

182-
def create_task(self, coro, *, name=None, context=None):
182+
def create_task(self, coro, **kwargs):
183183
"""Create a new task in this group and return it.
184184
185185
Similar to `asyncio.create_task`.
@@ -193,10 +193,7 @@ def create_task(self, coro, *, name=None, context=None):
193193
if self._aborting:
194194
coro.close()
195195
raise RuntimeError(f"TaskGroup {self!r} is shutting down")
196-
if context is None:
197-
task = self._loop.create_task(coro, name=name)
198-
else:
199-
task = self._loop.create_task(coro, name=name, context=context)
196+
task = self._loop.create_task(coro, **kwargs)
200197

201198
futures.future_add_to_awaited_by(task, self._parent_task)
202199

Lib/asyncio/tasks.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -386,19 +386,13 @@ def __wakeup(self, future):
386386
Task = _CTask = _asyncio.Task
387387

388388

389-
def create_task(coro, *, name=None, context=None):
389+
def create_task(coro, **kwargs):
390390
"""Schedule the execution of a coroutine object in a spawn task.
391391
392392
Return a Task object.
393393
"""
394394
loop = events.get_running_loop()
395-
if context is None:
396-
# Use legacy API if context is not needed
397-
task = loop.create_task(coro, name=name)
398-
else:
399-
task = loop.create_task(coro, name=name, context=context)
400-
401-
return task
395+
return loop.create_task(coro, **kwargs)
402396

403397

404398
# wait() and as_completed() similar to those in PEP 3148.
@@ -1030,9 +1024,9 @@ def create_eager_task_factory(custom_task_constructor):
10301024
used. E.g. `loop.set_task_factory(asyncio.eager_task_factory)`.
10311025
"""
10321026

1033-
def factory(loop, coro, *, name=None, context=None):
1027+
def factory(loop, coro, *, eager_start=True, **kwargs):
10341028
return custom_task_constructor(
1035-
coro, loop=loop, name=name, context=context, eager_start=True)
1029+
coro, loop=loop, eager_start=eager_start, **kwargs)
10361030

10371031
return factory
10381032

Lib/asyncio/tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from itertools import count
66
from enum import Enum
77
import sys
8-
from _remotedebugging import get_all_awaited_by
8+
from _remote_debugging import get_all_awaited_by
99

1010

1111
class NodeType(Enum):

Lib/subprocess.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,8 +1235,11 @@ def communicate(self, input=None, timeout=None):
12351235

12361236
finally:
12371237
self._communication_started = True
1238-
1239-
sts = self.wait(timeout=self._remaining_time(endtime))
1238+
try:
1239+
sts = self.wait(timeout=self._remaining_time(endtime))
1240+
except TimeoutExpired as exc:
1241+
exc.timeout = timeout
1242+
raise
12401243

12411244
return (stdout, stderr)
12421245

@@ -2145,8 +2148,11 @@ def _communicate(self, input, endtime, orig_timeout):
21452148
selector.unregister(key.fileobj)
21462149
key.fileobj.close()
21472150
self._fileobj2output[key.fileobj].append(data)
2148-
2149-
self.wait(timeout=self._remaining_time(endtime))
2151+
try:
2152+
self.wait(timeout=self._remaining_time(endtime))
2153+
except TimeoutExpired as exc:
2154+
exc.timeout = orig_timeout
2155+
raise
21502156

21512157
# All data exchanged. Translate lists into strings.
21522158
if stdout is not None:

Lib/test/test_asdl_parser.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,17 @@ def test_product(self):
6262
alias = self.types['alias']
6363
self.assertEqual(
6464
str(alias),
65-
'Product([Field(identifier, name), Field(identifier, asname, opt=True)], '
65+
'Product([Field(identifier, name), Field(identifier, asname, quantifiers=[OPTIONAL])], '
6666
'[Field(int, lineno), Field(int, col_offset), '
67-
'Field(int, end_lineno, opt=True), Field(int, end_col_offset, opt=True)])')
67+
'Field(int, end_lineno, quantifiers=[OPTIONAL]), Field(int, end_col_offset, quantifiers=[OPTIONAL])])')
6868

6969
def test_attributes(self):
7070
stmt = self.types['stmt']
7171
self.assertEqual(len(stmt.attributes), 4)
7272
self.assertEqual(repr(stmt.attributes[0]), 'Field(int, lineno)')
7373
self.assertEqual(repr(stmt.attributes[1]), 'Field(int, col_offset)')
74-
self.assertEqual(repr(stmt.attributes[2]), 'Field(int, end_lineno, opt=True)')
75-
self.assertEqual(repr(stmt.attributes[3]), 'Field(int, end_col_offset, opt=True)')
74+
self.assertEqual(repr(stmt.attributes[2]), 'Field(int, end_lineno, quantifiers=[OPTIONAL])')
75+
self.assertEqual(repr(stmt.attributes[3]), 'Field(int, end_col_offset, quantifiers=[OPTIONAL])')
7676

7777
def test_constructor_fields(self):
7878
ehandler = self.types['excepthandler']

Lib/test/test_asyncio/test_eager_task_factory.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,24 @@ async def run():
263263

264264
self.run_coro(run())
265265

266+
def test_eager_start_false(self):
267+
name = None
268+
269+
async def asyncfn():
270+
nonlocal name
271+
name = asyncio.current_task().get_name()
272+
273+
async def main():
274+
t = asyncio.get_running_loop().create_task(
275+
asyncfn(), eager_start=False, name="example"
276+
)
277+
self.assertFalse(t.done())
278+
self.assertIsNone(name)
279+
await t
280+
self.assertEqual(name, "example")
281+
282+
self.run_coro(main())
283+
266284

267285
class PyEagerTaskFactoryLoopTests(EagerTaskFactoryLoopTests, test_utils.TestCase):
268286
Task = tasks._PyTask
@@ -505,5 +523,24 @@ def tearDown(self):
505523
asyncio.current_task = asyncio.tasks.current_task = self._current_task
506524
return super().tearDown()
507525

526+
527+
class DefaultTaskFactoryEagerStart(test_utils.TestCase):
528+
def test_eager_start_true_with_default_factory(self):
529+
name = None
530+
531+
async def asyncfn():
532+
nonlocal name
533+
name = asyncio.current_task().get_name()
534+
535+
async def main():
536+
t = asyncio.get_running_loop().create_task(
537+
asyncfn(), eager_start=True, name="example"
538+
)
539+
self.assertTrue(t.done())
540+
self.assertEqual(name, "example")
541+
await t
542+
543+
asyncio.run(main(), loop_factory=asyncio.EventLoop)
544+
508545
if __name__ == '__main__':
509546
unittest.main()

0 commit comments

Comments
 (0)