forge: citation_resolve_check iter_citations DRY extraction, behavior-preserving (selftest 14/14, byte-identical census)
This commit is contained in:
1 parent
13beb50c14
commit
949858814c
3 files changed
+73
-56
No files matched your search
@@ -265,6 +265,47 @@ def classify_bare(store, here, inherited, want):
|
||||
return v, named, self_ok
|
||||
|
||||
|
||||
Cite = collections.namedtuple("Cite", [
|
||||
"abs_line", "in_fence", "kind", "target", "want",
|
||||
"verdict", "have", "named_has", "self_has", "end", "ctx"])
|
||||
|
||||
|
||||
def iter_citations(text, here, store):
|
||||
"""Yield one Cite per citation match in `text`, in source order.
|
||||
|
||||
Single source of truth for how citations are walked and classified -- shared
|
||||
by scan() (the production census) and _expect() (the selftest fixture grader).
|
||||
Both inlined this same blocks/fence/mention/classify loop; the two copies had
|
||||
already begun to drift: scan() tallies fenced+mention stats while _expect()
|
||||
silently drops them, and only the accumulator differed -- the classification
|
||||
itself was duplicated. One generator, one rule.
|
||||
"""
|
||||
mask = fence_mask(text)
|
||||
for first, blk in blocks(text):
|
||||
last = None
|
||||
for m in PAT.finditer(blk):
|
||||
abs_line = first + blk.count("\n", 0, m.start())
|
||||
in_fence = abs_line in mask
|
||||
ctx = blk[max(0, m.start() - 70):m.end() + 70].replace("\n", " ")
|
||||
target = m.group("path")
|
||||
if target:
|
||||
last = target
|
||||
if m.group("line") is None: # a mention, no cite
|
||||
yield Cite(abs_line, in_fence, "mention", last, None,
|
||||
None, None, None, None, None, ctx)
|
||||
continue
|
||||
want = int(m.group("line"))
|
||||
verdict, have = classify_explicit(store, last, want)
|
||||
end = int(m.group("end")) if m.group("end") else None
|
||||
yield Cite(abs_line, in_fence, "explicit", last, want,
|
||||
verdict, have, None, None, end, ctx)
|
||||
else:
|
||||
want = int(m.group("bline"))
|
||||
verdict, named, self_ok = classify_bare(store, here, last, want)
|
||||
yield Cite(abs_line, in_fence, "bare", last, want,
|
||||
verdict, None, named, self_ok, None, ctx)
|
||||
|
||||
|
||||
def scan(repo, rev, corpus, store):
|
||||
rows, stats = [], collections.Counter()
|
||||
for p in corpus:
|
||||
@@ -272,38 +313,21 @@ def scan(repo, rev, corpus, store):
|
||||
stats["corpus_missing"] += 1
|
||||
continue
|
||||
text = store.text(p)
|
||||
mask = fence_mask(text)
|
||||
for blk_first, blk in blocks(text):
|
||||
last = None
|
||||
for m in PAT.finditer(blk):
|
||||
abs_line = blk_first + blk.count("\n", 0, m.start())
|
||||
if abs_line in mask: # quoted, not asserted
|
||||
stats["fenced_mentions" if m.group("path") and m.group("line") is None
|
||||
else "fenced_citations"] += 1
|
||||
continue
|
||||
ctx = blk[max(0, m.start() - 70):m.end() + 70].replace("\n", " ")
|
||||
if m.group("path"):
|
||||
last = m.group("path")
|
||||
if m.group("line") is None: # a mention, no cite
|
||||
stats["mentions"] += 1
|
||||
continue
|
||||
want = int(m.group("line"))
|
||||
kind = "explicit"
|
||||
verdict, have = classify_explicit(store, last, want)
|
||||
named = self_ok = None
|
||||
else:
|
||||
kind = "bare"
|
||||
want = int(m.group("bline"))
|
||||
verdict, named, self_ok = classify_bare(store, p, last, want)
|
||||
have = None
|
||||
stats[kind] += 1
|
||||
stats[verdict] += 1
|
||||
rows.append({"file": p, "line": abs_line, "kind": kind,
|
||||
"target": last, "want": want,
|
||||
"end": int(m.group("end")) if m.group("end") else None,
|
||||
"verdict": verdict, "have": have,
|
||||
"named_has": named, "self_has": self_ok,
|
||||
"context": ctx})
|
||||
for c in iter_citations(text, p, store):
|
||||
if c.in_fence:
|
||||
stats["fenced_mentions" if c.kind == "mention"
|
||||
else "fenced_citations"] += 1
|
||||
continue
|
||||
if c.kind == "mention":
|
||||
stats["mentions"] += 1
|
||||
continue
|
||||
stats[c.kind] += 1
|
||||
stats[c.verdict] += 1
|
||||
rows.append({"file": p, "line": c.abs_line, "kind": c.kind,
|
||||
"target": c.target, "want": c.want,
|
||||
"end": c.end, "verdict": c.verdict, "have": c.have,
|
||||
"named_has": c.named_has, "self_has": c.self_has,
|
||||
"context": c.ctx})
|
||||
return rows, stats
|
||||
|
||||
|
||||
@@ -369,22 +393,10 @@ FIXTURES = [
|
||||
|
||||
def _expect(here, text, store):
|
||||
got = []
|
||||
mask = fence_mask(text)
|
||||
for first, blk in blocks(text):
|
||||
last = None
|
||||
for m in PAT.finditer(blk):
|
||||
if first + blk.count("\n", 0, m.start()) in mask:
|
||||
continue # quoted, not asserted
|
||||
if m.group("path"):
|
||||
last = m.group("path")
|
||||
if m.group("line") is None:
|
||||
continue
|
||||
v, _ = classify_explicit(store, last, int(m.group("line")))
|
||||
got.append(("explicit", v, last, int(m.group("line"))))
|
||||
else:
|
||||
want = int(m.group("bline"))
|
||||
v, _n, _s = classify_bare(store, here, last, want)
|
||||
got.append(("bare", v, last, want))
|
||||
for c in iter_citations(text, here, store):
|
||||
if c.in_fence or c.kind == "mention":
|
||||
continue
|
||||
got.append((c.kind, c.verdict, c.target, c.want))
|
||||
return got
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user