Source code for doot.control.runner.step_runner

  1#!/usr/bin/env python3
  2"""
  3
  4"""
  5# ruff: noqa: PLR1711
  6# Imports:
  7from __future__ import annotations
  8
  9# ##-- stdlib imports
 10import datetime
 11import enum
 12import functools as ftz
 13import itertools as itz
 14import logging as logmod
 15import pathlib as pl
 16import re
 17import time
 18import types
 19import weakref
 20from uuid import UUID, uuid1
 21
 22# ##-- end stdlib imports
 23
 24# ##-- 3rd party imports
 25from jgdv import Proto, Mixin
 26from jgdv.debugging import SignalHandler
 27
 28# ##-- end 3rd party imports
 29
 30# ##-- 1st party imports
 31import doot
 32import doot.errors
 33from .runner import DootRunner
 34from doot.workflow._interface import TaskMeta_e, TaskStatus_e
 35from doot.workflow import _interface as TaskAPI
 36from doot.workflow.structs import action_spec as actspec
 37# ##-- end 1st party imports
 38
 39# ##-- types
 40# isort: off
 41import abc
 42import collections.abc
 43from typing import TYPE_CHECKING, cast, assert_type, assert_never
 44from typing import Generic, NewType
 45# Protocols:
 46from typing import Protocol, runtime_checkable
 47from doot.control.runner._interface import WorkflowRunner_p
 48# Typing Decorators:
 49from typing import no_type_check, final, override, overload
 50
 51if TYPE_CHECKING:
 52    from jgdv import Maybe
 53    from typing import Final
 54    from typing import ClassVar, Any, LiteralString
 55    from typing import Never, Self, Literal
 56    from typing import TypeGuard
 57    from collections.abc import Iterable, Iterator, Callable, Generator
 58    from collections.abc import Sequence, Mapping, MutableMapping, Hashable
 59
 60# isort: on
 61# ##-- end types
 62
 63##-- logging
 64logging = logmod.getLogger(__name__)
 65
 66##-- end logging
 67
 68dry_run        : final[bool] = doot.args.on_fail(False).cmd.args.dry_run()
 69SLEEP_LENGTH   : Final[int]  = doot.config.on_fail(0.2, int|float).commands.run.sleep.task()
 70MAX_LOG_ACTIVE : Final[int]  = 100
 71CMDS           : Final[dict] = {
 72  ""           : "continue",
 73  "c"          : "continue",
 74  "n"          : "skip",
 75  "b"          : "break",
 76  "l"          : "list",
 77  "d"          : "down",
 78  "u"          : "up",
 79  "q"          : "quit",
 80  "?"          : "help",
 81  "I"          : "print_info",
 82  "W"          : "print_warn",
 83  "D"          : "print_debug",
 84  "s"          : "print_state",
 85 }
 86
 87break_on : str = doot.config.on_fail("job").settings.commands.run.stepper.break_on()
 88
 89##--|
 90
[docs] 91class _Instructions_m: 92
[docs] 93 def _do_default(self, *args): 94 doot.report.gen.trace("::- Default") 95 return None
96
[docs] 97 def _do_continue(self, *args): 98 doot.report.gen.trace("::- Continue") 99 return True
100
[docs] 101 def _do_skip(self, *args): 102 doot.report.gen.trace("::- Skipping") 103 return False
104
[docs] 105 def _do_help(self, *args): 106 doot.report.gen.trace("::- Help") 107 doot.report.gen.trace("::-- Available Commands: %s", " ".join([x.replace(self._cmd_prefix, "") for x in dir(self) if self._cmd_prefix in x])) 108 109 doot.report.gen.trace("") 110 doot.report.gen.trace("::-- Aliases:") 111 for x,y in self._aliases.items(): 112 if x == "": 113 continue 114 doot.report.gen.trace("::-- %s : %s", x, y) 115 116 doot.report.gen.gap() 117 doot.report.gen.trace("::-- Pausing on: %s", self._conf_types) 118 119 return None
120
[docs] 121 def _do_quit(self, *args): 122 doot.report.gen.trace("::- Quitting Doot") 123 self.tracker.clear_queue() 124 if len(self.tracker.active_set) < MAX_LOG_ACTIVE: 125 doot.report.gen.trace("Tracker Queue: %s", self.tracker.active_set) 126 else: 127 doot.report.gen.trace("Tracker Queue: %s", len(self.tracker_set)) 128 self._has_quit = True 129 return False
130
[docs] 131 def _do_list(self, *args): 132 doot.report.gen.trace("::- Listing Trace:") 133 for x in self.tracker.execution_path[:-1]: 134 doot.report.gen.trace("::-- %s", x) 135 136 doot.report.gen.trace("::-- Current: %s", self.tracker.execution_path[-1]) 137 138 return None
139
[docs] 140 def _do_break(self, *args): 141 doot.report.gen.trace("::- Break") 142 breakpoint() 143 pass
144
[docs] 145 def _do_down(self, *args): 146 doot.report.gen.trace("::- Down") 147 match self._conf_types: 148 case [True]: 149 self.set_confirm_type("job") 150 case [TaskAPI.Job_i]: 151 self.set_confirm_type("task") 152 case [TaskAPI.Task_i]: 153 self.set_confirm_type("action") 154 case [actspec.ActionSpec]: 155 self.set_confirm_type("all") 156 pass 157 158 doot.report.gen.trace("::- Stepping at: %s", self._conf_types)
159
[docs] 160 def _do_up(self, *args): 161 doot.report.gen.trace("Up") 162 match self._conf_types: 163 case [actspec.ActionSpec]: 164 self.set_confirm_type("task") 165 case [TaskAPI.Task_i]: 166 self.set_confirm_type("job") 167 case [TaskAPI.Job_i]: 168 self.set_confirm_type("all") 169 case [True]: 170 pass 171 172 doot.report.gen.trace("Stepping at: %s", self._conf_types)
173
[docs] 174 def _do_print_info(self, *args): 175 self._override_level = "INFO" 176 doot.report.gen.warn("Overring Printer to: %s", self._override_level) 177 self._set_print_level(self._override_level)
178
[docs] 179 def _do_print_warn(self, *args): 180 self._override_level = "WARNING" 181 doot.report.gen.warn("Overring Printer to: %s", self._override_level) 182 self._set_print_level(self._override_level)
183
[docs] 184 def _do_print_debug(self, *args): 185 self._override_level = "DEBUG" 186 doot.report.gen.warn("Overring Printer to: %s", self._override_level) 187 self._set_print_level(self._override_level)
188
[docs] 189 def _do_print_state(self, *args): 190 doot.report.gen.trace("Current State:") 191 doot.report.gen.trace("%20s : %s", "CLI Args", dict(doot.args)) 192 for arg in args: 193 match arg: 194 case actspec.ActionSpec(): 195 doot.report.gen.trace("%20s : %s", "Action", str(arg.do)) 196 doot.report.gen.trace("%20s : %s", "Action Spec kwargs", dict(arg.kwargs)) 197 case TaskAPI.Task_i(): 198 doot.report.gen.trace("%20s : %s", "Task Name", str(arg.name)) 199 doot.report.gen.trace("%20s : %s", "Task State", arg.state) 200 case TaskAPI.Job_i(): 201 doot.report.gen.trace("%20s : %s", "Job Args", arg.args)
202
[docs] 203class _Stepper_m: 204 205 def __init__(self, *args, **kwargs): 206 super().__init__(*args, **kwargs) 207 self._conf_types = [] 208 self._override_level = "INFO" 209 self._has_quit = False 210
[docs] 211 def _expand_job(self, job:TaskAPI.Job_i) -> None: 212 if self._pause(job): 213 super()._expand_job(job) 214 else: 215 doot.report.gen.trace("::- ...")
216
[docs] 217 def _execute_task(self, task:TaskAPI.Task_i) -> None: 218 if self._pause(task): 219 super()._execute_task(task) 220 else: 221 doot.report.gen.trace("::- ...")
222
[docs] 223 def _execute_action(self, count, action, task) -> None: 224 if self._pause(action, task, step=f"{self.step}.{count}"): 225 return super()._execute_action(count, action, task) 226 else: 227 doot.report.gen.trace("::- ...")
228
[docs] 229 def _pause(self, *args, step=None) -> bool: 230 if self._has_quit: 231 return False 232 233 if not bool(self._conf_types): 234 return True 235 236 target = args[0] 237 match target: 238 case _ if True in self._conf_types: 239 pass 240 case TaskAPI.Job_i() if TaskAPI.Job_i not in self._conf_types: 241 return True 242 case TaskAPI.Task_i() if TaskAPI.Task_i not in self._conf_types: 243 return True 244 case actspec.ActionSpec() if actspec.ActionSpec not in self._conf_types: 245 return True 246 247 doot.report.gen.trace("") 248 doot.report.gen.trace( "::- Step %s: %s", (step or self.step), str(target)) 249 result = None 250 while not isinstance(result, bool): 251 response = input(self._conf_prompt) 252 if hasattr(self, f"{self._cmd_prefix}{response}"): 253 result = getattr(self, self._cmd_prefix + response)(*args) 254 elif response in self._aliases: 255 result = getattr(self, self._cmd_prefix + self._aliases[response])(*args) 256 else: 257 result = self._do_default(*args) 258 259 return result
260
[docs] 261 def _set_print_level(self, level=None): 262 if level: 263 super()._set_print_level(self._override_level) 264 else: 265 super()._set_print_level()
266
[docs] 267 def set_confirm_type(self, val): 268 """ Sets the runners `breakpoints` """ 269 match val: 270 case "job": 271 self._conf_types = [TaskAPI.Job_i] 272 case "task": 273 self._conf_types = [TaskAPI.Task_i] 274 case "action": 275 self._conf_types = [actspec.ActionSpec] 276 case "all": 277 self._conf_types = [True] 278 case x: 279 raise ValueError("Unrecognized Step Runner Breakpoint", x)
280 281 282##--|
[docs] 283@Proto(WorkflowRunner_p) 284@Mixin(_Instructions_m, _Stepper_m) 285class DootStepRunner(DootRunner): 286 """ extends the default runner with step control """ 287 _conf_prompt = "::- Command? (? for help): " 288 _cmd_prefix = "_do_" 289 _aliases : ClassVar[dict] = CMDS.copy() 290 291 def __init__(self, *args, **kwargs) -> None: 292 super().__init__(*args, **kwargs) 293 self.set_confim_type(break_on)