fix: rewrite apply_patch.py to handle multi-line web.Application()

This commit is contained in:
railway-app[bot] 2026-04-27 22:40:40 +00:00 committed by GitHub
parent a68af047c2
commit a44a320d51
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -3,6 +3,21 @@ Injeta o registro das rotas aiohttp do skills_api dentro do método start()
do APIServerAdapter no api_server.py do Hermes. do APIServerAdapter no api_server.py do Hermes.
Idempotente: se o marcador existir, não faz nada. Idempotente: se o marcador existir, não faz nada.
Strategy
--------
The `web.Application(...)` call may span multiple lines, e.g.:
self._app = web.Application(
middlewares=[...],
client_max_size=...,
)
We therefore use a two-phase approach:
1. Locate the LHS assignment (``<indent><var> = web.Application(``) with a
simple MULTILINE regex this is always a single line.
2. From that position, scan forward to find the matching closing parenthesis,
correctly handling nested parentheses.
""" """
from __future__ import annotations from __future__ import annotations
@ -12,15 +27,32 @@ from pathlib import Path
MARKER = "# >>> hermes-custom: skills_api <<<" MARKER = "# >>> hermes-custom: skills_api <<<"
# Regex: captura uma linha que cria a aiohttp Application, ex: # Phase-1 regex: matches the *start* of the assignment up to and including the
# self._app = web.Application(...) # opening parenthesis of web.Application. Does NOT require the call to end on
# app = web.Application(...) # the same line, so it works for both single-line and multi-line forms.
APP_ASSIGN_RE = re.compile( APP_ASSIGN_START_RE = re.compile(
r"^(?P<indent>[ \t]+)(?P<lhs>(?:self\._?app|app))\s*=\s*web\.Application\([^)]*\)\s*$", r"^(?P<indent>[ \t]+)(?P<lhs>(?:self\._?app|app))\s*=\s*web\.Application\(",
re.MULTILINE, re.MULTILINE,
) )
def _find_closing_paren(src: str, open_pos: int) -> int:
"""Return the index of the ')' that closes the '(' at *open_pos*.
Handles nested parentheses. Raises ValueError if not found.
"""
depth = 0
for i in range(open_pos, len(src)):
ch = src[i]
if ch == "(":
depth += 1
elif ch == ")":
depth -= 1
if depth == 0:
return i
raise ValueError(f"No matching closing parenthesis found starting at offset {open_pos}")
def build_injection(indent: str, app_var: str) -> str: def build_injection(indent: str, app_var: str) -> str:
lines = [ lines = [
"", "",
@ -47,17 +79,39 @@ def main(target: str) -> int:
print("[apply_patch] already applied, skipping") print("[apply_patch] already applied, skipping")
return 0 return 0
match = APP_ASSIGN_RE.search(src) # Phase 1 — find the assignment start
match = APP_ASSIGN_START_RE.search(src)
if not match: if not match:
# Emit a diagnostic snippet to help debug future mismatches
print( print(
"[apply_patch] ERROR: não encontrei `... = web.Application(...)` no api_server.py", "[apply_patch] ERROR: não encontrei `... = web.Application(` no api_server.py",
file=sys.stderr, file=sys.stderr,
) )
# Print lines that contain "Application" to aid debugging
for lineno, line in enumerate(src.splitlines(), 1):
if "Application" in line:
print(f"[apply_patch] hint line {lineno}: {line!r}", file=sys.stderr)
return 2 return 2
indent = match.group("indent") indent = match.group("indent")
app_var = match.group("lhs") app_var = match.group("lhs")
insert_at = match.end()
# Phase 2 — walk forward to find the matching closing parenthesis
open_paren_pos = match.end() - 1 # position of the '(' in web.Application(
try:
close_paren_pos = _find_closing_paren(src, open_paren_pos)
except ValueError as exc:
print(f"[apply_patch] ERROR: {exc}", file=sys.stderr)
return 2
# Insert after the closing ')' (and any trailing whitespace on that line)
# so we land right after the complete statement.
insert_at = close_paren_pos + 1
# Advance past an optional trailing comment / whitespace up to the newline
newline_pos = src.find("\n", insert_at)
if newline_pos != -1:
insert_at = newline_pos # inject before the newline so indentation stays clean
injection = build_injection(indent, app_var) injection = build_injection(indent, app_var)
new_src = src[:insert_at] + injection + src[insert_at:] new_src = src[:insert_at] + injection + src[insert_at:]