mirror of
https://github.com/harvard-edge/cs249r_book.git
synced 2026-11-09 10:39:41 -06:00
Adds docstrings to the 900 definitions and 8 modules that had none across the binder CLI, its checks, and the audit and maintenance tools, and corrects a few docstrings that no longer matched the code. Only documentation changed.
73 lines
2.4 KiB
Python
73 lines
2.4 KiB
Python
"""
|
|
Catch inline `{python}` references that run before the cell that defines them.
|
|
|
|
Quarto executes a chapter's cells top to bottom, so a prose reference to
|
|
`SomeClass.value_str` fails at render time unless the LEGO cell defining
|
|
`SomeClass` appears earlier in the file.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from typing import List, Tuple
|
|
|
|
CELL = re.compile(r"^```\{python\}\s*$(.*?)^```\s*$", re.M | re.S)
|
|
CLASSDEF = re.compile(r"^\s*class\s+(\w+)", re.M)
|
|
ASSIGN = re.compile(r"^\s*(\w+)\s*=", re.M)
|
|
INLINE = re.compile(r"`\{python\}\s*([A-Za-z_]\w*)(?:\.\w+)*\s*`")
|
|
|
|
|
|
@dataclass
|
|
class InlineOrderIssue:
|
|
"""One inline `{python}` reference whose root name is not yet defined.
|
|
|
|
``line`` is 1-based; ``name`` is the leading identifier of the reference
|
|
and ``raw`` is the full matched inline span.
|
|
"""
|
|
|
|
path: Path
|
|
line: int
|
|
name: str
|
|
raw: str
|
|
message: str
|
|
|
|
|
|
def check_inline_order(path: Path, text: str) -> List[InlineOrderIssue]:
|
|
"""Check if any inline {python} references occur before their defining cell."""
|
|
events: List[Tuple[int, str, object]] = []
|
|
for m in CELL.finditer(text):
|
|
events.append((m.start(), "cell", m.group(1)))
|
|
for m in INLINE.finditer(text):
|
|
events.append((m.start(), "ref", (m.group(1), m.group(0))))
|
|
|
|
cell_spans = [(m.start(), m.end()) for m in CELL.finditer(text)]
|
|
|
|
def inside_cell(pos: int) -> bool:
|
|
"""Return True if character offset ``pos`` falls inside a Python cell."""
|
|
return any(a <= pos < b for a, b in cell_spans)
|
|
|
|
defined: set[str] = set()
|
|
issues: List[InlineOrderIssue] = []
|
|
for pos, kind, payload in sorted(events, key=lambda e: e[0]):
|
|
if kind == "cell":
|
|
body = payload
|
|
defined.update(CLASSDEF.findall(body))
|
|
defined.update(ASSIGN.findall(body))
|
|
continue
|
|
name, raw = payload
|
|
if inside_cell(pos):
|
|
continue
|
|
if name not in defined:
|
|
line = text.count("\n", 0, pos) + 1
|
|
issues.append(
|
|
InlineOrderIssue(
|
|
path=path,
|
|
line=line,
|
|
name=name,
|
|
raw=raw,
|
|
message=f"`{name}` referenced inline before its defining Python cell ({raw})",
|
|
)
|
|
)
|
|
return issues
|