Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/mistune/plugins/footnotes.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def parse_inline_footnote(inline: "InlineParser", m: Match[str], state: "InlineS
if not indexes:
indexes = {note_key: index for index, note_key in enumerate(notes)}
state.env["footnote_indexes"] = indexes
if key not in notes:
if key not in indexes:
notes.append(key)
indexes[key] = len(notes) - 1
state.env["footnotes"] = notes
Expand Down
63 changes: 63 additions & 0 deletions tests/footnote_defs_quadratic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Synthesized standalone repro for the footnote_defs defect (complexity).

Discovered autonomously by blackhole_agent.upstream_discovery. Runs a doubling
ladder against the source tree given as argv[1]; exits 1 while the defect is
present, 0 once repaired. Usage: python <this file> <path-to-src-dir>
"""
import json, math, sys, time

sys.path.insert(0, sys.argv[1])
PLUGINS = ['footnotes']
KIND = 'complexity'
EXPONENT_THRESHOLD = 1.75
TIME_FLAG_FLOOR = 0.3

def gen(n):
refs = ''.join('[^%d] ' % i for i in range(n))
defs = '\n'.join('[^%d]: x' % i for i in range(n))
return refs + '\n\n' + defs

import mistune
Comment thread
susyimes marked this conversation as resolved.
Outdated

_RENDERERS = {}

def render(text, plugins):
key = tuple(plugins)
md = _RENDERERS.get(key)
if md is None:
md = mistune.create_markdown(plugins=list(plugins))
_RENDERERS[key] = md
md(text)

render('warmup', PLUGINS)
n = 16000
times = []
crashed = None
limit = max(16000 * 4, 16000 + 1)
while n <= limit:
text = gen(n)
t0 = time.perf_counter()
try:
render(text, PLUGINS)
elapsed = time.perf_counter() - t0
except Exception as e:
crashed = type(e).__name__
break
times.append((n, elapsed))
if elapsed >= 1.0 and len(times) >= 2:
# Always measure at least two sizes: one load-inflated run must not
# end the ladder before any growth pair exists.
break
n *= 2

if crashed is not None:
print(json.dumps({'defect': True, 'kind': KIND, 'crash': crashed}))
sys.exit(1)
worst = 0.0
for (n1, t1), (n2, t2) in zip(times, times[1:]):
if t1 >= 0.02 and n2 > n1:
worst = max(worst, math.log2(max(t2, 1e-9) / t1) / math.log2(n2 / n1))
t_max = max((t for _, t in times), default=0.0)
defect = worst >= EXPONENT_THRESHOLD and t_max >= TIME_FLAG_FLOOR
print(json.dumps({'defect': defect, 'kind': KIND, 'exponent': round(worst, 3), 't_max': round(t_max, 4)}))
sys.exit(1 if defect else 0)
17 changes: 17 additions & 0 deletions tests/test_contribution_footnote_defs_membership_quadratic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Regression test synthesized by blackhole_agent.upstream_contribution.

Runs the minimized standalone repro for defect footnote-defs-membership-quadratic against the
patched source tree; the repro exits 0 only once the defect is repaired.
"""

import subprocess
import sys
from pathlib import Path

REPRO = Path(__file__).resolve().parent / 'footnote_defs_quadratic.py'
SRC = Path(__file__).resolve().parents[1] / 'src'


def test_footnote_defs_membership_quadratic_regression() -> None:
proc = subprocess.run([sys.executable, str(REPRO), str(SRC)], capture_output=True, text=True)
assert proc.returncode == 0, proc.stderr[-400:]
Comment thread
susyimes marked this conversation as resolved.
Outdated
Loading