"""Schemdraw code block renderer. Executes Python schemdraw code from a chapter code block and returns SVG. Usage in markdown: ```schemdraw import schemdraw from schemdraw import elements as e with schemdraw.Drawing(show=False) as d: d += e.Resistor().right().label('R1') ``` """ import base64 import subprocess import sys import tempfile import textwrap from pathlib import Path def render(code: str, project_dir: Path = None) -> str: """Execute schemdraw code, return inline SVG as HTML tag. Args: code: Python source code using schemdraw project_dir: project root (unused, kept for interface consistency) Returns: HTML string with inline base64 SVG image """ # Write code to a temp script and execute, capturing SVG output wrapped = textwrap.dedent(code).strip() with tempfile.NamedTemporaryFile( suffix='.svg', mode='w+', encoding='utf-8', delete=False ) as tmp: svg_path = Path(tmp.name) try: # Execute the schemdraw code as a subprocess for isolation. # The script must produce 'OUTPUT: ' on the last line, # or we inject a save hook. script = _wrap_code(wrapped, str(svg_path)) result = subprocess.run( [sys.executable, '-c', script], capture_output=True, text=True, timeout=30, ) if result.returncode != 0: return ( f'
' f'Schemdraw error:
{result.stderr[:500] or result.stdout[:500]}
' f'
' ) # Read the generated SVG if svg_path.exists() and svg_path.stat().st_size > 0: svg_content = svg_path.read_text(encoding='utf-8') b64 = base64.b64encode(svg_content.encode('utf-8')).decode('ascii') return ( f'
' f'schemdraw diagram' f'
' ) else: return ( f'
' f'Schemdraw ran but produced no SVG output.' f'
' ) finally: if svg_path.exists(): svg_path.unlink(missing_ok=True) def _wrap_code(code: str, svg_path: str) -> str: """Wrap user code to redirect schemdraw output to a file.""" # schemdraw writes SVG to the 'file' parameter of Drawing(). # We modify the code to inject file= if missing. if "file=" not in code and "Drawing(" in code: code = code.replace( 'Drawing(show=False)', f"Drawing(show=False, file=r'{svg_path}')", ) code = code.replace( 'Drawing()', f"Drawing(show=False, file=r'{svg_path}')", ) return code