Merge remote-tracking branch 'origin/main' into woa-nvidia-wsl-fallback

This commit is contained in:
Daniel Han 2026-06-08 05:25:12 -07:00
commit 05f909915c
383 changed files with 7003 additions and 12043 deletions

View file

@ -52,9 +52,7 @@ EXPECTED_NOISE_FILES = {
}
# Only quoted-string occurrences in these file types can be module specifiers.
JS_LIKE_EXT = re.compile(
r"\.(ts|tsx|js|jsx|mjs|cjs|html|htm|css|scss|sass|json|jsonc)$"
)
JS_LIKE_EXT = re.compile(r"\.(ts|tsx|js|jsx|mjs|cjs|html|htm|css|scss|sass|json|jsonc)$")
# Files where JS-syntactic import patterns (static/dynamic/require/re-export)
# could be a real module reference. Markdown gets a separate gate (.mdx is
# real ESM; .md code fences are not).
@ -273,9 +271,7 @@ def classify(pkg: str, file: str, content: str) -> str | None:
if is_script and re.search(rf"\bimport\(\s*['\"]{esc}{sub}['\"]\s*\)", content):
return "dynamic_import"
# require / require.resolve
if is_script and re.search(
rf"\brequire(?:\.resolve)?\(\s*['\"]{esc}{sub}['\"]\s*\)", content
):
if is_script and re.search(rf"\brequire(?:\.resolve)?\(\s*['\"]{esc}{sub}['\"]\s*\)", content):
return "require"
# Re-exports: `export * from "pkg"`, `export { x } from "pkg"`,
# `export type { Foo } from "pkg"`. Multi-line supported.
@ -289,16 +285,12 @@ def classify(pkg: str, file: str, content: str) -> str | None:
# segment bounded by a quote / `#` / `?` or a subpath `/`, so
# `/node_modules/foo-extra/...` is NOT treated as usage of `foo`.
html_pkg = rf"{esc}(?:/[^'\"#?]*)?(?=['\"#?])"
if is_html and re.search(
rf"<script[^>]*src\s*=\s*['\"][^'\"]*/{html_pkg}", content
):
if is_html and re.search(rf"<script[^>]*src\s*=\s*['\"][^'\"]*/{html_pkg}", content):
return "html_script"
if is_html and re.search(rf"<link[^>]*href\s*=\s*['\"][^'\"]*/{html_pkg}", content):
return "html_link"
# TypeScript triple-slash
if is_ts and re.search(
rf"///\s*<reference\s+types\s*=\s*['\"]{esc}{sub}['\"]", content
):
if is_ts and re.search(rf"///\s*<reference\s+types\s*=\s*['\"]{esc}{sub}['\"]", content):
return "tsc_triple_slash"
# new URL("pkg/...", import.meta.url)
if is_script and re.search(rf"\bnew\s+URL\(\s*['\"]{esc}{sub}['\"]", content):
@ -544,19 +536,13 @@ def _next_real_bin(words: list[str], idx: int) -> str | None:
if first in {"npx", "pnpx", "bunx"} and idx + 1 < len(words):
idx += 1
continue
if (
first in {"pnpm", "yarn"}
and idx + 2 < len(words)
and words[idx + 1] in {"exec", "dlx"}
):
if first in {"pnpm", "yarn"} and idx + 2 < len(words) and words[idx + 1] in {"exec", "dlx"}:
idx += 2
continue
# 3. Wrapper bin (cross-env, dotenv, etc.). Skip the wrapper's
# own flags and any subsequent env-prefix tokens, then re-loop.
bin_token = first.removeprefix("./node_modules/.bin/").removeprefix(
"node_modules/.bin/"
)
bin_token = first.removeprefix("./node_modules/.bin/").removeprefix("node_modules/.bin/")
if bin_token in _SCRIPT_WRAPPERS and bin_token not in seen_wrappers:
seen_wrappers.add(bin_token)
idx += 1
@ -585,9 +571,7 @@ def _next_real_bin(words: list[str], idx: int) -> str | None:
return None
def scripts_bin_refs(
head_pkg: dict, bin_to_pkg: dict[str, str]
) -> dict[str, list[str]]:
def scripts_bin_refs(head_pkg: dict, bin_to_pkg: dict[str, str]) -> dict[str, list[str]]:
"""Return `{package_name: ['scripts.X: cmd', ...]}` listing every
package referenced via its bin name in package.json scripts.
@ -652,11 +636,7 @@ def tsconfig_compiler_types_refs() -> set[str]:
if not isinstance(t, str):
continue
# `vite/client` resolves to `vite` package.
pkg = (
t.split("/", 1)[0]
if not t.startswith("@")
else "/".join(t.split("/", 2)[:2])
)
pkg = t.split("/", 1)[0] if not t.startswith("@") else "/".join(t.split("/", 2)[:2])
out.add(pkg)
return out
@ -820,9 +800,7 @@ _file_lines_cache: dict[str, list[str]] = {}
def _read_file(path: str) -> list[str]:
if path not in _file_lines_cache:
try:
_file_lines_cache[path] = (
Path(path).read_text(errors = "replace").splitlines()
)
_file_lines_cache[path] = Path(path).read_text(errors = "replace").splitlines()
except (OSError, UnicodeDecodeError):
_file_lines_cache[path] = []
return _file_lines_cache[path]
@ -952,18 +930,14 @@ def find_types_runtime_usage(pkg: str, tsc_types: set[str]) -> list[Hit]:
def main() -> int:
p = argparse.ArgumentParser(
description = __doc__, formatter_class = argparse.RawTextHelpFormatter
)
p = argparse.ArgumentParser(description = __doc__, formatter_class = argparse.RawTextHelpFormatter)
p.add_argument(
"--base",
default = "origin/main",
help = "git ref to diff against (default: origin/main). "
"Examples: HEAD~1, main, a-tag, a-sha.",
)
p.add_argument(
"--base-pkg", help = "optional override: read base package.json from this path"
)
p.add_argument("--base-pkg", help = "optional override: read base package.json from this path")
p.add_argument(
"--base-lock",
help = "optional override: read base package-lock.json from this path. "
@ -1057,9 +1031,7 @@ def main() -> int:
print(f" - {w}")
print()
if missing_imports:
print(
f"Imports without a matching package.json dep ({len(missing_imports)}):"
)
print(f"Imports without a matching package.json dep ({len(missing_imports)}):")
for file, ln, spec in missing_imports[:20]:
print(f" - {file}:{ln} imports '{spec}'")
print()
@ -1097,9 +1069,7 @@ def main() -> int:
return 1
return 0
print(
f"Checking {len(removed)} removed package(s) from studio/frontend/package.json"
)
print(f"Checking {len(removed)} removed package(s) from studio/frontend/package.json")
print(f"Base: {args.base} Head: working tree")
print()
@ -1127,9 +1097,7 @@ def main() -> int:
top = f"node_modules/{name}"
top_path = top if top in reachable_paths else None
nested = sorted(
p
for p in reachable_paths
if p != top and p.endswith(f"/node_modules/{name}")
p for p in reachable_paths if p != top and p.endswith(f"/node_modules/{name}")
)
return top_path, nested
@ -1177,9 +1145,7 @@ def main() -> int:
_print_hygiene()
if failures:
print(
f"FAIL: {len(failures)} removed package(s) still referenced and not resolvable"
)
print(f"FAIL: {len(failures)} removed package(s) still referenced and not resolvable")
for name, _ in failures:
print(f" - {name}")
return 1

View file

@ -53,9 +53,7 @@ HIGH = "HIGH"
class Finding:
__slots__ = ("severity", "name", "version", "kind", "detail")
def __init__(
self, severity: str, name: str, version: str, kind: str, detail: str
) -> None:
def __init__(self, severity: str, name: str, version: str, kind: str, detail: str) -> None:
self.severity = severity
self.name = name
self.version = version
@ -206,9 +204,7 @@ def diff_new_install_scripts(base_lock: dict, head_lock: dict) -> list[Finding]:
continue # pre-existing install-script dep; not in scope
name = head[key]
# key is "name@version"; rsplit("@", 1) handles scoped names.
version = (
key[len(name) + 1 :] if key.startswith(name + "@") else "<unversioned>"
)
version = key[len(name) + 1 :] if key.startswith(name + "@") else "<unversioned>"
scripts = _fetch_registry_scripts(name, version)
if scripts:
detail = "; ".join(f"{h}={cmd!r}" for h, cmd in scripts.items())
@ -238,8 +234,7 @@ def diff_new_install_scripts(base_lock: dict, head_lock: dict) -> list[Finding]:
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(
description = (
"Diff two package-lock.json files and refuse any newly-"
"added install-script dep."
"Diff two package-lock.json files and refuse any newly-added install-script dep."
),
)
parser.add_argument(

View file

@ -1,5 +1,10 @@
#!/usr/bin/env python3
"""Ensure keyword arguments use spaces around '=', prune redundant pass statements."""
"""Ensure keyword arguments use spaces around '=', prune redundant pass statements,
drop the blank line after a short indented import block, merge adjacent same-line
string literals, normalize def-signature magic commas (pre-ruff) so a def with
>= 3 params and a default goes one-per-line while everything else stays
collapsible, and collapse a short multi-line assert onto one line (pre-ruff) by
stripping the magic trailing comma that holds it open."""
from __future__ import annotations
@ -123,9 +128,7 @@ def remove_redundant_passes(text: str) -> tuple[str, bool]:
lines = text.splitlines(keepends=True)
changed = False
for node in sorted(
redundant, key=lambda item: (item.lineno, item.col_offset), reverse=True
):
for node in sorted(redundant, key=lambda item: (item.lineno, item.col_offset), reverse=True):
start = node.lineno - 1
end = (node.end_lineno or node.lineno) - 1
if start >= len(lines):
@ -160,7 +163,470 @@ def remove_redundant_passes(text: str) -> tuple[str, bool]:
return "".join(result_lines), changed
def process_file(path: Path) -> bool:
def remove_blank_after_short_import(text: str) -> tuple[str, bool]:
"""Drop blank line(s) after an import block in a *small* nested suite.
Inside an indented suite of <= 3 statements (function/try/if/with/etc., never
module level), when a run of consecutive ``import`` / ``from ... import``
statements is directly followed -- across one or more blank lines and nothing
else -- by another statement in the same suite, remove those blank lines so
the import sits next to the code that uses it. A comment in the gap blocks the
rule. Removing blank lines never changes the AST, so this is always
semantics-preserving.
"""
try:
tree = ast.parse(text)
except SyntaxError:
return text, False
lines = text.splitlines(keepends=True)
import_types = (ast.Import, ast.ImportFrom)
drop: set[int] = set() # 1-based physical line numbers to delete
def suites_of(node: ast.AST) -> list[list[ast.stmt]]:
if isinstance(node, ast.Module):
return [] # module-level import spacing is left alone
out: list[list[ast.stmt]] = []
for attr in ("body", "orelse", "finalbody"):
val = getattr(node, attr, None)
if isinstance(val, list) and val and all(isinstance(s, ast.stmt) for s in val):
out.append(val)
return out
for node in ast.walk(tree):
for suite in suites_of(node):
if len(suite) > 3: # only small blocks
continue
i = 0
while i < len(suite):
if not isinstance(suite[i], import_types):
i += 1
continue
j = i
while j + 1 < len(suite) and isinstance(suite[j + 1], import_types):
j += 1
if j + 1 < len(suite): # an import block followed by another statement
last_imp, nxt = suite[j], suite[j + 1]
gap = range((last_imp.end_lineno or last_imp.lineno) + 1, nxt.lineno)
nums = [n for n in gap if 1 <= n <= len(lines)]
if nums and all(lines[n - 1].strip() == "" for n in nums):
drop.update(nums)
i = j + 1
if not drop:
return text, False
kept = [ln for idx, ln in enumerate(lines, start=1) if idx not in drop]
return "".join(kept), True
_STRING_TRIVIA = (tokenize.NL, tokenize.NEWLINE, tokenize.COMMENT, tokenize.INDENT, tokenize.DEDENT)
_DEF_MIN_PARAMS_FOR_MULTILINE = 3 # signatures with < this many params stay one line
def _def_specs_by_line(tree: ast.AST) -> dict[int, tuple[int, bool]]:
"""Map the line of each def keyword to (param count, has-any-default).
One def per line, so the line is a stable key. ``*`` / ``/`` markers are not
parameters and are not counted. A default exists if any positional default
is present or any keyword-only default is not ``None`` (a ``None`` entry in
``kw_defaults`` means a required keyword-only arg).
"""
out: dict[int, tuple[int, bool]] = {}
for node in ast.walk(tree):
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
a = node.args
count = (
len(a.posonlyargs)
+ len(a.args)
+ len(a.kwonlyargs)
+ (1 if a.vararg else 0)
+ (1 if a.kwarg else 0)
)
has_default = bool(a.defaults) or any(d is not None for d in a.kw_defaults)
out[node.lineno] = (count, has_default)
return out
def normalize_def_trailing_comma(text: str) -> tuple[str, bool]:
"""Force a def / async-def signature one-per-line iff it has >= 3 parameters
AND at least one default value; otherwise keep it collapsible.
Rationale: signatures with defaults read better one parameter per line, but
only once they are non-trivial (< 3 params always stay on one line). A
signature with >= 3 params and a default gets a magic trailing comma added
(ruff then wraps it one-per-line regardless of length); every other
signature has its trailing comma stripped so ruff collapses it onto one line
when it fits (and wraps a genuinely long one by length alone).
Function-definition parameter lists only, never call sites or collection
literals. Parameter counts and defaults come from the AST. Run BEFORE ruff
format. Adding or removing a def trailing comma never changes the AST, which
is re-checked before returning.
"""
try:
tree = ast.parse(text)
toks = list(tokenize.generate_tokens(io.StringIO(text).readline))
except (tokenize.TokenError, IndentationError, SyntaxError):
return text, False
specs = _def_specs_by_line(tree)
n = len(toks)
edits: list[tuple[int, int, str]] = [] # (row, col, "del" | "ins")
i = 0
while i < n:
t = toks[i]
if t.type == tokenize.NAME and t.string == "def" and t.start[0] in specs:
cnt, has_default = specs[t.start[0]]
force_multiline = cnt >= _DEF_MIN_PARAMS_FOR_MULTILINE and has_default
j = i + 1
while j < n and not (toks[j].type == tokenize.OP and toks[j].string == "("):
if toks[j].type == tokenize.NEWLINE:
break
j += 1
if j < n and toks[j].type == tokenize.OP and toks[j].string == "(":
depth = 0
k = j
while k < n:
tk = toks[k]
if tk.type == tokenize.OP and tk.string == "(":
depth += 1
elif tk.type == tokenize.OP and tk.string == ")":
depth -= 1
if depth == 0:
m = k - 1
while m > j and toks[m].type in _STRING_TRIVIA:
m -= 1
last = toks[m]
has_comma = last.type == tokenize.OP and last.string == ","
empty = m == j # nothing between ( and )
if force_multiline and not has_comma and not empty:
edits.append((last.end[0], last.end[1], "ins"))
elif not force_multiline and has_comma:
edits.append((last.start[0], last.start[1], "del"))
break
k += 1
i = k + 1
continue
i += 1
if not edits:
return text, False
lines = text.splitlines(keepends=True)
for row, col, kind in sorted(edits, reverse=True):
ln = lines[row - 1]
if kind == "del":
if col < len(ln) and ln[col] == ",":
lines[row - 1] = ln[:col] + ln[col + 1 :]
else: # ins
lines[row - 1] = ln[:col] + "," + ln[col:]
out = "".join(lines)
try:
if ast.dump(ast.parse(out)) != ast.dump(ast.parse(text)):
return text, False
except SyntaxError:
return text, False
return out, True
def _split_string_token(s: str) -> tuple[str, str, str] | None:
"""Split a string literal's source into (prefix, quote, body).
``prefix`` is the letters before the opening quote (``r``/``f``/``b``/``u``
in any case/order), ``quote`` is the opening delimiter (``'``, ``"``,
``'''`` or ``\"\"\"``) and ``body`` is everything between the delimiters.
Returns ``None`` if ``s`` is not a recognizable string literal.
"""
i = 0
while i < len(s) and s[i] not in ("'", '"'):
i += 1
if i >= len(s):
return None
prefix, rest = s[:i], s[i:]
for q in ('"""', "'''", '"', "'"):
if rest.startswith(q) and rest.endswith(q) and len(rest) >= 2 * len(q):
return prefix, q, rest[len(q) : len(rest) - len(q)]
return None
# A "piece" is one string literal in source: a plain STRING token, or a whole
# f-string spanning FSTRING_START..FSTRING_END. (kind, (row, col0), (row, col1), raw)
def _string_pieces(
toks: list[tokenize.TokenInfo], lines: list[str]
) -> list[tuple[str, tuple[int, int], tuple[int, int], str | None]]:
pieces: list[tuple[str, tuple[int, int], tuple[int, int], str | None]] = []
n = len(toks)
def raw_of(start: tuple[int, int], end: tuple[int, int]) -> str | None:
if start[0] != end[0]: # only single-physical-line pieces are mergeable
return None
return lines[start[0] - 1][start[1] : end[1]]
i = 0
while i < n:
t = toks[i]
if t.type == tokenize.STRING:
pieces.append(("str", t.start, t.end, raw_of(t.start, t.end)))
i += 1
elif t.type == tokenize.FSTRING_START:
depth = 0
j = i
while j < n: # walk to the matching FSTRING_END (f-strings can nest)
if toks[j].type == tokenize.FSTRING_START:
depth += 1
elif toks[j].type == tokenize.FSTRING_END:
depth -= 1
if depth == 0:
break
j += 1
end = toks[j].end
pieces.append(("f", t.start, end, raw_of(t.start, end)))
i = j + 1
else:
pieces.append(("other", t.start, t.end, None))
i += 1
return pieces
def _merge_string_run(pieces: list[tuple[str, str]]) -> str | None:
"""Merge a run of adjacent string pieces into one literal's source text.
``pieces`` is a list of ``(kind, raw_source)`` where kind is ``"str"`` or
``"f"``. Rules: bytes are left side-by-side (return ``None``); a run with no
f-string merges plain/raw/unicode pieces sharing one prefix+quote by simple
body concatenation; a run mixing an f-string with at least one plain string
(and no bytes, no raw) folds into a single f-string -- f pieces keep their
bodies verbatim and plain pieces have their braces escaped (``{`` -> ``{{``).
Runs of only f-strings are left side-by-side. The caller re-checks the file
AST and drops the change if it differs, so any subtle case (e.g. ``\\N{...}``)
that this would mis-handle is caught and skipped.
"""
parsed = []
for kind, raw in pieces:
pqb = _split_string_token(raw)
if pqb is None:
return None
prefix, quote, body = pqb
if "b" in prefix.lower():
return None # bytes: leave side-by-side
parsed.append((kind, prefix, quote, body))
if len({p[2] for p in parsed}) != 1:
return None # mixed quote style: not a safe textual merge
quote = parsed[0][2]
if not any(p[0] == "f" for p in parsed):
# No f-string: merge plain/raw/unicode sharing one prefix by concatenation.
if len({p[1].lower() for p in parsed}) != 1:
return None
return f"{parsed[0][1]}{quote}{''.join(p[3] for p in parsed)}{quote}"
# f-string fold only when a plain string is glued onto an f-string; a run of
# only f-strings is left side-by-side (folding long ones would force ruff to
# re-wrap the surrounding statement).
if all(p[0] == "f" for p in parsed):
return None
# raw mixed with f is too subtle (backslash + brace escaping) -> skip.
if any("r" in p[1].lower() for p in parsed):
return None
body = "".join(
b if kind == "f" else b.replace("{", "{{").replace("}", "}}")
for kind, _pfx, _q, b in parsed
)
return f"f{quote}{body}{quote}"
_LINE_LENGTH = 100 # ruff line-length; an f-fold must not push a statement past it
def _enclosing_stmt(tree: ast.AST, row: int) -> ast.stmt | None:
"""The innermost statement whose physical-line span contains ``row``."""
best: tuple[ast.stmt, int] | None = None
for node in ast.walk(tree):
if isinstance(node, ast.stmt):
lo = node.lineno
hi = node.end_lineno or lo
if lo <= row <= hi and (best is None or hi - lo < best[1]):
best = (node, hi - lo)
return best[0] if best else None
def _fold_collapses(
tree: ast.AST, lines: list[str], row: int, c0: int, c1: int, merged: str
) -> bool:
"""Whether an f-string fold at ``row[c0:c1]`` -> ``merged`` is safe to apply.
Only ``assert`` statements wrap awkwardly when a message is folded: ruff
parenthesizes the *condition* once ``assert cond, msg`` no longer fits on one
line. For every other construct (call argument, ``raise``, assignment, ...) a
folded long message wraps acceptably, so the fold is always allowed. For an
``assert`` the fold is allowed only when the statement is already one physical
line, or its estimated one-line length after folding fits the line length;
otherwise the message is left side-by-side.
"""
stmt = _enclosing_stmt(tree, row)
if not isinstance(stmt, ast.Assert):
return True
lo, hi = stmt.lineno, stmt.end_lineno or stmt.lineno
if lo == hi:
return True
seg = []
for k in range(lo, hi + 1):
ln = lines[k - 1].rstrip("\n")
if k == row:
ln = ln[:c0] + merged + ln[c1:]
seg.append(ln)
indent = len(seg[0]) - len(seg[0].lstrip())
# Conservative over-estimate: join continuation lines with a single space
# (ruff joins bracketed wraps with none), so borderline cases skip the fold.
joined = " ".join(s.strip() for s in seg)
return indent + len(joined) <= _LINE_LENGTH
def merge_adjacent_string_literals(text: str) -> tuple[str, bool]:
"""Merge a run of adjacent string literals on ONE physical line into a single
literal (the ``"a" "b"`` form ruff emits when it collapses an implicit
concatenation). Plain/raw/unicode runs merge by concatenation; a run mixing
an f-string with a plain string folds into one f-string (plain parts' braces
escaped) -- but only when the statement still fits on one line, so a long
message is left side-by-side rather than forcing the statement to re-wrap.
Runs of only f-strings, and bytes, are left side-by-side. The whole file's AST
is re-checked and the change is dropped if it would differ, so the transform
can never change meaning.
"""
try:
toks = list(tokenize.generate_tokens(io.StringIO(text).readline))
tree = ast.parse(text)
except (tokenize.TokenError, IndentationError, SyntaxError):
return text, False
lines = text.splitlines(keepends=True)
pieces = _string_pieces(toks, lines)
# Group consecutive mergeable pieces (str/f, single line, same physical line).
runs: list[list[tuple[str, tuple[int, int], tuple[int, int], str]]] = []
cur: list[tuple[str, tuple[int, int], tuple[int, int], str]] = []
for kind, start, end, raw in pieces:
if kind in ("str", "f") and raw is not None:
if cur and cur[-1][2][0] != start[0]:
if len(cur) >= 2:
runs.append(cur)
cur = []
cur.append((kind, start, end, raw))
else:
if len(cur) >= 2:
runs.append(cur)
cur = []
if len(cur) >= 2:
runs.append(cur)
if not runs:
return text, False
edits = []
for run in runs:
merged = _merge_string_run([(kind, raw) for kind, _s, _e, raw in run])
if merged is None:
continue
row, c0, c1 = run[0][1][0], run[0][1][1], run[-1][2][1]
# An f-string fold must not push its statement onto extra lines; a plain
# concatenation always collapses cleanly so it skips this check.
if any(kind == "f" for kind, _s, _e, _r in run) and not _fold_collapses(
tree, lines, row, c0, c1, merged
):
continue
edits.append((row, c0, c1, merged))
if not edits:
return text, False
for row, c0, c1, repl in sorted(edits, key=lambda e: (e[0], e[1]), reverse=True):
ln = lines[row - 1]
lines[row - 1] = ln[:c0] + repl + ln[c1:]
out = "".join(lines)
try:
if ast.dump(ast.parse(text)) != ast.dump(ast.parse(out)):
return text, False
except SyntaxError:
return text, False
return out, True
def collapse_short_asserts(text: str) -> tuple[str, bool]:
"""Collapse a multi-line ``assert`` onto one line when it would fit.
An ``assert`` is often kept multi-line only by a magic trailing comma inside
a collection / call / tuple-message (``assert x == {\"a\": 1,}`` written
across lines). When the whole statement's estimated one-line length fits the
line length, strip those trailing commas (the comma before a ``)`` / ``]`` /
``}``) so ruff joins it back onto one line on the following format pass.
Run BEFORE ruff format. Skips any assert that contains a comment (a comment
forces ruff to keep it multi-line, which would oscillate). Stripping a
trailing comma is non-semantic except for a one-element tuple ``(x,)``; the
file AST is re-checked and any assert whose strip would change it is left
alone.
"""
try:
tree = ast.parse(text)
toks = list(tokenize.generate_tokens(io.StringIO(text).readline))
except (tokenize.TokenError, IndentationError, SyntaxError):
return text, False
lines = text.splitlines(keepends=True)
multiline = [
(n.lineno, n.end_lineno)
for n in ast.walk(tree)
if isinstance(n, ast.Assert) and (n.end_lineno or n.lineno) > n.lineno
]
if not multiline:
return text, False
comment_rows = {t.start[0] for t in toks if t.type == tokenize.COMMENT}
targets = [] # (lo, hi) spans whose one-line form fits and have no comment
for lo, hi in multiline:
if any(lo <= r <= hi for r in comment_rows):
continue # a comment would keep ruff multi-line -> never collapses
seg = [lines[k].rstrip("\n") for k in range(lo - 1, hi)]
indent = len(seg[0]) - len(seg[0].lstrip())
# Over-estimate (join with a space; keep the comma) so a "fits" verdict
# is always at least as long as ruff's real one-line output -> no fight.
if indent + len(" ".join(s.strip() for s in seg)) <= _LINE_LENGTH:
targets.append((lo, hi))
if not targets:
return text, False
# Trailing commas (a ',' whose next significant token is a closer), grouped
# by the target assert they belong to.
sig = [t for t in toks if t.type not in _STRING_TRIVIA]
by_target: dict[tuple[int, int], list[tuple[int, int]]] = defaultdict(list)
for i, t in enumerate(sig):
if t.type == tokenize.OP and t.string == ",":
nxt = sig[i + 1] if i + 1 < len(sig) else None
if nxt and nxt.type == tokenize.OP and nxt.string in (")", "]", "}"):
for lo, hi in targets:
if lo <= t.start[0] <= hi:
by_target[(lo, hi)].append(t.start)
break
if not by_target:
return text, False
base_dump = ast.dump(tree)
working = lines[:]
changed = False
for positions in by_target.values(): # apply per assert; skip any that break AST
trial = working[:]
for row, col in sorted(positions, reverse=True):
ln = trial[row - 1]
if col < len(ln) and ln[col] == ",":
trial[row - 1] = ln[:col] + ln[col + 1 :]
try:
if ast.dump(ast.parse("".join(trial))) == base_dump:
working, changed = trial, True
except SyntaxError:
pass
return ("".join(working), True) if changed else (text, False)
def process_file(path: Path, pre: bool = False) -> bool:
try:
with tokenize.open(path) as handle:
original = handle.read()
@ -169,9 +635,23 @@ def process_file(path: Path) -> bool:
print(f"Failed to read {path}: {exc}", file=sys.stderr)
return False
if pre:
# Pre-ruff: normalize def-signature magic commas (>=3 params + a default
# add so ruff forces one-per-line; everything else strips so ruff
# collapses), and strip the magic trailing comma from a short multi-line
# assert so ruff joins it onto one line. Everything else runs post-ruff.
updated, normalized = normalize_def_trailing_comma(original)
updated, collapsed = collapse_short_asserts(updated)
if normalized or collapsed:
_atomic_write_text(path, updated, encoding)
return True
return False
updated, changed = enforce_spacing(original)
updated, blanked = remove_blank_after_short_import(updated)
updated, merged = merge_adjacent_string_literals(updated)
updated, removed = remove_redundant_passes(updated)
if changed or removed:
if changed or blanked or merged or removed:
_atomic_write_text(path, updated, encoding)
return True
return False
@ -180,6 +660,11 @@ def process_file(path: Path) -> bool:
def main(argv: list[str]) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("files", nargs="+", help="Python files to fix")
parser.add_argument(
"--pre",
action="store_true",
help="pre-ruff pass: normalize def-signature commas + collapse short multi-line asserts",
)
args = parser.parse_args(argv)
touched: list[Path] = []
@ -192,7 +677,7 @@ def main(argv: list[str]) -> int:
continue
if not path.exists() or path.is_dir():
continue
if process_file(path):
if process_file(path, pre=args.pre):
touched.append(path)
if touched:

View file

@ -47,9 +47,7 @@ from pathlib import Path
try:
import yaml
except ImportError:
print(
"ERROR: PyYAML is required. Install with 'pip install pyyaml'", file = sys.stderr
)
print("ERROR: PyYAML is required. Install with 'pip install pyyaml'", file = sys.stderr)
sys.exit(2)
REPO_ROOT = Path(__file__).resolve().parents[1]
@ -153,9 +151,7 @@ def main() -> int:
)
if findings:
print(
"Workflow trigger lint failed with the following issues:", file = sys.stderr
)
print("Workflow trigger lint failed with the following issues:", file = sys.stderr)
for f in findings:
print(f" - {f}", file = sys.stderr)
return 1

View file

@ -541,9 +541,7 @@ def audit_npm_lockfile(path: Path) -> list[Finding]:
path = str(path),
package = key,
kind = "blocked-known-malicious",
detail = (
f"{pkg_name}@{version} is on the " "BLOCKED_NPM_VERSIONS list"
),
detail = (f"{pkg_name}@{version} is on the BLOCKED_NPM_VERSIONS list"),
)
)
@ -765,10 +763,7 @@ def main(argv: list[str] | None = None) -> int:
"--cargo-lockfile",
action = "append",
default = None,
help = (
"Path to a Cargo.lock (repeatable). "
"Default: studio/src-tauri/Cargo.lock."
),
help = ("Path to a Cargo.lock (repeatable). Default: studio/src-tauri/Cargo.lock."),
)
parser.add_argument(
"--strict",

View file

@ -186,9 +186,7 @@ def convert_cell_to_python(source: str, *, allow_shell: bool = True) -> str:
cmd_lines.append(lines[i].strip())
full_cmd = "\n".join(cmd_lines)
result.extend(
_emit_shell_command(indent, full_cmd, allow_shell = allow_shell)
)
result.extend(_emit_shell_command(indent, full_cmd, allow_shell = allow_shell))
# %cd path -> os.chdir(path)
elif stripped.startswith("%cd "):
@ -313,9 +311,7 @@ def convert_notebook_to_script(
# Generate output filename
output_filename = filename.replace(".ipynb", ".py")
# Clean up filename
output_filename = (
output_filename.replace("(", "").replace(")", "").replace("-", "_")
)
output_filename = output_filename.replace("(", "").replace(")", "").replace("-", "_")
# Add output directory if specified
if output_dir:
@ -337,9 +333,7 @@ def convert_notebook_to_script(
def main():
import argparse
class Formatter(
argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter
):
class Formatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter):
pass
parser = argparse.ArgumentParser(
@ -353,12 +347,8 @@ Examples:
python notebook_to_python.py https://github.com/unslothai/notebooks/blob/main/nb/Oute_TTS_(1B).ipynb
""",
)
parser.add_argument(
"notebooks", nargs = "+", help = "Notebook files or URLs to convert."
)
parser.add_argument(
"-o", "--output", dest = "output_dir", default = ".", help = "Output directory."
)
parser.add_argument("notebooks", nargs = "+", help = "Notebook files or URLs to convert.")
parser.add_argument("-o", "--output", dest = "output_dir", default = ".", help = "Output directory.")
# Default True for backwards compatibility: existing Colab notebooks
# routinely use pipes / redirection / interpolation in `!cmd` lines
# and the converted script needs to keep working. Operators who

View file

@ -92,9 +92,7 @@ COLAB_ORACLE_FILES: dict[str, str] = {
"apt-list-gpu.txt": "colab_apt_list.gpu.txt",
"os-info-gpu.txt": "colab_os_info.gpu.txt",
}
COLAB_ORACLE_BASE_URL = (
"https://raw.githubusercontent.com/googlecolab/backend-info/main/"
)
COLAB_ORACLE_BASE_URL = "https://raw.githubusercontent.com/googlecolab/backend-info/main/"
# ----- Compat tables. PRs add rows as new releases land. ----- #
@ -195,9 +193,7 @@ def install_cells(nb: dict[str, Any]) -> list[tuple[int, str]]:
if first and first[0].strip().startswith("%%capture"):
out.append((i, src))
continue
if re.search(
r"^[ \t]*!\s*(uv\s+)?pip\s+(install|uninstall)\b", src, re.MULTILINE
):
if re.search(r"^[ \t]*!\s*(uv\s+)?pip\s+(install|uninstall)\b", src, re.MULTILINE):
out.append((i, src))
return out
@ -331,9 +327,7 @@ def parse_pip_line(line: str, line_no: int = 0) -> PipInvocation | None:
if t in ("install", "uninstall"):
continue
packages.append(t)
return PipInvocation(
tool = tool, flags = flags, packages = packages, raw = line, line_no = line_no
)
return PipInvocation(tool = tool, flags = flags, packages = packages, raw = line, line_no = line_no)
def _glue_line_continuations(text: str) -> list[tuple[int, str]]:
@ -418,9 +412,7 @@ def pypi_metadata(name: str, version: str) -> dict[str, Any] | None:
return data
def transitive_constraint(
name: str, version: str, target: str
) -> tuple[str | None, list[str]]:
def transitive_constraint(name: str, version: str, target: str) -> tuple[str | None, list[str]]:
"""Return (raw_specifier_string_or_None, list_of_(op,version) tuples)
for the constraint that `name==version` places on `target`.
"""
@ -501,10 +493,7 @@ def resolved_set(install_cell: str, colab: dict[str, str]) -> dict[str, str]:
out[sp.name] = ver
pinned.add(sp.name)
elif op == "<=" and sp.name not in pinned:
if (
sp.name not in upper_bounds
or cmp_versions(ver, upper_bounds[sp.name]) < 0
):
if sp.name not in upper_bounds or cmp_versions(ver, upper_bounds[sp.name]) < 0:
upper_bounds[sp.name] = ver
# Apply upper bounds where Colab's preinstall violates them.
for name, ub in upper_bounds.items():
@ -519,9 +508,7 @@ def resolved_set(install_cell: str, colab: dict[str, str]) -> dict[str, str]:
# ----- Rules ----- #
def rule_inst_001_git_plus(
install_cell: str, file: str, cell_idx: int
) -> list[Finding]:
def rule_inst_001_git_plus(install_cell: str, file: str, cell_idx: int) -> list[Finding]:
findings: list[Finding] = []
for inv in iter_pip_invocations(install_cell):
if any("git+" in p for p in inv.packages) or "git+" in inv.raw:
@ -714,9 +701,7 @@ def rule_inst_005_transformers_tokenizers(
_RE_DOUBLE_BANG = re.compile(r"^[ \t]*!{2,}\s*pip\b", re.MULTILINE)
def rule_inst_006_double_bang(
install_cell: str, file: str, cell_idx: int
) -> list[Finding]:
def rule_inst_006_double_bang(install_cell: str, file: str, cell_idx: int) -> list[Finding]:
findings: list[Finding] = []
for m in _RE_DOUBLE_BANG.finditer(install_cell):
line_no = install_cell.count("\n", 0, m.start()) + 1
@ -813,9 +798,7 @@ POLICY_CLAUSES_DEFAULT = [
]
def extract_policy_clauses(
update_script: pathlib.Path,
) -> list[tuple[str, re.Pattern[str], Any]]:
def extract_policy_clauses(update_script: pathlib.Path) -> list[tuple[str, re.Pattern[str], Any]]:
"""Best-effort: scan update_all_notebooks.py for canonical phrases used by
multiple templates. Falls back to POLICY_CLAUSES_DEFAULT.
@ -879,11 +862,7 @@ def cmd_drift(args: argparse.Namespace) -> int:
print(f"FAIL: {update_script} not found", file = sys.stderr)
return 2
# Stash any pre-existing dirty state, run the updater, diff, restore.
head = (
subprocess.check_output(["git", "rev-parse", "HEAD"], cwd = nbdir)
.decode()
.strip()
)
head = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd = nbdir).decode().strip()
subprocess.run(
["git", "-C", str(nbdir), "stash", "--include-untracked"],
check = False,
@ -990,9 +969,7 @@ def cmd_convert(args: argparse.Namespace) -> int:
hint = proc.stderr[-200:].strip(),
)
)
print(
f"converted {len(notebooks) - len(failed)}/{len(notebooks)} notebooks to {out}"
)
print(f"converted {len(notebooks) - len(failed)}/{len(notebooks)} notebooks to {out}")
_emit(failed)
return 0 if not failed else 1
@ -1002,11 +979,7 @@ def cmd_convert(args: argparse.Namespace) -> int:
def cmd_lint(args: argparse.Namespace) -> int:
nbdir = pathlib.Path(args.notebooks_dir).resolve()
colab_path = (
pathlib.Path(args.colab_pin).resolve()
if args.colab_pin
else COLAB_FALLBACK_FILE
)
colab_path = pathlib.Path(args.colab_pin).resolve() if args.colab_pin else COLAB_FALLBACK_FILE
colab = parse_pip_freeze(colab_path)
if not colab:
print(
@ -1049,13 +1022,9 @@ def cmd_lint(args: argparse.Namespace) -> int:
first_cell = cells[0][0] if cells else None
findings += rule_inst_003_peft_torchao(merged, oracle, rel, first_cell)
findings += rule_inst_004_torchcodec_torch(merged, oracle, rel, first_cell)
findings += rule_inst_005_transformers_tokenizers(
merged, oracle, rel, first_cell
)
findings += rule_inst_005_transformers_tokenizers(merged, oracle, rel, first_cell)
if not args.no_pypi:
findings += rule_inst_002_no_deps_transitive(
merged, oracle, rel, first_cell
)
findings += rule_inst_002_no_deps_transitive(merged, oracle, rel, first_cell)
findings += scan_user_cells(nb, rel)
_emit(findings)
return 0 if not any(f.severity == "error" for f in findings) else 1
@ -1232,9 +1201,7 @@ def cmd_colab_diff(args: argparse.Namespace) -> int:
print(f"::warning::colab-diff: could not fetch {url}: {e}")
continue
if not snap_path.exists():
print(
f"::warning::colab-diff: no committed snapshot at {snap_path}; skipping"
)
print(f"::warning::colab-diff: no committed snapshot at {snap_path}; skipping")
continue
snapshot_text = snap_path.read_text(encoding = "utf-8", errors = "replace")
parser = _COLAB_ORACLE_PARSERS[upstream_name]

View file

@ -1,5 +1,7 @@
#!/usr/bin/env python3
"""Run `ruff format` followed by kwarg spacing enforcement."""
"""Run a pre-pass (normalize def-signature magic commas + collapse short
multi-line asserts), then `ruff format`, then the kwarg-spacing / import /
string-merge post-pass."""
from __future__ import annotations
@ -15,12 +17,22 @@ def main(argv: list[str]) -> int:
if not files:
return 0
spacing_script = HERE / "enforce_kwargs_spacing.py"
# Pre-ruff: normalize def-signature trailing commas (>=3 params with a
# default -> one-per-line; everything else collapsible) and strip the magic
# comma from a short multi-line assert, so ruff wraps signatures accordingly
# and joins the assert back onto one line.
pre_cmd = [sys.executable, str(spacing_script), "--pre", *files]
pre_proc = subprocess.run(pre_cmd)
if pre_proc.returncode != 0:
return pre_proc.returncode
ruff_cmd = [sys.executable, "-m", "ruff", "format", *files]
ruff_proc = subprocess.run(ruff_cmd)
if ruff_proc.returncode != 0:
return ruff_proc.returncode
spacing_script = HERE / "enforce_kwargs_spacing.py"
spacing_cmd = [sys.executable, str(spacing_script), *files]
spacing_proc = subprocess.run(spacing_cmd)
return spacing_proc.returncode

View file

@ -823,8 +823,7 @@ def download_tarball(
written += len(chunk)
if written > max_bytes:
return dest, (
f"download exceeded cap {max_bytes} bytes "
f"after {written} bytes"
f"download exceeded cap {max_bytes} bytes " f"after {written} bytes"
)
h.update(chunk)
out.write(chunk)
@ -926,11 +925,7 @@ def safe_extract(
# get the generous binary cap. We bound BOTH cases.
header = src.read(16)
is_binary = _looks_binary(name, header)
file_cap = (
HARD_MAX_BINARY_FILE_BYTES
if is_binary
else HARD_MAX_TEXT_FILE_BYTES
)
file_cap = HARD_MAX_BINARY_FILE_BYTES if is_binary else HARD_MAX_TEXT_FILE_BYTES
if declared > file_cap:
return (
f"member {name!r} declared size {declared} > "
@ -963,7 +958,11 @@ def safe_extract(
# ─────────────────────────────────────────────────────────────────────
def _evidence(text: str, pat: re.Pattern, max_chars: int = 200) -> str:
def _evidence(
text: str,
pat: re.Pattern,
max_chars: int = 200,
) -> str:
m = pat.search(text)
if not m:
return ""
@ -978,11 +977,7 @@ def _evidence(text: str, pat: re.Pattern, max_chars: int = 200) -> str:
LIFECYCLE_HOOKS = ("preinstall", "install", "postinstall", "prepare")
def scan_package_json(
pkg: PackageEntry,
rel: str,
text: str,
) -> list[Finding]:
def scan_package_json(pkg: PackageEntry, rel: str, text: str) -> list[Finding]:
findings: list[Finding] = []
try:
meta = json.loads(text)
@ -1056,9 +1051,7 @@ def scan_package_json(
if isinstance(opt, dict):
for k, v in opt.items():
if isinstance(v, str) and (
v.startswith("github:")
or v.startswith("git+")
or v.startswith("git://")
v.startswith("github:") or v.startswith("git+") or v.startswith("git://")
):
findings.append(
Finding(
@ -1117,11 +1110,7 @@ def _host_in_outbound_context(text: str, host: str) -> bool:
return False
def scan_text_blob(
pkg: PackageEntry,
rel: str,
text: str,
) -> list[Finding]:
def scan_text_blob(pkg: PackageEntry, rel: str, text: str) -> list[Finding]:
findings: list[Finding] = []
# IOC substrings (literal, case-sensitive).
@ -1190,10 +1179,7 @@ def scan_text_blob(
filename = rel,
pattern = "js-fetch-eval",
evidence = _evidence(text, _JS_FETCH_EVAL),
detail = (
"Function/eval against base64-decoded payload "
"(obfuscated dropper shape)"
),
detail = ("Function/eval against base64-decoded payload (obfuscated dropper shape)"),
)
)
if _JS_ENV_TOKEN.search(text):
@ -1247,10 +1233,7 @@ _TEXT_SUFFIXES = (
)
def scan_extracted_tree(
pkg: PackageEntry,
root: Path,
) -> list[Finding]:
def scan_extracted_tree(pkg: PackageEntry, root: Path) -> list[Finding]:
findings: list[Finding] = []
for path in sorted(root.rglob("*")):
if not path.is_file():
@ -1304,10 +1287,7 @@ def scan_extracted_tree(
# ─────────────────────────────────────────────────────────────────────
def scan_one(
pkg: PackageEntry,
workspace: Path,
) -> tuple[list[Finding], str | None]:
def scan_one(pkg: PackageEntry, workspace: Path) -> tuple[list[Finding], str | None]:
"""Download + extract + scan a single package. Cleans up its dir.
Returns (findings, error). `error` is non-None only on hard
@ -1444,8 +1424,7 @@ def main(argv: list[str] | None = None) -> int:
if hard_errors or blocking:
if blocking:
print(
f"\n[scan-npm] FAIL: {len(blocking)} finding(s) "
f"at or above {threshold}",
f"\n[scan-npm] FAIL: {len(blocking)} finding(s) " f"at or above {threshold}",
file = sys.stderr,
)
return 1

View file

@ -86,8 +86,7 @@ RE_SUBPROCESS = re.compile(
# Encoding / obfuscation
RE_BASE64 = re.compile(
r"\bbase64\s*\.\s*(b64decode|decodebytes|b32decode|b16decode)\b"
r"|\bcodecs\s*\.\s*decode\b",
r"\bbase64\s*\.\s*(b64decode|decodebytes|b32decode|b16decode)\b|\bcodecs\s*\.\s*decode\b",
)
# exec / eval
@ -299,9 +298,7 @@ RE_CRYPTO_THEFT = re.compile(
RE_PTH_IMPORT = re.compile(r"^\s*import\s+", re.MULTILINE)
# openssl CLI invocations via subprocess (encrypted exfiltration)
RE_OPENSSL_CLI = re.compile(
r"\bopenssl\s+(enc|rand|rsautl|pkeyutl|genrsa|dgst|s_client)\b"
)
RE_OPENSSL_CLI = re.compile(r"\bopenssl\s+(enc|rand|rsautl|pkeyutl|genrsa|dgst|s_client)\b")
# Write to /tmp then execute (staged dropper)
RE_TEMP_EXEC = re.compile(
@ -962,7 +959,11 @@ def check_py_file(content: str, filename: str, package: str) -> list[Finding]:
return findings
def _extract_evidence(content: str, pattern: re.Pattern, max_matches: int = 3) -> str:
def _extract_evidence(
content: str,
pattern: re.Pattern,
max_matches: int = 3,
) -> str:
"""Pull matching lines as evidence snippets."""
lines = content.splitlines()
matches = []
@ -1266,15 +1267,13 @@ def iter_archive_files(archive_path: str):
# have historically dereferenced them on extract.
if member.issym() or member.islnk():
print(
f" [WARN] {path.name}: refused link member "
f"{member.name!r}",
f" [WARN] {path.name}: refused link member " f"{member.name!r}",
file = sys.stderr,
)
continue
if member.isdev() or member.isfifo():
print(
f" [WARN] {path.name}: refused special member "
f"{member.name!r}",
f" [WARN] {path.name}: refused special member " f"{member.name!r}",
file = sys.stderr,
)
continue
@ -1379,9 +1378,7 @@ def scan_archive(archive_path: str, package: str) -> list[Finding]:
_RE_PYPI_SPEC_VERSION = re.compile(r"==\s*([A-Za-z0-9_.\-+!]+)")
def _check_blocked_pypi_versions(
specs: list[str],
) -> tuple[list[str], list[Finding]]:
def _check_blocked_pypi_versions(specs: list[str]) -> tuple[list[str], list[Finding]]:
"""Filter ``specs`` against ``BLOCKED_PYPI_VERSIONS``.
Returns ``(safe_specs, findings)``. Each blocked spec emits a CRITICAL
@ -1502,9 +1499,7 @@ def download_packages(
env = env,
)
if proc.returncode != 0:
msg = (
f"pip download (with deps) failed: " f"{proc.stderr.strip()[:500]}"
)
msg = f"pip download (with deps) failed: " f"{proc.stderr.strip()[:500]}"
print(f" [ERROR] {msg}", file = sys.stderr)
download_errors.append(msg)
except subprocess.TimeoutExpired:
@ -1547,10 +1542,7 @@ def download_packages(
env = env,
)
if proc.returncode != 0:
msg = (
f"pip download failed for {spec}: "
f"{proc.stderr.strip()[:500]}"
)
msg = f"pip download failed for {spec}: " f"{proc.stderr.strip()[:500]}"
print(f" [ERROR] {msg}", file = sys.stderr)
download_errors.append(msg)
continue
@ -1579,9 +1571,7 @@ def _extract_pkg_name(spec: str) -> str:
"""Extract the package name from a pip spec string."""
m = _RE_NAME.match(spec)
return (
m.group(1)
if m
else spec.split("==")[0].split(">=")[0].split("<=")[0].split("[")[0].strip()
m.group(1) if m else spec.split("==")[0].split(">=")[0].split("<=")[0].split("[")[0].strip()
)
@ -1917,11 +1907,7 @@ def update_req_file(filepath: str, updates: dict[int, str]) -> None:
raise
def _run_fix(
critical_pkgs: set[str],
entries: list[dict],
max_search: int,
) -> None:
def _run_fix(critical_pkgs: set[str], entries: list[dict], max_search: int) -> None:
"""Run the --fix flow: find safe versions, update requirements files."""
# Map package names to their entries for source tracking
pkg_entries: dict[str, list[dict]] = {}
@ -1941,9 +1927,7 @@ def _run_fix(
if git_entries:
for e in git_entries:
src = e["source_file"] or "CLI"
print(
f" [SKIP] {pkg_name} is a git URL dep in {src}, cannot auto-update"
)
print(f" [SKIP] {pkg_name} is a git URL dep in {src}, cannot auto-update")
changes_summary.append(f" SKIP {pkg_name} (git URL)")
continue
@ -1967,9 +1951,7 @@ def _run_fix(
shutil.rmtree(dl_dir, ignore_errors = True)
if not current_ver:
print(
f" [WARN] Cannot determine current version of {pkg_name}, skipping fix"
)
print(f" [WARN] Cannot determine current version of {pkg_name}, skipping fix")
changes_summary.append(f" SKIP {pkg_name} (version unknown)")
continue
@ -1986,9 +1968,7 @@ def _run_fix(
continue
print(f" [OK] {pkg_name}: {current_ver} -> {safe_ver}")
changes_summary.append(
f" FIX {pkg_name}=={current_ver} -> {pkg_name}=={safe_ver}"
)
changes_summary.append(f" FIX {pkg_name}=={current_ver} -> {pkg_name}=={safe_ver}")
# Update all occurrences in requirements files
file_updates: dict[str, dict[int, str]] = {}
@ -2038,9 +2018,7 @@ def _find_requirements_files(root: str) -> list[str]:
dirnames[:] = [
d
for d in dirnames
if not d.startswith(".")
and d not in skip_dirs
and not d.endswith(".egg-info")
if not d.startswith(".") and d not in skip_dirs and not d.endswith(".egg-info")
]
dirname = os.path.basename(dirpath)
for fname in sorted(filenames):
@ -2115,9 +2093,7 @@ def main() -> int:
print(f" {f}")
req_files.extend(found)
else:
print(
f" [WARN] No requirements files found in {scan_dir}/", file = sys.stderr
)
print(f" [WARN] No requirements files found in {scan_dir}/", file = sys.stderr)
# Build unified entry list: list of dicts with source tracking
entries: list[dict] = []
@ -2211,7 +2187,7 @@ def main() -> int:
for err in download_errors:
print(f" [ERROR] {err}", file = sys.stderr)
print(
" Refusing to report 'all clean' on a partial scan; " "exiting 2.",
" Refusing to report 'all clean' on a partial scan; exiting 2.",
file = sys.stderr,
)
return 2

View file

@ -17,7 +17,11 @@ import zipfile
from pathlib import Path
def _atomic_write_text(path: Path, data: str, encoding: str = "utf-8") -> None:
def _atomic_write_text(
path: Path,
data: str,
encoding: str = "utf-8",
) -> None:
"""Atomic version of ``Path.write_text``.
A crash or signal mid-write leaves the prior file intact; the
@ -41,9 +45,7 @@ def _atomic_write_text(path: Path, data: str, encoding: str = "utf-8") -> None:
REPO_ROOT = Path(__file__).resolve().parents[1]
BUILD_INFO_PATH = (
REPO_ROOT / "studio" / "backend" / "utils" / "_studio_release_build.py"
)
BUILD_INFO_PATH = REPO_ROOT / "studio" / "backend" / "utils" / "_studio_release_build.py"
BUILD_INFO_SUFFIX = "studio/backend/utils/_studio_release_build.py"
VERSION_RE = re.compile(r"^v\d+\.\d+\.\d+(?:-[0-9A-Za-z.][0-9A-Za-z.-]*)?$")
GIT_DESCRIBE_SUFFIX_RE = re.compile(r"-\d+-g[0-9A-Fa-f]+(?:-dirty)?$")

View file

@ -128,12 +128,15 @@ def _normalize_yaml_run_strings(obj: Any) -> Any:
return obj
def _walk_yaml_diff(b: Any, a: Any, prefix: str = "") -> None:
def _walk_yaml_diff(
b: Any,
a: Any,
prefix: str = "",
) -> None:
"""Print a path-keyed summary of the first structural / scalar diff."""
if type(b) is not type(a):
print(
f" type-diff at {prefix or '/'}: "
f"{type(b).__name__} -> {type(a).__name__}",
f" type-diff at {prefix or '/'}: " f"{type(b).__name__} -> {type(a).__name__}",
)
return
if isinstance(b, dict):

View file

@ -123,9 +123,7 @@ class _Builder(ast.NodeVisitor):
def __init__(self):
self.module = Scope("module", "<module>", None)
self.uses: list[tuple[Scope, str, int]] = [] # (scope, name, lineno) hard loads
self.soft_uses: list[
tuple[Scope, str, int]
] = [] # annotations: count as "used"
self.soft_uses: list[tuple[Scope, str, int]] = [] # annotations: count as "used"
# but never as "unresolved"
# (forward refs / string annos)
@ -166,9 +164,7 @@ class _Builder(ast.NodeVisitor):
def _visit_stmt(self, node: ast.AST, scope: Scope) -> None:
if isinstance(node, (ast.Import, ast.ImportFrom)):
star = isinstance(node, ast.ImportFrom) and any(
a.name == "*" for a in node.names
)
star = isinstance(node, ast.ImportFrom) and any(a.name == "*" for a in node.names)
if star:
scope.star_import = True
for alias in node.names:
@ -356,9 +352,7 @@ class _Builder(ast.NodeVisitor):
self._bind_args(node.args, child)
self._visit_expr(node.body, child)
return
if isinstance(
node, (ast.ListComp, ast.SetComp, ast.GeneratorExp, ast.DictComp)
):
if isinstance(node, (ast.ListComp, ast.SetComp, ast.GeneratorExp, ast.DictComp)):
child = Scope("comp", f"{scope.qualname}.<comp>", scope)
for i, gen in enumerate(node.generators):
# first iterable is evaluated in the enclosing scope
@ -447,9 +441,7 @@ def _legb_chain(scope: Scope) -> list[Scope]:
chain = [scope]
p = scope.parent
while p is not None:
if (
p.kind != "class" or p.parent is None
): # module-level class never happens; keep module
if p.kind != "class" or p.parent is None: # module-level class never happens; keep module
if p.kind != "class":
chain.append(p)
p = p.parent
@ -624,9 +616,7 @@ def compare(before_src: str, after_src: str, path: str) -> list[tuple[str, str]]
for scope, names in b["ambiguous"].items():
new = names - a["ambiguous"].get(scope, set())
for n in sorted(new):
findings.append(
("WARN", f"{path}: AMBIGUOUS-BIND '{n}' import+non-import in {scope}")
)
findings.append(("WARN", f"{path}: AMBIGUOUS-BIND '{n}' import+non-import in {scope}"))
# 6. TARGET-MISSING (informational): a scope stopped resolving to an import
# target. Real bugs are already covered above; remaining cases are code
@ -639,9 +629,7 @@ def compare(before_src: str, after_src: str, path: str) -> list[tuple[str, str]]
if t in added_module_targets
else " [target not re-added here -> likely relocated/deleted]"
)
findings.append(
("INFO", f"{path}: TARGET-MISSING {t} in scope {scope}{relocated}")
)
findings.append(("INFO", f"{path}: TARGET-MISSING {t} in scope {scope}{relocated}"))
return findings
@ -650,45 +638,38 @@ def compare(before_src: str, after_src: str, path: str) -> list[tuple[str, str]]
_SELF_TESTS = {
"dangling_alias": (
# before: inline aliased import, used as _b
"import os\n"
"def f():\n"
" import glob as _b\n"
" return _b.glob('*')\n",
"import os\ndef f():\n import glob as _b\n return _b.glob('*')\n",
# after: hoisted to canonical, but reference NOT normalized -> _b dangles
"import os\n" "import glob\n" "def f():\n" " return _b.glob('*')\n",
"import os\nimport glob\ndef f():\n return _b.glob('*')\n",
"BLOCKER",
),
"rename_clash": (
# before: _b is a deliberate alias; `b` already means something else
"import re as _b\n" "b = 123\n" "def f():\n" " return _b.compile('x'), b\n",
"import re as _b\nb = 123\ndef f():\n return _b.compile('x'), b\n",
# after: someone normalized _b -> b ; now f().b is the int, re is lost
"import re\n" "b = 123\n" "def f():\n" " return b.compile('x'), b\n",
"import re\nb = 123\ndef f():\n return b.compile('x'), b\n",
"BLOCKER", # TARGET-MISSING from:.. or import:re in f
),
"clean_rename": (
"def f():\n" " import glob as _g\n" " return _g.glob('*')\n",
"import glob\n" "def f():\n" " return glob.glob('*')\n",
"def f():\n import glob as _g\n return _g.glob('*')\n",
"import glob\ndef f():\n return glob.glob('*')\n",
None, # expect NO blocker
),
"clean_dedup_redundant": (
"import sys\n" "def f():\n" " import sys\n" " return sys.argv\n",
"import sys\n" "def f():\n" " return sys.argv\n",
"import sys\ndef f():\n import sys\n return sys.argv\n",
"import sys\ndef f():\n return sys.argv\n",
None,
),
"from_import_dangling": (
# from-import alias left un-normalized
"def f():\n"
" from importlib.metadata import version as _v\n"
" return _v('x')\n",
"from importlib.metadata import version\n" "def f():\n" " return _v('x')\n",
"def f():\n from importlib.metadata import version as _v\n return _v('x')\n",
"from importlib.metadata import version\ndef f():\n return _v('x')\n",
"BLOCKER",
),
"local_var_clash": (
# _b renamed to b, but b is a LOCAL variable in f -> import silently unused
"def f(b):\n" " import re as _b\n" " return _b.compile(b)\n",
"import re\n"
"def f(b):\n"
" return b.compile(b)\n", # 'b' is the param, not the module
"def f(b):\n import re as _b\n return _b.compile(b)\n",
"import re\ndef f(b):\n return b.compile(b)\n", # 'b' is the param, not the module
"BLOCKER",
),
"substring_safe": (
@ -705,11 +686,8 @@ _SELF_TESTS = {
),
"attr_access_not_a_use": (
# x._b is attribute access, not a use of name _b; removing import _b is fine
"import os\n"
"def f(x):\n"
" import sys as _b\n"
" return x._b + _b.argv[0]\n",
"import os\n" "import sys\n" "def f(x):\n" " return x._b + sys.argv[0]\n",
"import os\ndef f(x):\n import sys as _b\n return x._b + _b.argv[0]\n",
"import os\nimport sys\ndef f(x):\n return x._b + sys.argv[0]\n",
None,
),
}
@ -797,9 +775,7 @@ def audit_files(paths: list[str]) -> int:
ok = n_err == 0 and n_fp == 0
print(
"\nAUDIT:",
"ROBUST (no crashes, no false positives vs pyflakes)"
if ok
else "NEEDS WORK (see above)",
"ROBUST (no crashes, no false positives vs pyflakes)" if ok else "NEEDS WORK (see above)",
)
return 0 if ok else 1
@ -835,18 +811,12 @@ def main() -> int:
blockers = [f for f in findings if f[0] == "BLOCKER"]
warns = [f for f in findings if f[0] == "WARN"]
infos = [f for f in findings if f[0] == "INFO"]
status = (
"CLEAN"
if not blockers and not warns
else ("BLOCKERS" if blockers else "WARNINGS")
)
status = "CLEAN" if not blockers and not warns else ("BLOCKERS" if blockers else "WARNINGS")
print(f"\n=== {path}: {status} ===")
for sev, m in blockers + warns + infos:
print(f" [{sev}] {m}")
any_blocker = any_blocker or bool(blockers)
print(
"\nOVERALL:", "FAIL (blockers found)" if any_blocker else "PASS (no blockers)"
)
print("\nOVERALL:", "FAIL (blockers found)" if any_blocker else "PASS (no blockers)")
return 1 if any_blocker else 0