diff --git a/src/mckicad/tools/netlist.py b/src/mckicad/tools/netlist.py index 1da641f..a1a49c8 100644 --- a/src/mckicad/tools/netlist.py +++ b/src/mckicad/tools/netlist.py @@ -15,6 +15,7 @@ import re from typing import Any import xml.etree.ElementTree as ET +from mckicad.config import INLINE_RESULT_THRESHOLD from mckicad.server import mcp from mckicad.utils.file_utils import write_detail_file @@ -405,14 +406,7 @@ def import_netlist( stats["net_count"], stats["component_count"], stats["connection_count"], ) - result: dict[str, Any] = { - "success": True, - "format_detected": fmt, - "source_path": source_path, - **parsed, - } - - # Write output JSON + # Write full parsed data to output JSON (always — this is the complete data) if output_path: out = os.path.abspath(os.path.expanduser(output_path)) os.makedirs(os.path.dirname(out) or ".", exist_ok=True) @@ -423,6 +417,31 @@ def import_netlist( source_path, "imported_netlist.json", parsed, ) - result["output_path"] = out + result: dict[str, Any] = { + "success": True, + "format_detected": fmt, + "source_path": source_path, + "output_path": out, + "statistics": stats, + } + + nets = parsed["nets"] + if stats["net_count"] <= INLINE_RESULT_THRESHOLD: + # Small netlist — return everything inline + result["nets"] = nets + result["components"] = parsed["components"] + if "pin_metadata" in parsed: + result["pin_metadata"] = parsed["pin_metadata"] + else: + # Large netlist — compact summary inline, full data in output file + # Sort nets by connection count (most connected first) for useful preview + sorted_nets = sorted(nets.items(), key=lambda kv: len(kv[1]), reverse=True) + result["nets_preview"] = dict(sorted_nets[:INLINE_RESULT_THRESHOLD]) + result["nets_preview_note"] = ( + f"Showing top {INLINE_RESULT_THRESHOLD} of {stats['net_count']} nets " + f"by connection count. Full data in output_path." + ) + # Include component count but not full component dict + result["component_refs"] = sorted(parsed["components"].keys()) return result diff --git a/tests/test_netlist.py b/tests/test_netlist.py index 6ab873a..718d27c 100644 --- a/tests/test_netlist.py +++ b/tests/test_netlist.py @@ -430,3 +430,83 @@ class TestOutputFile: assert result["success"] is True assert "output_path" in result assert os.path.isfile(result["output_path"]) + + def test_large_netlist_returns_preview(self, tmp_output_dir): + """Netlists exceeding INLINE_RESULT_THRESHOLD return a preview, not full data.""" + from mckicad.config import INLINE_RESULT_THRESHOLD + from mckicad.tools.netlist import import_netlist + + # Generate a netlist with more nets than the threshold + net_count = INLINE_RESULT_THRESHOLD + 5 + net_lines = [] + comp_lines = [] + refs_seen: set[str] = set() + for i in range(1, net_count + 1): + ref_a = f"R{i}" + ref_b = f"C{i}" + comp_lines.append(f'{i}k') + comp_lines.append(f'{i}00nF') + refs_seen.update([ref_a, ref_b]) + net_lines.append( + f'' + f'' + f'' + f'' + ) + + xml = ( + '' + + "".join(comp_lines) + + "" + + "".join(net_lines) + + "" + ) + src = os.path.join(tmp_output_dir, "large.xml") + with open(src, "w") as f: + f.write(xml) + + result = import_netlist(source_path=src) + assert result["success"] is True + assert result["statistics"]["net_count"] == net_count + + # Should NOT have full nets inline + assert "nets" not in result + # Should have preview + assert "nets_preview" in result + assert len(result["nets_preview"]) == INLINE_RESULT_THRESHOLD + assert "nets_preview_note" in result + + # Should have component refs list instead of full component dict + assert "component_refs" in result + assert "components" not in result + + # Full data should be in the output file + assert os.path.isfile(result["output_path"]) + with open(result["output_path"]) as f: + full_data = json.load(f) + assert len(full_data["nets"]) == net_count + + def test_small_netlist_returns_inline(self, tmp_output_dir): + """Netlists within INLINE_RESULT_THRESHOLD return full data inline.""" + from mckicad.tools.netlist import import_netlist + + xml = textwrap.dedent("""\ + + 1k + + + + + """) + src = os.path.join(tmp_output_dir, "small.xml") + with open(src, "w") as f: + f.write(xml) + + result = import_netlist(source_path=src) + assert result["success"] is True + # Should have full data inline + assert "nets" in result + assert "components" in result + # Should NOT have preview keys + assert "nets_preview" not in result + assert "component_refs" not in result