Source code for circuit_synth.core.component_replacement

"""
Bulk component replacement utilities.

Provides functions to replace/substitute components across circuits based on
property values, manufacturers, or other criteria.

Example:
    >>> import circuit_synth as cs
    >>>
    >>> circuit = cs.Circuit("MyDesign")
    >>> # ... add components with MPN properties ...
    >>>
    >>> # Replace all components with specific MPN
    >>> result = cs.replace_components(
    ...     circuit,
    ...     match={"MPN": "ASD123"},
    ...     update={"MPN": "FGS032", "Manufacturer": "NewCo"}
    ... )
    >>> print(f"Replaced {result.count} components")
"""

from dataclasses import dataclass
from typing import Dict, List, Optional

from .circuit import Circuit


[docs] @dataclass class ReplacementResult: """ Results from bulk component replacement operation. Attributes: count: Number of components modified affected_components: List of component references that were modified warnings: List of warning messages errors: List of error messages """ count: int affected_components: List[str] warnings: List[str] errors: List[str]
[docs] def __str__(self) -> str: """Format result as string.""" result = f"Replaced {self.count} components" if self.affected_components: result += f": {', '.join(self.affected_components)}" if self.warnings: result += f"\nWarnings: {len(self.warnings)}" if self.errors: result += f"\nErrors: {len(self.errors)}" return result
[docs] def replace_components( circuit: Circuit, match: Dict[str, str], update: Dict[str, str], dry_run: bool = False, ) -> ReplacementResult: """ Replace property values for components matching criteria. Finds all components with properties matching the `match` criteria and updates their properties with values from `update`. Args: circuit: Circuit to modify match: Property criteria to match (e.g., {"MPN": "ASD123"}) update: Properties to update (e.g., {"MPN": "FGS032", "Manufacturer": "NewCo"}) dry_run: If True, don't actually modify, just return what would change Returns: ReplacementResult with count and list of affected components Example: >>> circuit = cs.Circuit("Test") >>> r1 = cs.Component("Device:R", ref="R", value="10k") >>> r1.MPN = "ASD123" >>> circuit.add_component(r1) >>> circuit.finalize_references() >>> >>> # Replace MPN >>> result = cs.replace_components( ... circuit, ... match={"MPN": "ASD123"}, ... update={"MPN": "FGS032"} ... ) >>> print(result.count) # 1 >>> print(r1.MPN) # "FGS032" """ warnings = [] errors = [] affected = [] count = 0 # Iterate through all components for comp in circuit.components.values(): # Check if component matches all criteria matches = True for prop_name, prop_value in match.items(): # Check if component has this property if not hasattr(comp, prop_name): matches = False break # Check if value matches comp_value = getattr(comp, prop_name, None) if comp_value != prop_value: matches = False break # If component matches, update properties if matches: if not dry_run: for prop_name, new_value in update.items(): try: setattr(comp, prop_name, new_value) except Exception as e: errors.append( f"Failed to update {prop_name} on {comp.ref}: {str(e)}" ) affected.append(comp.ref) count += 1 return ReplacementResult( count=count, affected_components=affected, warnings=warnings, errors=errors )
[docs] def replace_multiple( circuit: Circuit, replacements: List[Dict[str, any]], dry_run: bool = False, ) -> ReplacementResult: """ Apply multiple replacement operations in sequence. Each replacement in the list should have 'match' and 'update' dicts. Args: circuit: Circuit to modify replacements: List of replacement specs, each with 'match' and 'update' dry_run: If True, don't actually modify Returns: Combined ReplacementResult from all operations Example: >>> replacements = [ ... { ... "match": {"MPN": "ASD123"}, ... "update": {"MPN": "FGS032", "Manufacturer": "NewCo"} ... }, ... { ... "match": {"MPN": "OLD456"}, ... "update": {"MPN": "NEW789"} ... } ... ] >>> result = cs.replace_multiple(circuit, replacements) >>> print(f"Total replaced: {result.count}") """ total_count = 0 all_affected = [] all_warnings = [] all_errors = [] for replacement_spec in replacements: match = replacement_spec.get("match", {}) update = replacement_spec.get("update", {}) if not match or not update: all_warnings.append( f"Skipping invalid replacement spec: {replacement_spec}" ) continue result = replace_components(circuit, match, update, dry_run=dry_run) total_count += result.count all_affected.extend(result.affected_components) all_warnings.extend(result.warnings) all_errors.extend(result.errors) return ReplacementResult( count=total_count, affected_components=all_affected, warnings=all_warnings, errors=all_errors, )
[docs] def find_replaceable_components( circuit: Circuit, match: Dict[str, str] ) -> List[str]: """ Find components that match criteria without modifying them. Useful for previewing what would be affected by a replacement operation. Args: circuit: Circuit to search match: Property criteria to match Returns: List of component references that match criteria Example: >>> # Find all components with specific MPN >>> matches = cs.find_replaceable_components( ... circuit, ... match={"MPN": "ASD123"} ... ) >>> print(f"Would affect: {matches}") """ result = replace_components(circuit, match, update={}, dry_run=True) return result.affected_components