fix: prevent pcbnew stdout noise from causing sync_schematic_to_board timeouts

The TS<->Python communication channel uses stdout for JSON responses.
pcbnew's C++ SWIG layer can write warnings and diagnostics directly to
C-level stdout (fd 1), corrupting the JSON framing. The TS parser then
never sees valid JSON and the command times out after 30 seconds.

Three changes fix this:

1. Python stdout redirect: In main(), save the original stdout fd for
   exclusive JSON response use, then redirect fd 1 to stderr so all
   pcbnew C++ output goes to logs instead of the response pipe.

2. Robust TS JSON parser: tryParseResponse() now uses newline-delimited
   parsing as a fallback. The Python side writes single-line JSON
   terminated by \n; the parser uses this as the completion signal
   instead of brace-matching, which prevents premature resolution of
   truncated chunked responses. Non-JSON preamble lines are logged
   and stripped.

3. Fix stray print() calls: Converted print() to logger in
   component_schematic.py and library_schematic.py so they don't
   leak to stdout during normal operations.

Also adds sync_schematic_to_board to the longRunningCommands list for
an appropriate timeout value.
This commit is contained in:
William Viana
2026-04-03 11:24:42 -07:00
parent f79a3d6435
commit 0bc00b1451
4 changed files with 137 additions and 50 deletions

View File

@@ -278,13 +278,13 @@ class ComponentManager:
if symbol_to_remove:
schematic.symbol._elements.remove(symbol_to_remove)
print(f"Removed component {component_ref} from schematic.")
logger.info(f"Removed component {component_ref} from schematic.")
return True
else:
print(f"Component with reference {component_ref} not found.")
logger.warning(f"Component with reference {component_ref} not found.")
return False
except Exception as e:
print(f"Error removing component {component_ref}: {e}")
logger.error(f"Error removing component {component_ref}: {e}")
return False
@staticmethod
@@ -302,15 +302,14 @@ class ComponentManager:
if key in symbol_to_update.property:
symbol_to_update.property[key].value = value
else:
# Add as a new property if it doesn't exist
symbol_to_update.property.append(key, value)
print(f"Updated properties for component {component_ref}.")
logger.info(f"Updated properties for component {component_ref}.")
return True
else:
print(f"Component with reference {component_ref} not found.")
logger.warning(f"Component with reference {component_ref} not found.")
return False
except Exception as e:
print(f"Error updating component {component_ref}: {e}")
logger.error(f"Error updating component {component_ref}: {e}")
return False
@staticmethod
@@ -318,9 +317,9 @@ class ComponentManager:
"""Get a component by reference designator"""
for symbol in schematic.symbol:
if symbol.reference == component_ref:
print(f"Found component with reference {component_ref}.")
logger.debug(f"Found component with reference {component_ref}.")
return symbol
print(f"Component with reference {component_ref} not found.")
logger.warning(f"Component with reference {component_ref} not found.")
return None
@staticmethod
@@ -339,13 +338,13 @@ class ComponentManager:
)
):
matching_components.append(symbol)
print(f"Found {len(matching_components)} components matching query '{query}'.")
logger.debug(f"Found {len(matching_components)} components matching query '{query}'.")
return matching_components
@staticmethod
def get_all_components(schematic: Schematic):
"""Get all components in schematic"""
print(f"Retrieving all {len(schematic.symbol)} components.")
logger.debug(f"Retrieving all {len(schematic.symbol)} components.")
return list(schematic.symbol)