From 36108557d955a67ca4af3364c1d13bf253babf2a Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 23 Aug 2026 02:08:48 +0000 Subject: [PATCH 1/2] perf: optimize json schema registry loading by caching it globally Previously, `_validator` was cached per-contract, but the registry building logic was inside it. This meant that when validating a different contract, the entire `schema` directory was re-read and re-parsed to rebuild the same registry. This change extracts the registry building into its own cached helper function. Co-authored-by: joy7758 <138868899+joy7758@users.noreply.github.com> --- .gitignore | 1 + benchmark.py | 62 ++++++++++------------------- src/titmas_action_gate/contracts.py | 18 ++++++--- 3 files changed, 35 insertions(+), 46 deletions(-) diff --git a/.gitignore b/.gitignore index ae69a1f..24c743c 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ evaluations/results/ # Installed locally from an exact source lock. Upstream redistribution permission is unresolved. skills/alibabacloud-resourcecenter-search/ venv/ +.venv/ diff --git a/benchmark.py b/benchmark.py index 9347732..00a87c9 100644 --- a/benchmark.py +++ b/benchmark.py @@ -1,40 +1,22 @@ -import re -import timeit - -source_commit = "1234567890abcdef1234567890abcdef12345678" -invalid_commit = "1234567890abcdef1234567890abcdef1234567g" -hex_set = set("0123456789abcdef") -hex_pattern = re.compile(r"^[0-9a-f]{40}$") - - -def original(val): - return not isinstance(val, str) or len(val) != 40 or any(v not in "0123456789abcdef" for v in val) - - -def with_set(val): - return not isinstance(val, str) or len(val) != 40 or not set(val).issubset(hex_set) - - -def with_regex(val): - return not isinstance(val, str) or not hex_pattern.match(val) - - -def with_int(val): - if not isinstance(val, str) or len(val) != 40: - return True - try: - # this accepts uppercase and signs, so maybe not exact match - int(val, 16) - return not val.islower() and not val.isdigit() # this is complicated - except ValueError: - return True - - -print("Original (valid):", timeit.timeit("original(source_commit)", globals=globals(), number=100000)) -print("Original (invalid):", timeit.timeit("original(invalid_commit)", globals=globals(), number=100000)) - -print("Set (valid):", timeit.timeit("with_set(source_commit)", globals=globals(), number=100000)) -print("Set (invalid):", timeit.timeit("with_set(invalid_commit)", globals=globals(), number=100000)) - -print("Regex (valid):", timeit.timeit("with_regex(source_commit)", globals=globals(), number=100000)) -print("Regex (invalid):", timeit.timeit("with_regex(invalid_commit)", globals=globals(), number=100000)) +import time +import os +import sys +sys.path.insert(0, os.path.abspath('src')) +from titmas_action_gate.contracts import _validator, SCHEMA_FILES, schema_directory + +def bench_original(): + # Warm up schema directory if needed + schema_directory() + + # We want to measure calling _validator_unwrapped multiple times + _validator_unwrapped = getattr(_validator, "__wrapped__", _validator) + + start = time.perf_counter() + for _ in range(10): + for contract in SCHEMA_FILES.keys(): + _validator_unwrapped(contract) + end = time.perf_counter() + print(f"Time for building validators: {end - start:.4f}s") + +if __name__ == "__main__": + bench_original() diff --git a/src/titmas_action_gate/contracts.py b/src/titmas_action_gate/contracts.py index c93ea3e..4c9e26c 100644 --- a/src/titmas_action_gate/contracts.py +++ b/src/titmas_action_gate/contracts.py @@ -47,6 +47,17 @@ def schema_directory() -> Path: ) +@functools.cache +def _schema_registry() -> Registry: + directory = schema_directory() + resources = [] + for path in directory.glob("*.schema.json"): + candidate = json.loads(path.read_text(encoding="utf-8")) + if isinstance(candidate.get("$id"), str): + resources.append((candidate["$id"], Resource.from_contents(candidate))) + return Registry().with_resources(resources) + + @functools.cache def _validator(contract: str) -> Draft202012Validator: try: @@ -55,12 +66,7 @@ def _validator(contract: str) -> Draft202012Validator: raise ContractValidationError("SCHEMA_UNKNOWN", f"Unknown contract: {contract}") from exc directory = schema_directory() schema = json.loads((directory / filename).read_text(encoding="utf-8")) - resources = [] - for path in directory.glob("*.schema.json"): - candidate = json.loads(path.read_text(encoding="utf-8")) - if isinstance(candidate.get("$id"), str): - resources.append((candidate["$id"], Resource.from_contents(candidate))) - registry = Registry().with_resources(resources) + registry = _schema_registry() return Draft202012Validator(schema, registry=registry, format_checker=FormatChecker()) From 3f3f2d5e4ba0992ae07db5878551490f26703230 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 23 Aug 2026 02:11:59 +0000 Subject: [PATCH 2/2] perf: optimize json schema registry loading by caching it globally Previously, `_validator` was cached per-contract, but the registry building logic was inside it. This meant that when validating a different contract, the entire `schema` directory was re-read and re-parsed to rebuild the same registry. This change extracts the registry building into its own cached helper function. Also fixes ruff lint errors that were causing the CI pipeline to fail. Co-authored-by: joy7758 <138868899+joy7758@users.noreply.github.com> --- benchmark.py | 62 +++++++++++++++-------- src/titmas_action_gate/service.py | 2 +- tests/test_security_argument_injection.py | 5 +- tests/test_workflow.py | 6 +-- 4 files changed, 46 insertions(+), 29 deletions(-) diff --git a/benchmark.py b/benchmark.py index 00a87c9..9347732 100644 --- a/benchmark.py +++ b/benchmark.py @@ -1,22 +1,40 @@ -import time -import os -import sys -sys.path.insert(0, os.path.abspath('src')) -from titmas_action_gate.contracts import _validator, SCHEMA_FILES, schema_directory - -def bench_original(): - # Warm up schema directory if needed - schema_directory() - - # We want to measure calling _validator_unwrapped multiple times - _validator_unwrapped = getattr(_validator, "__wrapped__", _validator) - - start = time.perf_counter() - for _ in range(10): - for contract in SCHEMA_FILES.keys(): - _validator_unwrapped(contract) - end = time.perf_counter() - print(f"Time for building validators: {end - start:.4f}s") - -if __name__ == "__main__": - bench_original() +import re +import timeit + +source_commit = "1234567890abcdef1234567890abcdef12345678" +invalid_commit = "1234567890abcdef1234567890abcdef1234567g" +hex_set = set("0123456789abcdef") +hex_pattern = re.compile(r"^[0-9a-f]{40}$") + + +def original(val): + return not isinstance(val, str) or len(val) != 40 or any(v not in "0123456789abcdef" for v in val) + + +def with_set(val): + return not isinstance(val, str) or len(val) != 40 or not set(val).issubset(hex_set) + + +def with_regex(val): + return not isinstance(val, str) or not hex_pattern.match(val) + + +def with_int(val): + if not isinstance(val, str) or len(val) != 40: + return True + try: + # this accepts uppercase and signs, so maybe not exact match + int(val, 16) + return not val.islower() and not val.isdigit() # this is complicated + except ValueError: + return True + + +print("Original (valid):", timeit.timeit("original(source_commit)", globals=globals(), number=100000)) +print("Original (invalid):", timeit.timeit("original(invalid_commit)", globals=globals(), number=100000)) + +print("Set (valid):", timeit.timeit("with_set(source_commit)", globals=globals(), number=100000)) +print("Set (invalid):", timeit.timeit("with_set(invalid_commit)", globals=globals(), number=100000)) + +print("Regex (valid):", timeit.timeit("with_regex(source_commit)", globals=globals(), number=100000)) +print("Regex (invalid):", timeit.timeit("with_regex(invalid_commit)", globals=globals(), number=100000)) diff --git a/src/titmas_action_gate/service.py b/src/titmas_action_gate/service.py index 90d7164..df6bc08 100644 --- a/src/titmas_action_gate/service.py +++ b/src/titmas_action_gate/service.py @@ -5,6 +5,7 @@ import hashlib import hmac import json +from dataclasses import dataclass from datetime import datetime, timedelta from pathlib import Path from typing import Any @@ -20,7 +21,6 @@ from .provider import GitHubProvider from .signing import HmacRecordSigner from .store import AppendOnlyStore -from dataclasses import dataclass @dataclass(frozen=True) diff --git a/tests/test_security_argument_injection.py b/tests/test_security_argument_injection.py index 3990256..4d8bb42 100644 --- a/tests/test_security_argument_injection.py +++ b/tests/test_security_argument_injection.py @@ -1,9 +1,8 @@ import unittest -import subprocess -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch from titmas_action_gate.provider import GhCliProvider -from titmas_action_gate.errors import ActionGateError + class ProviderSecurityTests(unittest.TestCase): @patch("subprocess.run") diff --git a/tests/test_workflow.py b/tests/test_workflow.py index 61c356d..461a3bf 100644 --- a/tests/test_workflow.py +++ b/tests/test_workflow.py @@ -1,8 +1,8 @@ +import json import tempfile import unittest from pathlib import Path -import json from titmas_action_gate.workflow import validate_agentteams_template, write_demo_report @@ -83,7 +83,7 @@ def test_write_demo_report(self): self.assertEqual(result_path, output_file) self.assertTrue(output_file.exists()) - with open(output_file, "r", encoding="utf-8") as f: + with open(output_file, encoding="utf-8") as f: content = json.load(f) self.assertEqual(content, report_data) @@ -95,7 +95,7 @@ def test_write_demo_report_creates_directories(self): write_demo_report(report_data, output_file) self.assertTrue(output_file.exists()) - with open(output_file, "r", encoding="utf-8") as f: + with open(output_file, encoding="utf-8") as f: content = json.load(f) self.assertEqual(content, report_data)