"""Table-specific Markdown probes. Run in the existing renderer environment. Outputs stay under this run. No network, dependency installation, or UI claims. """ from __future__ import annotations import hashlib import importlib.metadata import inspect import json import platform import subprocess from datetime import datetime, timezone from html.parser import HTMLParser from pathlib import Path import markdown from markdown.extensions.tables import TableProcessor from markdown_it import MarkdownIt from markdown_it.rules_block import table as table_rule HERE = Path(__file__).resolve().parent OUT = HERE / "renderers" SAMPLES = { "table-bare-markers": "| Status | Task |\n| --- | --- |\n| [ ] | Draft release notes |\n| [x] | Review schema |\n", "table-list-markers": "| Task | Owner |\n| --- | --- |\n| - [ ] Draft release notes | Writer |\n| - [x] Review schema | Reviewer |\n", "table-break-markers": "| Task | Status |\n| --- | --- |\n| Draft release notes | Pending
- [ ] Approve |\n| Review schema | Done
- [x] Approve |\n", "table-status-text": "| Task | Status |\n| --- | --- |\n| Draft release notes | ☐ Pending |\n| Review schema | ☑ Done |\n", "table-html-inputs": '| Task | Status |\n| --- | --- |\n| Draft release notes | |\n| Review schema | |\n', "outside-task-list": "- [ ] Draft release notes\n- [x] Review schema\n", } class Structure(HTMLParser): def __init__(self): super().__init__(convert_charrefs=True) self.tags = {} self.inputs = [] self.cells = [] self.cell = None self.table_depth = 0 self.checkboxes_in_tables = 0 def handle_starttag(self, tag, attrs): self.tags[tag] = self.tags.get(tag, 0) + 1 if tag == "table": self.table_depth += 1 if tag in ("td", "th"): self.cell = "" a = dict(attrs) if tag == "input" and a.get("type") == "checkbox": self.inputs.append({"checked": "checked" in a, "disabled": "disabled" in a}) self.checkboxes_in_tables += bool(self.table_depth) def handle_endtag(self, tag): if tag == "table": self.table_depth -= 1 if tag in ("td", "th") and self.cell is not None: self.cells.append(self.cell) self.cell = None def handle_data(self, data): if self.cell is not None: self.cell += data def run(): OUT.mkdir(exist_ok=True) results = json.loads((OUT / "results.json").read_text()) # Preserve the upstream baseline; replace only this script's named cases. rows = [r for r in results["results"] if r["sample"] not in SAMPLES] versions = { "markdown-it-commonmark": importlib.metadata.version("markdown-it-py"), "markdown-it-table-html": importlib.metadata.version("markdown-it-py"), "markdown-it-table-escaped": importlib.metadata.version("markdown-it-py"), "python-markdown": importlib.metadata.version("Markdown"), "pandoc-gfm": subprocess.check_output(["pandoc", "--version"], text=True).splitlines()[0], } options = { "markdown-it-commonmark": 'MarkdownIt("commonmark", {"html": True})', "markdown-it-table-html": 'MarkdownIt("commonmark", {"html": True}).enable("table")', "markdown-it-table-escaped": 'MarkdownIt("commonmark", {"html": False}).enable("table")', "python-markdown": 'markdown.markdown(source, extensions=["tables", "fenced_code"])', "pandoc-gfm": "pandoc -f gfm -t html", } traces = {} for name, source in SAMPLES.items(): (OUT / f"{name}.md").write_text(source) rendered = { "markdown-it-commonmark": MarkdownIt("commonmark", {"html": True}).render(source), "markdown-it-table-html": MarkdownIt("commonmark", {"html": True}).enable("table").render(source), "markdown-it-table-escaped": MarkdownIt("commonmark", {"html": False}).enable("table").render(source), "python-markdown": markdown.markdown(source, extensions=["tables", "fenced_code"]), "pandoc-gfm": subprocess.run(["pandoc", "-f", "gfm", "-t", "html"], input=source, text=True, capture_output=True, check=True).stdout, } traces[name] = [{"type": t.type, "tag": t.tag, "nesting": t.nesting, "content": t.content} for t in MarkdownIt("commonmark").enable("table").parse(source)] for renderer, html in rendered.items(): path = OUT / f"{name}--{renderer}.txt" path.write_text(html) p = Structure() p.feed(html) rows.append({"sample": name, "renderer": renderer, "tags": p.tags, "checkbox_inputs": len(p.inputs), "checked": sum(x["checked"] for x in p.inputs), "disabled": sum(x["disabled"] for x in p.inputs), "checkboxes_in_tables": p.checkboxes_in_tables, "cells": p.cells, "source": f"lab/renderers/{name}.md", "output": f"lab/renderers/{path.name}", "source_sha256": hashlib.sha256(source.encode()).hexdigest(), "output_sha256": hashlib.sha256(html.encode()).hexdigest()}) results["results"] = rows results["checkbox_table_experiment"] = { "date": datetime.now(timezone.utc).isoformat(), "python": platform.python_version(), "versions": versions, "options": options, "command": "PYTHONDONTWRITEBYTECODE=1 .venv/bin/python runs/2026-11-04-markovo-markdown-checkbox-in-table/lab/checkbox_table_probe.py", "scope": "Local HTML structure only; no GitHub.com, GitLab, VS Code, Obsidian UI, browser clicks, or source write-back tested.", "samples": list(SAMPLES), "metrics": {"tags": "HTMLParser start-tag counts, including XHTML start-end tags once", "checkbox_inputs": "input elements whose type attribute is checkbox", "checked": "checkbox input elements carrying the checked attribute", "disabled": "checkbox input elements carrying the disabled attribute", "checkboxes_in_tables": "checkbox input elements with a table ancestor", "cells": "decoded text inside th/td, excluding markup and attributes"}, } (OUT / "results.json").write_text(json.dumps(results, indent=2, ensure_ascii=False) + "\n") (OUT / "table-token-traces.json").write_text(json.dumps(traces, indent=2) + "\n") source_record = [] for title, obj in [("markdown_it/rules_block/table.py:table", table_rule), ("markdown/extensions/tables.py:TableProcessor._build_row", TableProcessor._build_row)]: file = Path(inspect.getfile(obj)) source_record.extend([title, f"installed_file_sha256={hashlib.sha256(file.read_bytes()).hexdigest()}", f"function_start_line={inspect.getsourcelines(obj)[1]}", inspect.getsource(obj)]) (HERE / "table-parser-source.txt").write_text("\n".join(source_record)) lines = ["# Checkbox-in-table local experiment", "", results["checkbox_table_experiment"]["scope"], "", "| Sample | Configuration | tables | li | checkbox inputs | checked | disabled | table checkboxes |", "| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: |"] for r in rows: if r["sample"] in SAMPLES: lines.append(f'| {r["sample"]} | {r["renderer"]} | {r["tags"].get("table",0)} | {r["tags"].get("li",0)} | {r["checkbox_inputs"]} | {r["checked"]} | {r["disabled"]} | {r["checkboxes_in_tables"]} |') (OUT / "checkbox-table-summary.md").write_text("\n".join(lines) + "\n") manifest = ["# Inputs and raw HTML outputs", "", "All paths below are relative to this directory.", "", "| Input | " + " | ".join(versions) + " |", "| --- | " + " | ".join("---" for _ in versions) + " |"] for name in SAMPLES: manifest.append(f"| [{name}.md]({name}.md) | " + " | ".join( f"[{name}--{renderer}.txt]({name}--{renderer}.txt)" for renderer in versions) + " |") (OUT / "checkbox-table-files.md").write_text("\n".join(manifest) + "\n") print(json.dumps({"cases": len(SAMPLES) * len(versions), "versions": versions, "python": platform.python_version(), "outputs": str(OUT)})) if __name__ == "__main__": run()