Bump "task-manager(-nursery)" naming, add logging
Namely just renaming any `trio.Nursery` instances to `tn`, the primary `@acm`-API to `.trionics.open_taskman()` and change out all `print()`s for logger instances with 'info' level enabled by the mod-script usage.oco_supervisor_prototype
							parent
							
								
									90db6f2299
								
							
						
					
					
						commit
						653f23a04c
					
				|  | @ -15,13 +15,14 @@ | |||
| # along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| ''' | ||||
| Erlang-style (ish) "one-cancels-one" nursery. | ||||
| Erlang-style (ish) "one-cancels-one" nursery, what we just call | ||||
| a "task manager". | ||||
| 
 | ||||
| ''' | ||||
| from __future__ import annotations | ||||
| from contextlib import ( | ||||
|     asynccontextmanager as acm, | ||||
|     contextmanager as cm, | ||||
|     # contextmanager as cm, | ||||
| ) | ||||
| from functools import partial | ||||
| from typing import ( | ||||
|  | @ -35,11 +36,17 @@ from outcome import ( | |||
| ) | ||||
| from msgspec import Struct | ||||
| import trio | ||||
| from trio._core._run import ( | ||||
|     Task, | ||||
| from trio import ( | ||||
|     TaskStatus, | ||||
|     CancelScope, | ||||
|     Nursery, | ||||
| ) | ||||
| from trio.lowlevel import ( | ||||
|     Task, | ||||
| ) | ||||
| from tractor.log import get_logger | ||||
| 
 | ||||
| log = get_logger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| class TaskOutcome(Struct): | ||||
|  | @ -101,7 +108,7 @@ class TaskOutcome(Struct): | |||
| 
 | ||||
| 
 | ||||
| class TaskManagerNursery(Struct): | ||||
|     _n: Nursery | ||||
|     _tn: Nursery | ||||
|     _scopes: dict[ | ||||
|         Task, | ||||
|         tuple[CancelScope, Outcome] | ||||
|  | @ -127,20 +134,20 @@ class TaskManagerNursery(Struct): | |||
|         # assert len(new_tasks) == 1 | ||||
|         # task = new_tasks.pop() | ||||
| 
 | ||||
|         n: Nursery = self._n | ||||
|         tn: Nursery = self._tn | ||||
| 
 | ||||
|         sm = self.task_manager | ||||
|         # we do default behavior of a scope-per-nursery | ||||
|         # if the user did not provide a task manager. | ||||
|         if sm is None: | ||||
|             return n.start_soon(async_fn, *args, name=None) | ||||
|             return tn.start_soon(async_fn, *args, name=None) | ||||
| 
 | ||||
|         new_task: Task | None = None | ||||
|         # new_task: Task|None = None | ||||
|         to_return: tuple[Any] | None = None | ||||
| 
 | ||||
|         # NOTE: what do we enforce as a signature for the | ||||
|         # `@task_scope_manager` here? | ||||
|         mngr = sm(nursery=n) | ||||
|         mngr = sm(nursery=tn) | ||||
| 
 | ||||
|         async def _start_wrapped_in_scope( | ||||
|             task_status: TaskStatus[ | ||||
|  | @ -190,7 +197,7 @@ class TaskManagerNursery(Struct): | |||
|             else: | ||||
|                 raise RuntimeError(f"{mngr} didn't stop!") | ||||
| 
 | ||||
|         to_return = await n.start(_start_wrapped_in_scope) | ||||
|         to_return = await tn.start(_start_wrapped_in_scope) | ||||
|         assert to_return is not None | ||||
| 
 | ||||
|         # TODO: use the fancy type-check-time type signature stuff from | ||||
|  | @ -222,7 +229,7 @@ def add_task_handle_and_crash_handling( | |||
|     ''' | ||||
|     # if you need it you can ask trio for the task obj | ||||
|     task: Task = trio.lowlevel.current_task() | ||||
|     print(f'Spawning task: {task.name}') | ||||
|     log.info(f'Spawning task: {task.name}') | ||||
| 
 | ||||
|     # User defined "task handle" for more granular supervision | ||||
|     # of each spawned task as needed for their particular usage. | ||||
|  | @ -247,18 +254,27 @@ def add_task_handle_and_crash_handling( | |||
|     # Adds "crash handling" from `pdbp` by entering | ||||
|     # a REPL on std errors. | ||||
|     except Exception as err: | ||||
|         print(f'{task.name} crashed, entering debugger!') | ||||
|         if debug_mode: | ||||
|             log.exception( | ||||
|                 f'{task.name} crashed, entering debugger!' | ||||
|             ) | ||||
|             import pdbp | ||||
|             pdbp.xpm() | ||||
|         raise | ||||
| 
 | ||||
|         raise err | ||||
| 
 | ||||
|     finally: | ||||
|         print(f'{task.name} Exitted') | ||||
|         log.info( | ||||
|             f'Task exitted\n' | ||||
|             f')>\n' | ||||
|             f' |_{task}\n' | ||||
|             # ^^TODO? use sclang formatter? | ||||
|             # -[ ] .devx.pformat.nest_from_op()` yo! | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
| @acm | ||||
| async def open_nursery( | ||||
| async def open_taskman( | ||||
|     task_manager: Generator[Any, Outcome, None] | None = None, | ||||
| 
 | ||||
|     **lowlevel_nursery_kwargs, | ||||
|  | @ -281,7 +297,7 @@ async def ensure_cancelled(): | |||
| 
 | ||||
|     except trio.Cancelled: | ||||
|         task = trio.lowlevel.current_task() | ||||
|         print(f'heyyo ONLY {task.name} was cancelled as expected B)') | ||||
|         log.cancel(f'heyyo ONLY {task.name} was cancelled as expected B)') | ||||
|         assert 0 | ||||
| 
 | ||||
|     except BaseException: | ||||
|  | @ -290,30 +306,33 @@ async def ensure_cancelled(): | |||
| 
 | ||||
| if __name__ == '__main__': | ||||
| 
 | ||||
|     from tractor.log import get_console_log | ||||
|     get_console_log(level='info') | ||||
| 
 | ||||
|     async def main(): | ||||
|         async with open_nursery( | ||||
|         async with open_taskman( | ||||
|             task_manager=partial( | ||||
|                 add_task_handle_and_crash_handling, | ||||
|                 debug_mode=True, | ||||
|             ), | ||||
|         ) as sn: | ||||
|         ) as tm: | ||||
|             for _ in range(3): | ||||
|                 outcome, _ = await sn.start_soon(trio.sleep_forever) | ||||
|                 outcome, _ = await tm.start_soon(trio.sleep_forever) | ||||
| 
 | ||||
|             # extra task we want to engage in debugger post mortem. | ||||
|             err_outcome, cs = await sn.start_soon(ensure_cancelled) | ||||
|             err_outcome, cs = await tm.start_soon(ensure_cancelled) | ||||
| 
 | ||||
|             val: str = 'yoyoyo' | ||||
|             val_outcome, _ = await sn.start_soon( | ||||
|             val_outcome, _ = await tm.start_soon( | ||||
|                 sleep_then_return_val, | ||||
|                 val, | ||||
|             ) | ||||
|             res = await val_outcome.wait_for_result() | ||||
|             assert res == val | ||||
|             print(f'{res} -> GOT EXPECTED TASK VALUE') | ||||
|             log.info(f'{res} -> GOT EXPECTED TASK VALUE') | ||||
| 
 | ||||
|             await trio.sleep(0.6) | ||||
|             print( | ||||
|             log.cancel( | ||||
|                 f'Cancelling and waiting on {err_outcome.lowlevel_task} ' | ||||
|                 'to CRASH..' | ||||
|             ) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue