[CI] Stop the config ratchets re-parsing the package on every scan (#36240)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alex Nails
2026-08-24 18:22:32 -07:00
committed by GitHub
co-authored by Claude Opus 5
parent f8f9226cd2
commit 68575b23d0
8 changed files with 115 additions and 45 deletions
@@ -13,6 +13,7 @@ one field that is deliberately read before resolution touches it.
"""
import ast
import functools
import pathlib
import unittest
@@ -51,6 +52,24 @@ _STALE_FROM_THE_REGISTRIES = frozenset(
)
@functools.lru_cache(maxsize=None)
def _parsed(path):
return ast.parse(path.read_text(encoding="utf-8-sig"))
@functools.lru_cache(maxsize=None)
def _declared_resolution_fields(path):
fields = set()
for node in ast.walk(_parsed(path)):
if (
isinstance(node, ast.Call)
and isinstance(node.func, ast.Name)
and node.func.id == "declare_resolution"
):
fields |= {kw.arg for kw in node.keywords if kw.arg}
return frozenset(fields)
def _registry_declared_fields():
"""What the live registries and passes declare.
@@ -79,7 +98,7 @@ def _registry_collection_is_after_the_build():
collection above this handler's own `get_model_config()` call does not move
it above the configuration another handler already cached.
"""
tree = ast.parse((_SRT / "server_args.py").read_text(encoding="utf-8-sig"))
tree = _parsed(_SRT / "server_args.py")
handler = next(
node
for node in ast.walk(tree)
@@ -129,7 +148,7 @@ def _server_args_names(tree, path):
def _constructor_reads():
"""Fields `ModelConfig.from_server_args` takes off the record."""
path = _SRT / "configs/model_config.py"
tree = ast.parse(path.read_text(encoding="utf-8-sig"))
tree = _parsed(path)
constructor = next(
node
for node in ast.walk(tree)
@@ -177,7 +196,7 @@ def _late_resolution_fields():
path = _SRT / name
if not path.exists():
continue
tree = ast.parse(path.read_text(encoding="utf-8-sig"))
tree = _parsed(path)
for node in ast.walk(tree):
if not isinstance(node, ast.Call):
continue
@@ -205,7 +224,7 @@ def _hook_declarations(dispatch, source_module):
`test_every_opaque_callback_is_still_late`.
"""
imported = {}
for node in ast.walk(ast.parse(source_module.read_text(encoding="utf-8-sig"))):
for node in ast.walk(_parsed(source_module)):
if isinstance(node, ast.ImportFrom) and node.module:
for alias in node.names:
imported[alias.asname or alias.name] = node.module
@@ -225,22 +244,14 @@ def _hook_declarations(dispatch, source_module):
path = _SRT / (module[len("sglang.srt.") :].replace(".", "/") + ".py")
if not path.exists():
continue
for inner in ast.walk(ast.parse(path.read_text(encoding="utf-8-sig"))):
if (
isinstance(inner, ast.Call)
and isinstance(inner.func, ast.Name)
and inner.func.id == "declare_resolution"
):
for keyword in inner.keywords:
if keyword.arg:
out[keyword.arg] = max(out.get(keyword.arg, 0), node.lineno)
for field in _declared_resolution_fields(path):
out[field] = max(out.get(field, 0), node.lineno)
return out
def _pipeline():
"""(ordered steps, {step: methods it reaches}) for the resolution dispatch."""
source = (_SRT / "server_args.py").read_text(encoding="utf-8-sig")
tree = ast.parse(source)
tree = _parsed(_SRT / "server_args.py")
record = next(
node
for node in tree.body
@@ -305,7 +316,7 @@ def _opaque_callback_positions(dispatch, source_module):
it.
"""
imported = {}
for node in ast.walk(ast.parse(source_module.read_text(encoding="utf-8-sig"))):
for node in ast.walk(_parsed(source_module)):
if isinstance(node, ast.ImportFrom) and node.module:
for alias in node.names:
imported[alias.asname or alias.name] = node.module
@@ -349,7 +360,7 @@ def _opaque_callback_positions(dispatch, source_module):
path = _SRT / (module[len("sglang.srt.") :].replace(".", "/") + ".py")
if not path.exists():
continue
for spelling in callbacks_in(ast.parse(path.read_text(encoding="utf-8-sig"))):
for spelling in callbacks_in(_parsed(path)):
positions[spelling] = min(positions.get(spelling, 10**9), node.lineno)
return positions
@@ -392,7 +403,7 @@ def _declaration_positions():
source_module = _SRT / "server_args.py"
imported = {}
for node in ast.walk(ast.parse(source_module.read_text(encoding="utf-8-sig"))):
for node in ast.walk(_parsed(source_module)):
if isinstance(node, ast.ImportFrom) and node.module:
for alias in node.names:
imported[alias.asname or alias.name] = node.module
@@ -405,15 +416,7 @@ def _declaration_positions():
path = _SRT / (module[len("sglang.srt.") :].replace(".", "/") + ".py")
if not path.exists():
return frozenset()
fields = set()
for node in ast.walk(ast.parse(path.read_text(encoding="utf-8-sig"))):
if (
isinstance(node, ast.Call)
and isinstance(node.func, ast.Name)
and node.func.id == "declare_resolution"
):
fields |= {kw.arg for kw in node.keywords if kw.arg}
return frozenset(fields)
return _declared_resolution_fields(path)
declared_at = {}
for index, step in enumerate(steps):
@@ -28,6 +28,7 @@ It is a ratchet, not a proof.
"""
import ast
import functools
import inspect
import pathlib
import unittest
@@ -114,6 +115,7 @@ def _registry_functions():
return functions
@functools.lru_cache(maxsize=None)
def _registered_entries():
"""Entries the import map cannot reach: passes and override providers.
@@ -138,13 +140,17 @@ def _registered_entries():
# from the source. The entry carries the *defining* file: `_reaches_a_bag`
# walks functions in the entry's file, so a call-site key walks nothing.
by_value = set()
trees = {}
for path in sorted(_SRT.rglob("*.py")):
sources = {
path: path.read_text(encoding="utf-8-sig")
for path in sorted(_SRT.rglob("*.py"))
}
for path, source in sources.items():
if "run_post_process_pass" not in source:
continue
try:
trees[path] = ast.parse(path.read_text(encoding="utf-8-sig"))
tree = ast.parse(source)
except SyntaxError:
continue
for path, tree in trees.items():
for node in ast.walk(tree):
if (
isinstance(node, ast.Call)
@@ -154,6 +160,14 @@ def _registered_entries():
and isinstance(node.args[1], ast.Name)
):
by_value.add(node.args[1].id)
trees = {}
for path, source in sources.items():
if not any(name in source for name in by_value):
continue
try:
trees[path] = ast.parse(source)
except SyntaxError:
continue
for name in sorted(by_value):
defined_in = [
path
@@ -173,14 +187,19 @@ def _registered_entries():
return entries
def _reaches_a_bag(path, entry):
"""Does `entry` in `path` reach a bag accessor, following calls in-module?"""
@functools.lru_cache(maxsize=None)
def _functions_in(path):
tree = ast.parse(path.read_text(encoding="utf-8-sig"))
functions = {
return {
node.name: node
for node in ast.walk(tree)
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
}
def _reaches_a_bag(path, entry):
"""Does `entry` in `path` reach a bag accessor, following calls in-module?"""
functions = _functions_in(path)
seen = set()
def walk(name):
@@ -47,8 +47,11 @@ def _declared_by_keyword():
"""Fields named as a keyword at a declaration site."""
written = set()
for path in sorted(_SRT.rglob("*.py")):
source = path.read_text(encoding="utf-8-sig")
if not any(declarer in source for declarer in _DECLARERS):
continue
try:
tree = ast.parse(path.read_text(encoding="utf-8-sig"))
tree = ast.parse(source)
except SyntaxError:
raise AssertionError(f"unparsable module in the census: {path}")
for node in ast.walk(tree):
@@ -293,8 +296,11 @@ def _subtrees_with_their_own_record():
rel = path.relative_to(_PACKAGE).as_posix()
if rel.startswith("srt/") or "/" not in rel:
continue
source = path.read_text(encoding="utf-8-sig")
if "ServerArgs" not in source:
continue
try:
tree = ast.parse(path.read_text(encoding="utf-8-sig"))
tree = ast.parse(source)
except SyntaxError:
continue
if any(
@@ -333,8 +339,11 @@ def _chain_reads(written):
under_srt = rel[len("srt/") :]
if path.name in _OWNERS or under_srt.startswith(_OWNERS[-1]):
continue
source = path.read_text(encoding="utf-8-sig")
if "server_args" not in source:
continue
try:
tree = ast.parse(path.read_text(encoding="utf-8-sig"))
tree = ast.parse(source)
except SyntaxError:
raise AssertionError(f"unparsable module in the census: {rel}")
if (
@@ -498,8 +498,11 @@ def _field_reads():
rel = path.relative_to(_PACKAGE_ROOT).as_posix()
if rel.startswith(_SLOT_OWNERS):
continue
source = path.read_text()
if "get_server_args" not in source:
continue
try:
tree = ast.parse(path.read_text())
tree = ast.parse(source)
except SyntaxError:
continue
module_direct, module_alias = _collect(rel, tree)
@@ -551,8 +554,11 @@ class TestConfiguredSizeCallSites(CustomTestCase):
rel = path.relative_to(_PACKAGE_ROOT).as_posix()
if rel.startswith(_SLOT_OWNERS):
continue
source = path.read_text()
if "configured_" not in source:
continue
try:
tree = ast.parse(path.read_text())
tree = ast.parse(source)
except SyntaxError:
continue
for node in ast.walk(tree):
@@ -588,8 +594,11 @@ class TestNoRenamedAccessorImports(CustomTestCase):
offenders = []
for path in sorted(_PACKAGE_ROOT.rglob("*.py")):
rel = path.relative_to(_PACKAGE_ROOT).as_posix()
source = path.read_text()
if "get_server_args" not in source and "configured_" not in source:
continue
try:
tree = ast.parse(path.read_text())
tree = ast.parse(source)
except SyntaxError:
continue
for node in ast.walk(tree):
@@ -8,6 +8,7 @@ reaches, because nothing short of booting a server runs the launcher.
"""
import ast
import functools
import pathlib
import unittest
@@ -82,6 +83,7 @@ def _multiprocessing_names(tree):
return modules, constructors
@functools.lru_cache(maxsize=None)
def _configured_accessors() -> frozenset:
"""The `configured_*_size()` names `runtime_context` exports.
@@ -217,11 +219,14 @@ def _launch_paths():
nothing, which no derivation can reach.
"""
seen = {}
sizes = frozenset(_LIVE_SHADOWED) | _configured_accessors()
for path in sorted(_PACKAGE_ROOT.rglob("*.py")):
source = path.read_text()
# Every spawn shape below names Process, ProcessPoolExecutor or Popen.
if not any(name in source for name in ("Process", "Popen", "spawn")):
continue
if not any(name in source for name in sizes):
continue
try:
tree = ast.parse(source)
except SyntaxError:
@@ -397,6 +397,9 @@ def _publishing_functions():
rel = path.relative_to(_PACKAGE_ROOT).as_posix()
if rel in _PUBLISH_HOMES:
continue
source = path.read_text(encoding="utf-8-sig")
if not any(name in source for name in _PUBLISH_NAMES):
continue
mod = _module(rel)
if mod is None or not mod.publishers:
continue
@@ -81,8 +81,11 @@ class TestServerArgsNamespaces(CustomTestCase):
for path in sorted(srt.rglob("*.py")):
if path.name == "runtime_context.py":
continue
source = path.read_text(encoding="utf-8-sig")
if "runtime_context" not in source:
continue
try:
tree = ast.parse(path.read_text(encoding="utf-8-sig"))
tree = ast.parse(source)
except SyntaxError:
self.fail(f"unparsable module in the census: {path}")
bindings = collections.defaultdict(set)
@@ -155,8 +158,11 @@ class TestServerArgsNamespaces(CustomTestCase):
sites = 0
disagreements = []
for path in sorted(srt.rglob("*.py")):
source = path.read_text(encoding="utf-8-sig")
if not any(name in source for name in accessors):
continue
try:
tree = ast.parse(path.read_text(encoding="utf-8-sig"))
tree = ast.parse(source)
except SyntaxError:
self.fail(f"unparsable module in the census: {path}")
for node in ast.walk(tree):
@@ -682,8 +682,11 @@ class TestSuppliedInstanceExposure(CustomTestCase):
root = _PACKAGE_ROOT
for path in sorted(root.rglob("*.py")):
rel = path.relative_to(root).as_posix()
source = path.read_text(encoding="utf-8-sig")
if "_late_resolution" not in source:
continue
try:
tree = ast.parse(path.read_text(encoding="utf-8-sig"))
tree = ast.parse(source)
except SyntaxError:
self.fail(f"unparsable module in the census: {rel}")
for node in ast.walk(tree):
@@ -734,6 +737,8 @@ class TestSuppliedInstanceExposure(CustomTestCase):
written |= _expanded_override_keys(rel, tree, node, kw)
return written
_READS_CACHE = None
def _supplied_instance_reads(self) -> set:
"""Three spellings of the same read: ``server_args.field`` off the
parameter, ``getattr(server_args, "field", default)`` with a literal
@@ -746,13 +751,18 @@ class TestSuppliedInstanceExposure(CustomTestCase):
census still does not count -- but those reads are gone for every
resolution-written field and ``test_chain_read_ratchet.py`` holds them
at zero, so the gap is no longer where the risk is."""
if TestSuppliedInstanceExposure._READS_CACHE is not None:
return TestSuppliedInstanceExposure._READS_CACHE
pairs = set()
for path in sorted(_PACKAGE_ROOT.rglob("*.py")):
rel = path.relative_to(_PACKAGE_ROOT).as_posix()
if rel.startswith(_OWNERS):
continue
source = path.read_text(encoding="utf-8-sig")
if "server_args" not in source:
continue
try:
tree = ast.parse(path.read_text(encoding="utf-8-sig"))
tree = ast.parse(source)
except SyntaxError:
# A silently dropped module shrinks `found` and reads as
# intentional surface shrinkage under the bidirectional pin.
@@ -818,6 +828,7 @@ class TestSuppliedInstanceExposure(CustomTestCase):
and node.value.value.id == "self"
):
pairs.add((rel, node.attr))
TestSuppliedInstanceExposure._READS_CACHE = pairs
return pairs
@staticmethod
@@ -826,8 +837,13 @@ class TestSuppliedInstanceExposure(CustomTestCase):
written = set()
for path in sorted(_PACKAGE_ROOT.rglob("*.py")):
rel = path.relative_to(_PACKAGE_ROOT).as_posix()
source = path.read_text(encoding="utf-8-sig")
if "record_config_updates" not in source and not (
"get_context" in source and "override" in source
):
continue
try:
tree = ast.parse(path.read_text(encoding="utf-8-sig"))
tree = ast.parse(source)
except SyntaxError:
raise AssertionError(f"unparsable module in the census: {rel}")
for node in ast.walk(tree):