Skip to content

Commit

Permalink
Nursery docstring
Browse files Browse the repository at this point in the history
  • Loading branch information
bengartner committed Jun 9, 2019
1 parent a588b48 commit 397dbbb
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 93 deletions.
100 changes: 8 additions & 92 deletions docs/source/reference-core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -819,90 +819,12 @@ The nursery API
.. autofunction:: open_nursery
:async-with: nursery

Nursery objects provide the following interface:

.. interface:: The nursery interface

.. method:: start_soon(async_fn, *args, name=None)

Creates a new child task inside this nursery, and sets it up to
run ``await async_fn(*args)``.

This and :meth:`start` are the two fundamental methods for
creating concurrent tasks in Trio.

Note that this is *not* an async function and you don't use await
when calling it. It sets up the new task, but then returns
immediately, *before* it has a chance to run. The new task won’t
actually get a chance to do anything until some later point when
you execute a checkpoint and the scheduler decides to run it.
If you want to run a function and immediately wait for its result,
then you don't need a nursery; just use ``await async_fn(*args)``.
If you want to wait for the task to initialize itself before
continuing, see :meth:`start()`.

It's possible to pass a nursery object into another task, which
allows that task to start new child tasks in the first task's
nursery.

The child task inherits its parent nursery's cancel scopes.

:param async_fn: An async callable.
:param args: Positional arguments for ``async_fn``. If you want
to pass keyword arguments, use
:func:`functools.partial`.
:param name: The name for this task. Only used for
debugging/introspection
(e.g. ``repr(task_obj)``). If this isn't a string,
:meth:`start_soon` will try to make it one. A
common use case is if you're wrapping a function
before spawning a new task, you might pass the
original function as the ``name=`` to make
debugging easier.
:raises RuntimeError: If this nursery is no longer open
(i.e. its ``async with`` block has
exited).

.. method:: start(async_fn, *args, name=None)
:async:

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 make sure that it (or someone) eventually calls
``task_status.started()``.

The conventional way to define ``async_fn`` is like::

async def async_fn(arg1, arg2, *, task_status=trio.TASK_STATUS_IGNORED):
...
task_status.started()
...

:attr:`trio.TASK_STATUS_IGNORED` is a special global object with
a do-nothing ``started`` method. This way your function supports
being called either like ``await nursery.start(async_fn, arg1,
arg2)`` or directly like ``await async_fn(arg1, arg2)``, and
either way it can call ``task_status.started()`` without
worrying about which mode it's in. Defining your function like
this will make it obvious to readers that it supports being used
in both modes.

Before the child calls ``task_status.started()``, it's
effectively run underneath the call to :meth:`start`: if it
raises an exception then that exception is reported by
:meth:`start`, and does *not* propagate out of the nursery. If
:meth:`start` is cancelled, then the child task is also
cancelled.

When the child calls ``task_status.started()``, it's moved from
out from underneath :meth:`start` and into the given nursery.

If the child task passes a value to
``task_status.started(value)``, then :meth:`start` returns this
value. Otherwise it returns ``None``.

.. autoclass:: Nursery

.. automethod:: start_soon

.. automethod:: start

.. attribute:: cancel_scope

Expand All @@ -916,15 +838,9 @@ Nursery objects provide the following interface:
The last two attributes are mainly to enable introspection of the
task tree, for example in debuggers.

.. attribute:: parent_task

The :class:`~trio.hazmat.Task` that opened this nursery.

.. attribute:: child_tasks

A :class:`frozenset` containing all the child
:class:`~trio.hazmat.Task` objects which are still running.
.. autoattribute:: parent_task

.. autoattribute:: child_tasks

.. attribute:: TASK_STATUS_IGNORED

Expand Down
104 changes: 103 additions & 1 deletion trio/_core/_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ def __exit__(self): # pragma: no cover

def open_nursery():
"""Returns an async context manager which must be used to create a
new ``Nursery``.
new `Nursery.
It does not block on entry; on exit it blocks until all child tasks
have exited.
Expand All @@ -762,6 +762,24 @@ def open_nursery():


class Nursery(metaclass=NoPublicConstructor):
"""The summary line for a class docstring should fit on one line.
If the class has public attributes, they may be documented here
in an ``Attributes`` section and follow the same formatting as a
function's ``Args`` section. Alternatively, attributes may be documented
inline with the attribute's declaration (see __init__ method below).
Properties created with the ``@property`` decorator should be documented
in the property's getter method.

This comment has been minimized.

Copy link
@pquentin

pquentin Jun 10, 2019

Member

It looks like those few lines are some sort of Sphinx boilerplate that you forgot to replace?

Attributes:
parent_task (`Task`):
The :class:`~trio.hazmat.Task` that opened this nursery.
child_tasks (`frozenset`):
A :class:`frozenset` containing all the child
:class:`~trio.hazmat.Task` objects which are still running.
"""

def __init__(self, parent_task, cancel_scope):
self._parent_task = parent_task
parent_task._child_nurseries.append(self)
Expand Down Expand Up @@ -841,9 +859,93 @@ def aborted(raise_cancel):
return MultiError(self._pending_excs)

def start_soon(self, async_fn, *args, name=None):
""" Creates a child task, scheduling ``await async_fn(*args)``.
This and :meth:`start` are the two fundamental methods for
creating concurrent tasks in Trio.
Note that this is *not* an async function and you don't use await
when calling it. It sets up the new task, but then returns
immediately, *before* it has a chance to run. The new task won’t
actually get a chance to do anything until some later point when
you execute a checkpoint and the scheduler decides to run it.
If you want to run a function and immediately wait for its result,
then you don't need a nursery; just use ``await async_fn(*args)``.
If you want to wait for the task to initialize itself before
continuing, see :meth:`start()`.
It's possible to pass a nursery object into another task, which
allows that task to start new child tasks in the first task's
nursery.
The child task inherits its parent nursery's cancel scopes.
Args:
async_fn: An async callable.
args: Positional arguments for ``async_fn``. If you want
to pass keyword arguments, use
:func:`functools.partial`.
name: The name for this task. Only used for
debugging/introspection
(e.g. ``repr(task_obj)``). If this isn't a string,
:meth:`start_soon` will try to make it one. A
common use case is if you're wrapping a function
before spawning a new task, you might pass the
original function as the ``name=`` to make
debugging easier.
Returns:
True if successful, False otherwise.
Raises:
RuntimeError: If this nursery is no longer open
(i.e. its ``async with`` block has
exited).
"""
GLOBAL_RUN_CONTEXT.runner.spawn_impl(async_fn, args, self, name)

async def start(self, async_fn, *args, name=None):
""" Creates and initalizes 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 make sure that it (or someone) eventually calls
``task_status.started()``.
The conventional way to define ``async_fn`` is like::
Example:
async def async_fn(arg1, arg2, *, task_status=trio.TASK_STATUS_IGNORED):
...
task_status.started()
...
:attr:`trio.TASK_STATUS_IGNORED` is a special global object with
a do-nothing ``started`` method. This way your function supports
being called either like ``await nursery.start(async_fn, arg1,
arg2)`` or directly like ``await async_fn(arg1, arg2)``, and
either way it can call ``task_status.started()`` without
worrying about which mode it's in. Defining your function like
this will make it obvious to readers that it supports being used
in both modes.
Before the child calls ``task_status.started()``, it's
effectively run underneath the call to :meth:`start`: if it
raises an exception then that exception is reported by
:meth:`start`, and does *not* propagate out of the nursery. If
:meth:`start` is cancelled, then the child task is also
cancelled.
When the child calls ``task_status.started()``, it's moved from
out from underneath :meth:`start` and into the given nursery.
If the child task passes a value to
``task_status.started(value)``, then :meth:`start` returns this
value. Otherwise it returns ``None``.
"""
if self._closed:
raise RuntimeError("Nursery is closed to new arrivals")
try:
Expand Down

0 comments on commit 397dbbb

Please sign in to comment.