r"""Creates and initializes a child task. Like :meth:`start_soon`, but blocks until the new task has finished initializing itself, and optionally returns some information from it. The ``async_fn`` must accept a ``task_status`` keyword argument, and it must m
( # type: ignore[explicit-any]
self,
async_fn: Callable[..., Awaitable[object]],
*args: object,
name: object = None,
)
| 1395 | |
| 1396 | # Typing changes blocked by https://github.com/python/mypy/pull/17512 |
| 1397 | async def start( # type: ignore[explicit-any] |
| 1398 | self, |
| 1399 | async_fn: Callable[..., Awaitable[object]], |
| 1400 | *args: object, |
| 1401 | name: object = None, |
| 1402 | ) -> Any: |
| 1403 | r"""Creates and initializes a child task. |
| 1404 | |
| 1405 | Like :meth:`start_soon`, but blocks until the new task has |
| 1406 | finished initializing itself, and optionally returns some |
| 1407 | information from it. |
| 1408 | |
| 1409 | The ``async_fn`` must accept a ``task_status`` keyword argument, |
| 1410 | and it must make sure that it (or someone) eventually calls |
| 1411 | :meth:`task_status.started() <TaskStatus.started>`. |
| 1412 | |
| 1413 | The conventional way to define ``async_fn`` is like:: |
| 1414 | |
| 1415 | async def async_fn(arg1, arg2, *, task_status=trio.TASK_STATUS_IGNORED): |
| 1416 | ... # Caller is blocked waiting for this code to run |
| 1417 | task_status.started() |
| 1418 | ... # This async code can be interleaved with the caller |
| 1419 | |
| 1420 | :attr:`trio.TASK_STATUS_IGNORED` is a special global object with |
| 1421 | a do-nothing ``started`` method. This way your function supports |
| 1422 | being called either like ``await nursery.start(async_fn, arg1, |
| 1423 | arg2)`` or directly like ``await async_fn(arg1, arg2)``, and |
| 1424 | either way it can call :meth:`task_status.started() <TaskStatus.started>` |
| 1425 | without worrying about which mode it's in. Defining your function like |
| 1426 | this will make it obvious to readers that it supports being used |
| 1427 | in both modes. |
| 1428 | |
| 1429 | Before the child calls :meth:`task_status.started() <TaskStatus.started>`, |
| 1430 | it's effectively run underneath the call to :meth:`start`: if it |
| 1431 | raises an exception then that exception is reported by |
| 1432 | :meth:`start`, and does *not* propagate out of the nursery. If |
| 1433 | :meth:`start` is cancelled, then the child task is also |
| 1434 | cancelled. |
| 1435 | |
| 1436 | When the child calls :meth:`task_status.started() <TaskStatus.started>`, |
| 1437 | it's moved out from underneath :meth:`start` and into the given nursery. |
| 1438 | |
| 1439 | If the child task passes a value to :meth:`task_status.started(value) <TaskStatus.started>`, |
| 1440 | then :meth:`start` returns this value. Otherwise, it returns ``None``. |
| 1441 | """ |
| 1442 | if self._closed: |
| 1443 | raise RuntimeError("Nursery is closed to new arrivals") |
| 1444 | try: |
| 1445 | self._pending_starts += 1 |
| 1446 | # wrap internal nursery in try-except to unroll any exceptiongroups |
| 1447 | # to avoid wrapping pre-started() exceptions in an extra ExceptionGroup. |
| 1448 | # See #2611. |
| 1449 | try: |
| 1450 | # set strict_exception_groups = True to make sure we always unwrap |
| 1451 | # *this* nursery's exceptiongroup |
| 1452 | async with open_nursery(strict_exception_groups=True) as old_nursery: |
| 1453 | task_status: _TaskStatus[object | None] = _TaskStatus( |
| 1454 | old_nursery, |