refactor(issue-17): item 4 - the delegate-job wrapper's decisions move to Python
The wrapper keeps spawn / wait / kill, key injection and the herdr
diagnostic probe; what it decided in bash is now lib_py/delegation/wrapper.py,
reached through one `_decide` helper:
- finalize_unfinished: the clock choice, the disk grace and broker
re-observation, the close request / label, the close and the event
(finalize-begin / finalize-end; bash runs only the busy probe between them)
- start_stall_watch: whether to watch (MAM_STALL_WATCH, remote, human)
- _map_sub_rc, _rc_for (+ MAM_DELEGATE_FAIL_CLOSED), the subscriber
early-exit check and cmd_wait's rc normalisation
- mam_int / mam_int_knob run lib_py/ints.py, which is also the runner's
_positive_int: one implementation of the Issue #26 rule
No behaviour change: tests/test_issue17_item4_decisions.py runs the old
bash functions (copied verbatim from 7209750) and the new wrapper on the
same inputs and compares output, FINAL_JOB_STATUS and the registry /
publish calls. Three static tests that pinned the old bash literals now
pin the Python entry points instead.
Refs #17
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7209750ac7
commit
63d9c205ff
13 files changed
+1131
-289
No files matched your search
@@ -0,0 +1,63 @@
|
||||
"""Issue #26 / #17 item 4: the one integer rule for knobs and flags.
|
||||
|
||||
lib_int.sh's mam_int / mam_int_knob run this file, and the loop runner's
|
||||
_positive_int is positive_int below, so bash and Python share one
|
||||
implementation. Surrounding ASCII blanks are allowed, then ASCII digits only,
|
||||
read as DECIMAL (bash would read 0700 as octal 448), and
|
||||
minimum <= n <= 999999999. The per-knob minimum lives in
|
||||
tests/test_issue26_ints.py.
|
||||
|
||||
Stdlib only and Python 3.9: lib_int.sh runs it as a script (python3 -I),
|
||||
where lib_py is not importable.
|
||||
|
||||
ints.py int <value> <min> print n, or exit 1
|
||||
ints.py knob <name> <value> <default> <min> print n; empty gives <default>,
|
||||
invalid gives <default> + a warning
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from typing import List, Optional
|
||||
|
||||
_BLANKS = " \t\n\r\v\f"
|
||||
|
||||
|
||||
def positive_int(text: Optional[str], minimum: int = 1) -> Optional[int]:
|
||||
"""n when `text` is a decimal integer >= minimum (and <= 999999999), else None."""
|
||||
s = (text or "").strip(_BLANKS)
|
||||
if not s or not s.isascii() or not s.isdigit():
|
||||
return None
|
||||
s = s.lstrip("0") or "0"
|
||||
if len(s) > 9: # bounded before int(), which refuses > 4300 digits
|
||||
return None
|
||||
n = int(s)
|
||||
return n if n >= minimum else None
|
||||
|
||||
|
||||
def knob(name: str, raw: str, default: str, minimum: int) -> str:
|
||||
"""mam_int_knob: the value of $name by the rule, <default> when empty or invalid."""
|
||||
if not raw:
|
||||
return default
|
||||
n = positive_int(raw, minimum)
|
||||
if n is not None:
|
||||
return str(n)
|
||||
print(f"WARNING: {name}={raw} ignored (not a decimal integer >= {minimum}); using {default}", file=sys.stderr)
|
||||
return default
|
||||
|
||||
|
||||
def main(argv: List[str]) -> int:
|
||||
if len(argv) == 3 and argv[0] == "int":
|
||||
n = positive_int(argv[1], int(argv[2]))
|
||||
if n is None:
|
||||
return 1
|
||||
print(n)
|
||||
return 0
|
||||
if len(argv) == 5 and argv[0] == "knob":
|
||||
print(knob(argv[1], argv[2], argv[3], int(argv[4])))
|
||||
return 0
|
||||
print("usage: ints.py int <value> <min> | knob <name> <value> <default> <min>", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
Reference in new issue
Block a user