From b4d114ac740367b37f761e66955c6c8d35694296 Mon Sep 17 00:00:00 2001 From: KiCAD MCP Bot Date: Thu, 26 Feb 2026 10:03:20 -0500 Subject: [PATCH] fix: Address critical Windows compatibility and JLCPCB issues (#36, #37, #35) Phase 1 critical fixes addressing three high-priority issues: Issue #36 - Windows IPC backend crash (os.getuid not available): - Add platform detection to skip Unix-specific socket paths on Windows - Use hasattr check before calling os.getuid() - Windows now uses auto-detect fallback (named pipes) - Maintains full Unix socket support on Linux/macOS Issue #37 - Windows schematic file creation broken: - Generate unique UUIDs instead of invalid all-zeros UUID - Add explicit UTF-8 encoding for cross-platform compatibility - Force Unix line endings (LF) to prevent Windows CRLF issues - Update template file with valid placeholder UUID - Fix hardcoded /tmp/ paths in wire_manager.py and pin_locator.py Issue #35 - JLCPCB download limited to 100 parts: - Change batch_size from 1000 to 100 to match tscircuit API limit - Remove premature loop termination when batch < batch_size - Add documentation explaining API limitation - Expected result: Full ~2.5M part catalog download (40-60 minutes) All changes maintain backward compatibility and include detailed comments. Co-Authored-By: Claude Sonnet 4.5 --- python/commands/jlcsearch.py | 15 +++++++++------ python/commands/pin_locator.py | 5 +++-- python/commands/schematic.py | 8 ++++++-- python/commands/wire_manager.py | 5 +++-- python/kicad_api/ipc_backend.py | 17 +++++++++++------ .../templates/template_with_symbols.kicad_sch | 2 +- 6 files changed, 33 insertions(+), 19 deletions(-) diff --git a/python/commands/jlcsearch.py b/python/commands/jlcsearch.py index b5f265b..b6e4584 100644 --- a/python/commands/jlcsearch.py +++ b/python/commands/jlcsearch.py @@ -143,14 +143,17 @@ class JLCSearchClient: def download_all_components( self, callback: Optional[Callable[[int, str], None]] = None, - batch_size: int = 1000 + batch_size: int = 100 ) -> List[Dict]: """ Download all components from jlcsearch database + Note: tscircuit API has a hard-coded 100 result limit per request. + Full catalog download requires ~25,000 paginated requests (~40-60 minutes). + Args: callback: Optional progress callback function(parts_count, status_msg) - batch_size: Number of parts per batch + batch_size: Number of parts per batch (max 100 due to API limit) Returns: List of all parts @@ -168,7 +171,8 @@ class JLCSearchClient: offset=offset ) - if not batch: + # Stop if no results returned (end of catalog) + if not batch or len(batch) == 0: break all_parts.extend(batch) @@ -179,9 +183,8 @@ class JLCSearchClient: else: logger.info(f"Downloaded {len(all_parts)} parts so far...") - # If we got fewer results than requested, we've reached the end - if len(batch) < batch_size: - break + # Continue pagination - API returns exactly 100 results per page until exhausted + # Only stop when we get 0 results (handled above) # Rate limiting - be nice to the API time.sleep(0.1) diff --git a/python/commands/pin_locator.py b/python/commands/pin_locator.py index a4b3835..1e91bb0 100644 --- a/python/commands/pin_locator.py +++ b/python/commands/pin_locator.py @@ -7,6 +7,7 @@ Uses S-expression parsing to extract pin data from symbol definitions. import logging import math +import tempfile from pathlib import Path from typing import List, Tuple, Optional, Dict import sexpdata @@ -314,8 +315,8 @@ if __name__ == '__main__': print("PIN LOCATOR TEST") print("=" * 80) - # Create test schematic with components - test_path = Path('/tmp/test_pin_locator.kicad_sch') + # Create test schematic with components (cross-platform temp directory) + test_path = Path(tempfile.gettempdir()) / 'test_pin_locator.kicad_sch' template_path = Path('/home/chris/MCP/KiCAD-MCP-Server/python/templates/template_with_symbols_expanded.kicad_sch') shutil.copy(template_path, test_path) diff --git a/python/commands/schematic.py b/python/commands/schematic.py index d662ca9..cc97629 100644 --- a/python/commands/schematic.py +++ b/python/commands/schematic.py @@ -2,6 +2,7 @@ from skip import Schematic import os import shutil import logging +import uuid logger = logging.getLogger('kicad_interface') @@ -28,9 +29,12 @@ class SchematicManager: else: # Fallback: create minimal schematic logger.warning(f"Template not found at {template_path}, creating minimal schematic") - with open(output_path, 'w') as f: + # Generate unique UUID for this schematic + schematic_uuid = str(uuid.uuid4()) + # Write with explicit UTF-8 encoding and Unix line endings for cross-platform compatibility + with open(output_path, 'w', encoding='utf-8', newline='\n') as f: f.write('(kicad_sch (version 20230121) (generator "KiCAD-MCP-Server")\n\n') - f.write(' (uuid 00000000-0000-0000-0000-000000000000)\n\n') + f.write(f' (uuid {schematic_uuid})\n\n') f.write(' (paper "A4")\n\n') f.write(' (lib_symbols\n )\n\n') f.write(' (sheet_instances\n (path "/" (page "1"))\n )\n') diff --git a/python/commands/wire_manager.py b/python/commands/wire_manager.py index 95788c4..8ff9267 100644 --- a/python/commands/wire_manager.py +++ b/python/commands/wire_manager.py @@ -9,6 +9,7 @@ manipulate the .kicad_sch file directly. import uuid import logging import math +import tempfile from pathlib import Path from typing import List, Tuple, Optional, Dict import sexpdata @@ -382,8 +383,8 @@ if __name__ == '__main__': print("WIRE MANAGER TEST") print("=" * 80) - # Create test schematic - test_path = Path('/tmp/test_wire_manager.kicad_sch') + # Create test schematic (cross-platform temp directory) + test_path = Path(tempfile.gettempdir()) / 'test_wire_manager.kicad_sch' template_path = Path('/home/chris/MCP/KiCAD-MCP-Server/python/templates/empty.kicad_sch') shutil.copy(template_path, test_path) diff --git a/python/kicad_api/ipc_backend.py b/python/kicad_api/ipc_backend.py index 0f9339e..94deda9 100644 --- a/python/kicad_api/ipc_backend.py +++ b/python/kicad_api/ipc_backend.py @@ -15,6 +15,7 @@ Key Benefits over SWIG: """ import logging import os +import platform from pathlib import Path from typing import Optional, Dict, Any, List, Callable @@ -74,12 +75,16 @@ class IPCBackend(KiCADBackend): if socket_path: socket_paths_to_try.append(socket_path) else: - # Common socket locations - socket_paths_to_try = [ - 'ipc:///tmp/kicad/api.sock', # Linux default - f'ipc:///run/user/{os.getuid()}/kicad/api.sock', # XDG runtime - None # Let kipy auto-detect - ] + # Common socket locations (Unix-like systems only) + # Windows uses named pipes, handled by auto-detect + if platform.system() != "Windows": + socket_paths_to_try.append('ipc:///tmp/kicad/api.sock') # Linux default + # XDG runtime directory (requires getuid, Unix only) + if hasattr(os, 'getuid'): + socket_paths_to_try.append(f'ipc:///run/user/{os.getuid()}/kicad/api.sock') + + # Auto-detect for all platforms (Windows uses named pipes, Unix uses sockets) + socket_paths_to_try.append(None) last_error = None for path in socket_paths_to_try: diff --git a/python/templates/template_with_symbols.kicad_sch b/python/templates/template_with_symbols.kicad_sch index afcc779..8183ad2 100644 --- a/python/templates/template_with_symbols.kicad_sch +++ b/python/templates/template_with_symbols.kicad_sch @@ -1,6 +1,6 @@ (kicad_sch (version 20230121) (generator "KiCAD-MCP-Server") - (uuid 00000000-0000-0000-0000-000000000000) + (uuid a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d) (paper "A4")