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)