H1+M2 review findings: CSV 5000x50 render cap (truncated with note) + stale output sweep; 4 regression tests (H1x3, M2x1); USER_GUIDE truncation docs
Test / Tests (Python 3.12) (push) Canceled after 0s

This commit is contained in:
sirdave73
2026-09-04 06:34:20 -04:00
parent 72b5f2f0dd
commit 7ea2b2baad
4 changed files with 150 additions and 28 deletions
+24 -4
View File
@@ -18,6 +18,20 @@ from . import common
from .text_converter import decode_bytes
# H1 render cap: a single oversized CSV must not exhaust the single worker
# thread (unbounded CPU/memory DoS). Rows/columns are TRUNCATED (not rejected),
# and a visible note is appended so the behaviour is honest. Mirrors the
# documented limit (USER_GUIDE.md: max 5000 rows x 50 cols).
MAX_ROWS = 5000
MAX_COLS = 50
# Visible note appended to the rendered table when the cap is exceeded.
_TRUNCATION_NOTE = (
"<p class=\"warn\">truncated to 5000 rows x 50 cols; "
"split the file to include more</p>"
)
def _rows(text: str, delimiter: str) -> list[list[str]]:
rows = list(csv.reader(io.StringIO(text), delimiter=delimiter))
return [row for row in rows if any(cell.strip() for cell in row)]
@@ -26,13 +40,19 @@ def _rows(text: str, delimiter: str) -> list[list[str]]:
def _table_html(rows: list[list[str]]) -> str:
if not rows:
return ""
header, body = rows[0], rows[1:]
head = "".join(f"<th>{common.escape(c)}</th>" for c in header)
# H1: cap to the first MAX_ROWS rows and first MAX_COLS columns.
shown = rows[:MAX_ROWS]
header, body = shown[0], shown[1:]
n_cols = min(len(header), MAX_COLS)
head = "".join(f"<th>{common.escape(c)}</th>" for c in header[:MAX_COLS])
out = [f"<table><tr>{head}</tr>"]
truncated = len(rows) > MAX_ROWS
for row in body:
# pad ragged rows to header width so borders line up
cells = row + [""] * (len(header) - len(row))
# pad ragged rows to the (truncated) header width so borders line up
cells = row[:MAX_COLS] + [""] * (n_cols - len(row[:MAX_COLS]))
out.append("<tr>" + "".join(f"<td>{common.escape(c)}</td>" for c in cells) + "</tr>")
if truncated:
out.append(_TRUNCATION_NOTE)
out.append("</table>")
return "\n".join(out)