From 3acf69be8b162c10b20af779cc44d0fa7fa9daa1 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Wed, 7 May 2025 21:08:05 -0400 Subject: [PATCH] Add a new `.trionics._tn` for "task nursery stuff" Since I'd like to decouple the new "task-manager-nursery" lowlevel primitives/abstractions from the higher-level `TaskManagerNursery`-supporting API(s) and default per-task supervision-strat and because `._mngr` is already purposed for higher-level "on-top-of-nursery" patterns as it is. Deats, - move `maybe_open_nursery()` into the new mod. - adjust the pkg-mod's import to the new sub-mod. - also draft up this idea for an API which stacks `._beg.collapse_eg()` onto a nursery with the WIP name `open_loose_tn()` but more then likely i'll just discard this idea bc i think the explicit `@acm` stacking is more explicit/pythonic/up-front-grokable despite the extra LoC. --- tractor/trionics/__init__.py | 4 +- tractor/trionics/_mngrs.py | 28 ----------- tractor/trionics/_tn.py | 94 ++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 29 deletions(-) create mode 100644 tractor/trionics/_tn.py diff --git a/tractor/trionics/__init__.py b/tractor/trionics/__init__.py index 42f675b2..1678aa1d 100644 --- a/tractor/trionics/__init__.py +++ b/tractor/trionics/__init__.py @@ -21,7 +21,6 @@ Sugary patterns for trio + tractor designs. from ._mngrs import ( gather_contexts as gather_contexts, maybe_open_context as maybe_open_context, - maybe_open_nursery as maybe_open_nursery, ) from ._broadcast import ( AsyncReceiver as AsyncReceiver, @@ -33,3 +32,6 @@ from ._beg import ( collapse_eg as collapse_eg, maybe_collapse_eg as maybe_collapse_eg, ) +from ._tn import ( + maybe_open_nursery as maybe_open_nursery, +) diff --git a/tractor/trionics/_mngrs.py b/tractor/trionics/_mngrs.py index 9a5ed156..12e6ab9c 100644 --- a/tractor/trionics/_mngrs.py +++ b/tractor/trionics/_mngrs.py @@ -23,7 +23,6 @@ from contextlib import ( asynccontextmanager as acm, ) import inspect -from types import ModuleType from typing import ( Any, AsyncContextManager, @@ -34,16 +33,12 @@ from typing import ( Optional, Sequence, TypeVar, - TYPE_CHECKING, ) import trio from tractor._state import current_actor from tractor.log import get_logger -if TYPE_CHECKING: - from tractor import ActorNursery - log = get_logger(__name__) @@ -51,29 +46,6 @@ log = get_logger(__name__) T = TypeVar("T") -@acm -async def maybe_open_nursery( - nursery: trio.Nursery|ActorNursery|None = None, - shield: bool = False, - lib: ModuleType = trio, - - **kwargs, # proxy thru - -) -> AsyncGenerator[trio.Nursery, Any]: - ''' - Create a new nursery if None provided. - - Blocks on exit as expected if no input nursery is provided. - - ''' - if nursery is not None: - yield nursery - else: - async with lib.open_nursery(**kwargs) as nursery: - nursery.cancel_scope.shield = shield - yield nursery - - async def _enter_and_wait( mngr: AsyncContextManager[T], unwrapped: dict[int, T], diff --git a/tractor/trionics/_tn.py b/tractor/trionics/_tn.py new file mode 100644 index 00000000..3ed044dd --- /dev/null +++ b/tractor/trionics/_tn.py @@ -0,0 +1,94 @@ +# tractor: structured concurrent "actors". +# Copyright 2018-eternity Tyler Goodlet. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +''' +`trio.Nursery` wrappers which we short-hand refer to as +`tn`: "task nursery". + +(whereas we refer to `tractor.ActorNursery` as the short-hand `an`) + +''' +from __future__ import annotations +from contextlib import ( + asynccontextmanager as acm, +) +from types import ModuleType +from typing import ( + Any, + AsyncGenerator, + TYPE_CHECKING, +) + +import trio +from tractor.log import get_logger + +# from ._beg import ( +# collapse_eg, +# ) + +if TYPE_CHECKING: + from tractor import ActorNursery + +log = get_logger(__name__) + + +# ??TODO? is this even a good idea?? +# it's an extra LoC to stack `collapse_eg()` vs. +# a new/foreign/bad-std-named very thing wrapper..? +# -[ ] is there a better/simpler name? +# @acm +# async def open_loose_tn() -> trio.Nursery: +# ''' +# Implements the equivalent of the old style loose eg raising +# task-nursery from `trio<=0.25.0` , + +# .. code-block:: python + +# async with trio.open_nursery( +# strict_exception_groups=False, +# ) as tn: +# ... + +# ''' +# async with ( +# collapse_eg(), +# trio.open_nursery() as tn, +# ): +# yield tn + + +@acm +async def maybe_open_nursery( + nursery: trio.Nursery|ActorNursery|None = None, + shield: bool = False, + lib: ModuleType = trio, + loose: bool = False, + + **kwargs, # proxy thru + +) -> AsyncGenerator[trio.Nursery, Any]: + ''' + Create a new nursery if None provided. + + Blocks on exit as expected if no input nursery is provided. + + ''' + if nursery is not None: + yield nursery + else: + async with lib.open_nursery(**kwargs) as tn: + tn.cancel_scope.shield = shield + yield tn