Skills/Python: harden script edge cases and add regression tests (#24277)

* Skill creator: skip self-including .skill output

* Skill creator tests: cover output-dir-inside-skill case

* Skill validator: parse frontmatter robustly across newlines

* Skill validator tests: add CRLF and malformed frontmatter coverage

* Model usage: require positive --days value

* Model usage tests: cover --days validation and filtering

* Nano banana: close input image handles after loading

* Skill validator: keep type hints compatible with older python

* Changelog: credit @vincentkoc for Python skills hardening
This commit is contained in:
Vincent Koc
2026-02-23 02:34:23 -05:00
committed by GitHub
parent 36400df086
commit c8a62e1cea
8 changed files with 137 additions and 11 deletions

View File

@@ -17,6 +17,16 @@ from datetime import date, datetime, timedelta
from typing import Any, Dict, Iterable, List, Optional, Tuple
def positive_int(value: str) -> int:
try:
parsed = int(value)
except ValueError as exc:
raise argparse.ArgumentTypeError("must be an integer") from exc
if parsed < 1:
raise argparse.ArgumentTypeError("must be >= 1")
return parsed
def eprint(msg: str) -> None:
print(msg, file=sys.stderr)
@@ -239,7 +249,7 @@ def main() -> int:
parser.add_argument("--mode", choices=["current", "all"], default="current")
parser.add_argument("--model", help="Explicit model name to report instead of auto-current.")
parser.add_argument("--input", help="Path to codexbar cost JSON (or '-' for stdin).")
parser.add_argument("--days", type=int, help="Limit to last N days (based on daily rows).")
parser.add_argument("--days", type=positive_int, help="Limit to last N days (based on daily rows).")
parser.add_argument("--format", choices=["text", "json"], default="text")
parser.add_argument("--pretty", action="store_true", help="Pretty-print JSON output.")

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env python3
"""
Tests for model_usage helpers.
"""
import argparse
from datetime import date, timedelta
from unittest import TestCase, main
from model_usage import filter_by_days, positive_int
class TestModelUsage(TestCase):
def test_positive_int_accepts_valid_numbers(self):
self.assertEqual(positive_int("1"), 1)
self.assertEqual(positive_int("7"), 7)
def test_positive_int_rejects_zero_and_negative(self):
with self.assertRaises(argparse.ArgumentTypeError):
positive_int("0")
with self.assertRaises(argparse.ArgumentTypeError):
positive_int("-3")
def test_filter_by_days_keeps_recent_entries(self):
today = date.today()
entries = [
{"date": (today - timedelta(days=5)).strftime("%Y-%m-%d"), "modelBreakdowns": []},
{"date": (today - timedelta(days=1)).strftime("%Y-%m-%d"), "modelBreakdowns": []},
{"date": today.strftime("%Y-%m-%d"), "modelBreakdowns": []},
]
filtered = filter_by_days(entries, 2)
self.assertEqual(len(filtered), 2)
self.assertEqual(filtered[0]["date"], (today - timedelta(days=1)).strftime("%Y-%m-%d"))
self.assertEqual(filtered[1]["date"], today.strftime("%Y-%m-%d"))
if __name__ == "__main__":
main()