kicad-sch-api 0.4.4__py3-none-any.whl → 0.4.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kicad-sch-api might be problematic. Click here for more details.

kicad_sch_api/__init__.py CHANGED
@@ -53,6 +53,12 @@ from .core.config import KiCADConfig, config
53
53
  from .core.schematic import Schematic
54
54
  from .library.cache import SymbolLibraryCache, get_symbol_cache
55
55
  from .utils.validation import ValidationError, ValidationIssue
56
+ # Commonly-used exceptions (ValidationError re-exported from utils for backward compat)
57
+ from .core.exceptions import (
58
+ KiCadSchError,
59
+ ElementNotFoundError,
60
+ DuplicateElementError,
61
+ )
56
62
 
57
63
  # Version info
58
64
  VERSION_INFO = (0, 4, 0)
@@ -69,8 +75,11 @@ __all__ = [
69
75
  "KiCADConfig",
70
76
  "config",
71
77
  # Exceptions
78
+ "KiCadSchError",
72
79
  "ValidationError",
73
80
  "ValidationIssue",
81
+ "ElementNotFoundError",
82
+ "DuplicateElementError",
74
83
  # Version info
75
84
  "__version__",
76
85
  "VERSION_INFO",
@@ -5,6 +5,24 @@ from .formatter import ExactFormatter
5
5
  from .parser import SExpressionParser
6
6
  from .schematic import Schematic, create_schematic, load_schematic
7
7
  from .types import Junction, Label, Net, Point, SchematicSymbol, Wire
8
+ # Exception hierarchy
9
+ from .exceptions import (
10
+ KiCadSchError,
11
+ ValidationError,
12
+ ReferenceError,
13
+ LibraryError,
14
+ GeometryError,
15
+ NetError,
16
+ ParseError,
17
+ FormatError,
18
+ CollectionError,
19
+ ElementNotFoundError,
20
+ DuplicateElementError,
21
+ CollectionOperationError,
22
+ FileOperationError,
23
+ CLIError,
24
+ SchematicStateError,
25
+ )
8
26
 
9
27
  __all__ = [
10
28
  "Schematic",
@@ -20,4 +38,20 @@ __all__ = [
20
38
  "ExactFormatter",
21
39
  "load_schematic",
22
40
  "create_schematic",
41
+ # Exceptions
42
+ "KiCadSchError",
43
+ "ValidationError",
44
+ "ReferenceError",
45
+ "LibraryError",
46
+ "GeometryError",
47
+ "NetError",
48
+ "ParseError",
49
+ "FormatError",
50
+ "CollectionError",
51
+ "ElementNotFoundError",
52
+ "DuplicateElementError",
53
+ "CollectionOperationError",
54
+ "FileOperationError",
55
+ "CLIError",
56
+ "SchematicStateError",
23
57
  ]
@@ -0,0 +1,175 @@
1
+ """
2
+ Exception hierarchy for kicad-sch-api.
3
+
4
+ Provides a structured exception hierarchy for better error handling and debugging.
5
+ All exceptions inherit from the base KiCadSchError class.
6
+ """
7
+
8
+ from typing import Any, List, Optional, TYPE_CHECKING
9
+
10
+ # Import validation types for type hints
11
+ # ValidationLevel is imported at runtime in methods that need it
12
+ if TYPE_CHECKING:
13
+ from ..utils.validation import ValidationIssue
14
+
15
+
16
+ class KiCadSchError(Exception):
17
+ """Base exception for all kicad-sch-api errors."""
18
+
19
+ pass
20
+
21
+
22
+ class ValidationError(KiCadSchError):
23
+ """
24
+ Raised when validation fails.
25
+
26
+ Supports rich error context with field/value information and can collect
27
+ multiple validation issues.
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ message: str,
33
+ issues: Optional[List["ValidationIssue"]] = None,
34
+ field: str = "",
35
+ value: Any = None,
36
+ ):
37
+ """
38
+ Initialize validation error with context.
39
+
40
+ Args:
41
+ message: Error message describing the validation failure
42
+ issues: List of validation issues (for collecting multiple errors)
43
+ field: The field name that failed validation
44
+ value: The invalid value that was provided
45
+ """
46
+ self.issues = issues or []
47
+ self.field = field
48
+ self.value = value
49
+ super().__init__(message)
50
+
51
+ def add_issue(self, issue: "ValidationIssue") -> None:
52
+ """Add a validation issue to this error."""
53
+ self.issues.append(issue)
54
+
55
+ def get_errors(self) -> List["ValidationIssue"]:
56
+ """Get only error-level issues."""
57
+ # Import here to avoid circular dependency
58
+ from ..utils.validation import ValidationLevel
59
+
60
+ return [
61
+ issue
62
+ for issue in self.issues
63
+ if hasattr(issue, 'level') and issue.level in (ValidationLevel.ERROR, ValidationLevel.CRITICAL)
64
+ ]
65
+
66
+ def get_warnings(self) -> List["ValidationIssue"]:
67
+ """Get only warning-level issues."""
68
+ # Import here to avoid circular dependency
69
+ from ..utils.validation import ValidationLevel
70
+
71
+ return [issue for issue in self.issues if hasattr(issue, 'level') and issue.level == ValidationLevel.WARNING]
72
+
73
+
74
+ class ReferenceError(ValidationError):
75
+ """Raised when a component reference is invalid."""
76
+
77
+ pass
78
+
79
+
80
+ class LibraryError(ValidationError):
81
+ """Raised when a library or symbol reference is invalid."""
82
+
83
+ pass
84
+
85
+
86
+ class GeometryError(ValidationError):
87
+ """Raised when geometry validation fails (positions, shapes, dimensions)."""
88
+
89
+ pass
90
+
91
+
92
+ class NetError(ValidationError):
93
+ """Raised when a net specification or operation is invalid."""
94
+
95
+ pass
96
+
97
+
98
+ class ParseError(KiCadSchError):
99
+ """Raised when parsing a schematic file fails."""
100
+
101
+ pass
102
+
103
+
104
+ class FormatError(KiCadSchError):
105
+ """Raised when formatting a schematic file fails."""
106
+
107
+ pass
108
+
109
+
110
+ class CollectionError(KiCadSchError):
111
+ """Raised when a collection operation fails."""
112
+
113
+ pass
114
+
115
+
116
+ class ElementNotFoundError(CollectionError):
117
+ """Raised when an element is not found in a collection."""
118
+
119
+ def __init__(self, message: str, element_type: str = "", identifier: str = ""):
120
+ """
121
+ Initialize element not found error.
122
+
123
+ Args:
124
+ message: Error message
125
+ element_type: Type of element (e.g., 'component', 'wire', 'junction')
126
+ identifier: The identifier used to search (e.g., 'R1', UUID)
127
+ """
128
+ self.element_type = element_type
129
+ self.identifier = identifier
130
+ super().__init__(message)
131
+
132
+
133
+ class DuplicateElementError(CollectionError):
134
+ """Raised when attempting to add a duplicate element."""
135
+
136
+ def __init__(self, message: str, element_type: str = "", identifier: str = ""):
137
+ """
138
+ Initialize duplicate element error.
139
+
140
+ Args:
141
+ message: Error message
142
+ element_type: Type of element (e.g., 'component', 'wire', 'junction')
143
+ identifier: The duplicate identifier (e.g., 'R1', UUID)
144
+ """
145
+ self.element_type = element_type
146
+ self.identifier = identifier
147
+ super().__init__(message)
148
+
149
+
150
+ class CollectionOperationError(CollectionError):
151
+ """Raised when a collection operation fails for reasons other than not found/duplicate."""
152
+
153
+ pass
154
+
155
+
156
+ class FileOperationError(KiCadSchError):
157
+ """Raised when a file I/O operation fails."""
158
+
159
+ pass
160
+
161
+
162
+ class CLIError(KiCadSchError):
163
+ """Raised when KiCad CLI execution fails."""
164
+
165
+ pass
166
+
167
+
168
+ class SchematicStateError(KiCadSchError):
169
+ """
170
+ Raised when an operation requires specific schematic state.
171
+
172
+ Examples: schematic must be saved before export, etc.
173
+ """
174
+
175
+ pass
@@ -5,6 +5,7 @@ This package contains specialized managers for different aspects of schematic
5
5
  manipulation, enabling clean separation of concerns and better maintainability.
6
6
  """
7
7
 
8
+ from .base import BaseManager
8
9
  from .file_io import FileIOManager
9
10
  from .format_sync import FormatSyncManager
10
11
  from .graphics import GraphicsManager
@@ -15,6 +16,7 @@ from .validation import ValidationManager
15
16
  from .wire import WireManager
16
17
 
17
18
  __all__ = [
19
+ "BaseManager",
18
20
  "FileIOManager",
19
21
  "FormatSyncManager",
20
22
  "GraphicsManager",
@@ -0,0 +1,76 @@
1
+ """Base manager class for schematic operations.
2
+
3
+ Provides a consistent interface for all manager classes and enforces
4
+ common patterns for validation and data access.
5
+ """
6
+
7
+ from abc import ABC
8
+ from typing import Any, Dict, List, Optional, TYPE_CHECKING
9
+
10
+ from ...utils.validation import ValidationIssue
11
+
12
+ if TYPE_CHECKING:
13
+ from ..schematic import Schematic
14
+
15
+
16
+ class BaseManager(ABC):
17
+ """Base class for all schematic managers.
18
+
19
+ Managers encapsulate complex operations and keep Schematic focused.
20
+ This base class provides a consistent interface and common utilities
21
+ for all managers.
22
+
23
+ Attributes:
24
+ _data: Reference to schematic data (optional, varies by manager)
25
+ _schematic: Reference to parent Schematic instance (optional)
26
+ """
27
+
28
+ def __init__(self, schematic_data: Optional[Dict[str, Any]] = None, **kwargs):
29
+ """Initialize manager with schematic data reference.
30
+
31
+ Args:
32
+ schematic_data: Reference to schematic data dictionary (optional)
33
+ **kwargs: Additional manager-specific parameters
34
+ """
35
+ self._data = schematic_data
36
+ self._schematic: Optional["Schematic"] = None
37
+
38
+ def set_schematic(self, schematic: "Schematic") -> None:
39
+ """Set reference to parent schematic.
40
+
41
+ This is called by Schematic after manager initialization to establish
42
+ bidirectional relationship.
43
+
44
+ Args:
45
+ schematic: The parent Schematic instance
46
+ """
47
+ self._schematic = schematic
48
+
49
+ @property
50
+ def schematic(self) -> Optional["Schematic"]:
51
+ """Get the parent schematic instance.
52
+
53
+ Returns:
54
+ The parent Schematic, or None if not set
55
+ """
56
+ return self._schematic
57
+
58
+ @property
59
+ def data(self) -> Optional[Dict[str, Any]]:
60
+ """Get the schematic data dictionary.
61
+
62
+ Returns:
63
+ The schematic data, or None if not set
64
+ """
65
+ return self._data
66
+
67
+ def validate(self) -> List[ValidationIssue]:
68
+ """Validate managed elements.
69
+
70
+ This is an optional method that managers can override to provide
71
+ validation. Default implementation returns empty list (no issues).
72
+
73
+ Returns:
74
+ List of validation issues found
75
+ """
76
+ return []
@@ -14,11 +14,12 @@ from ...utils.validation import ValidationError
14
14
  from ..config import config
15
15
  from ..formatter import ExactFormatter
16
16
  from ..parser import SExpressionParser
17
+ from .base import BaseManager
17
18
 
18
19
  logger = logging.getLogger(__name__)
19
20
 
20
21
 
21
- class FileIOManager:
22
+ class FileIOManager(BaseManager):
22
23
  """
23
24
  Manages file I/O operations for KiCAD schematics.
24
25
 
@@ -31,6 +32,7 @@ class FileIOManager:
31
32
 
32
33
  def __init__(self):
33
34
  """Initialize the FileIOManager."""
35
+ super().__init__()
34
36
  self._parser = SExpressionParser(preserve_format=True)
35
37
  self._formatter = ExactFormatter()
36
38
 
@@ -11,11 +11,12 @@ from typing import Any, Dict, List, Optional, Set, Union
11
11
 
12
12
  from ..components import Component
13
13
  from ..types import Point, Wire
14
+ from .base import BaseManager
14
15
 
15
16
  logger = logging.getLogger(__name__)
16
17
 
17
18
 
18
- class FormatSyncManager:
19
+ class FormatSyncManager(BaseManager):
19
20
  """
20
21
  Manages synchronization between object models and S-expression data.
21
22
 
@@ -34,7 +35,7 @@ class FormatSyncManager:
34
35
  Args:
35
36
  schematic_data: Reference to schematic data
36
37
  """
37
- self._data = schematic_data
38
+ super().__init__(schematic_data)
38
39
  self._dirty_flags: Set[str] = set()
39
40
  self._change_log: List[Dict[str, Any]] = []
40
41
  self._sync_lock = False
@@ -11,11 +11,12 @@ import uuid
11
11
  from typing import Any, Dict, List, Optional, Tuple, Union
12
12
 
13
13
  from ..types import Point
14
+ from .base import BaseManager
14
15
 
15
16
  logger = logging.getLogger(__name__)
16
17
 
17
18
 
18
- class GraphicsManager:
19
+ class GraphicsManager(BaseManager):
19
20
  """
20
21
  Manages graphic elements and drawing shapes in KiCAD schematics.
21
22
 
@@ -34,7 +35,7 @@ class GraphicsManager:
34
35
  Args:
35
36
  schematic_data: Reference to schematic data
36
37
  """
37
- self._data = schematic_data
38
+ super().__init__(schematic_data)
38
39
 
39
40
  def add_rectangle(
40
41
  self,
@@ -8,10 +8,12 @@ paper size, title block, version information, and instance sections.
8
8
  import logging
9
9
  from typing import Any, Dict, List, Optional
10
10
 
11
+ from .base import BaseManager
12
+
11
13
  logger = logging.getLogger(__name__)
12
14
 
13
15
 
14
- class MetadataManager:
16
+ class MetadataManager(BaseManager):
15
17
  """
16
18
  Manages schematic metadata and configuration settings.
17
19
 
@@ -30,7 +32,7 @@ class MetadataManager:
30
32
  Args:
31
33
  schematic_data: Reference to schematic data dictionary
32
34
  """
33
- self._data = schematic_data
35
+ super().__init__(schematic_data)
34
36
 
35
37
  def set_paper_size(self, paper: str) -> None:
36
38
  """
@@ -11,11 +11,12 @@ from pathlib import Path
11
11
  from typing import Any, Dict, List, Optional, Tuple, Union
12
12
 
13
13
  from ..types import Point
14
+ from .base import BaseManager
14
15
 
15
16
  logger = logging.getLogger(__name__)
16
17
 
17
18
 
18
- class SheetManager:
19
+ class SheetManager(BaseManager):
19
20
  """
20
21
  Manages hierarchical sheets and multi-sheet project coordination.
21
22
 
@@ -34,7 +35,7 @@ class SheetManager:
34
35
  Args:
35
36
  schematic_data: Reference to schematic data
36
37
  """
37
- self._data = schematic_data
38
+ super().__init__(schematic_data)
38
39
 
39
40
  def add_sheet(
40
41
  self,
@@ -11,11 +11,12 @@ import uuid
11
11
  from typing import Any, Dict, List, Optional, Tuple, Union
12
12
 
13
13
  from ..types import Point
14
+ from .base import BaseManager
14
15
 
15
16
  logger = logging.getLogger(__name__)
16
17
 
17
18
 
18
- class TextElementManager:
19
+ class TextElementManager(BaseManager):
19
20
  """
20
21
  Manages text elements and labeling in KiCAD schematics.
21
22
 
@@ -34,7 +35,7 @@ class TextElementManager:
34
35
  Args:
35
36
  schematic_data: Reference to schematic data
36
37
  """
37
- self._data = schematic_data
38
+ super().__init__(schematic_data)
38
39
 
39
40
  def add_label(
40
41
  self,
@@ -11,11 +11,12 @@ from typing import Any, Dict, List, Optional, Set, Tuple
11
11
 
12
12
  from ...utils.validation import ValidationError, ValidationIssue
13
13
  from ..types import Point
14
+ from .base import BaseManager
14
15
 
15
16
  logger = logging.getLogger(__name__)
16
17
 
17
18
 
18
- class ValidationManager:
19
+ class ValidationManager(BaseManager):
19
20
  """
20
21
  Comprehensive validation manager for schematic integrity.
21
22
 
@@ -39,7 +40,7 @@ class ValidationManager:
39
40
  component_collection: Component collection for validation
40
41
  wire_collection: Wire collection for connectivity analysis
41
42
  """
42
- self._data = schematic_data
43
+ super().__init__(schematic_data)
43
44
  self._components = component_collection
44
45
  self._wires = wire_collection
45
46
  self._validation_rules = self._initialize_validation_rules()
@@ -12,11 +12,12 @@ from typing import Any, Dict, List, Optional, Tuple, Union
12
12
  from ...library.cache import get_symbol_cache
13
13
  from ..types import Point, Wire, WireType
14
14
  from ..wires import WireCollection
15
+ from .base import BaseManager
15
16
 
16
17
  logger = logging.getLogger(__name__)
17
18
 
18
19
 
19
- class WireManager:
20
+ class WireManager(BaseManager):
20
21
  """
21
22
  Manages wire operations and pin connectivity in KiCAD schematics.
22
23
 
@@ -39,7 +40,7 @@ class WireManager:
39
40
  wire_collection: Wire collection for management
40
41
  component_collection: Component collection for pin lookups
41
42
  """
42
- self._data = schematic_data
43
+ super().__init__(schematic_data)
43
44
  self._wires = wire_collection
44
45
  self._components = component_collection
45
46
  self._symbol_cache = get_symbol_cache()
@@ -1216,7 +1216,33 @@ class Schematic:
1216
1216
  # Internal sync methods (migrated from original implementation)
1217
1217
  def _sync_components_to_data(self):
1218
1218
  """Sync component collection state back to data structure."""
1219
- self._data["components"] = [comp._data.__dict__ for comp in self._components]
1219
+ logger.debug("🔍 _sync_components_to_data: Syncing components to _data")
1220
+
1221
+ components_data = []
1222
+ for comp in self._components:
1223
+ # Start with base component data
1224
+ comp_dict = {k: v for k, v in comp._data.__dict__.items() if not k.startswith('_')}
1225
+
1226
+ # CRITICAL FIX: Explicitly preserve instances if user set them
1227
+ if hasattr(comp._data, 'instances') and comp._data.instances:
1228
+ logger.debug(f" Component {comp._data.reference} has {len(comp._data.instances)} instance(s)")
1229
+ comp_dict['instances'] = [
1230
+ {
1231
+ 'project': getattr(inst, 'project', self.name) if hasattr(inst, 'project') else self.name,
1232
+ 'path': inst.path, # PRESERVE exact path user set!
1233
+ 'reference': inst.reference,
1234
+ 'unit': inst.unit,
1235
+ }
1236
+ for inst in comp._data.instances
1237
+ ]
1238
+ logger.debug(f" Instance paths: {[inst.path for inst in comp._data.instances]}")
1239
+ else:
1240
+ logger.debug(f" Component {comp._data.reference} has NO instances (will be generated by parser)")
1241
+
1242
+ components_data.append(comp_dict)
1243
+
1244
+ self._data["components"] = components_data
1245
+ logger.debug(f" Synced {len(components_data)} components to _data")
1220
1246
 
1221
1247
  # Populate lib_symbols with actual symbol definitions used by components
1222
1248
  lib_symbols = {}
@@ -171,6 +171,7 @@ class SchematicSymbol:
171
171
  in_bom: bool = True
172
172
  on_board: bool = True
173
173
  unit: int = 1
174
+ instances: List["SymbolInstance"] = field(default_factory=list) # FIX: Add instances field for hierarchical support
174
175
 
175
176
  def __post_init__(self) -> None:
176
177
  # Generate UUID if not provided
@@ -184,53 +184,83 @@ class SymbolParser(BaseElementParser):
184
184
  # Add instances section (required by KiCAD)
185
185
  from ...core.config import config
186
186
 
187
- # Get project name from config or properties
188
- project_name = symbol_data.get("properties", {}).get("project_name")
189
- if not project_name:
190
- project_name = getattr(self, "project_name", config.defaults.project_name)
191
-
192
- # CRITICAL FIX: Use the FULL hierarchy_path from properties if available
193
- # For hierarchical schematics, this contains the complete path: /root_uuid/sheet_symbol_uuid/...
194
- # This ensures KiCad can properly annotate components in sub-sheets
195
- hierarchy_path = symbol_data.get("properties", {}).get("hierarchy_path")
196
- if hierarchy_path:
197
- # Use the full hierarchical path (includes root + all sheet symbols)
198
- instance_path = hierarchy_path
199
- logger.debug(
200
- f"🔧 Using FULL hierarchy_path: {instance_path} for component {symbol_data.get('reference', 'unknown')}"
201
- )
187
+ # HIERARCHICAL FIX: Check if user explicitly set instances
188
+ # If so, preserve them exactly as-is (don't generate!)
189
+ user_instances = symbol_data.get("instances")
190
+ if user_instances:
191
+ logger.debug(f"🔍 HIERARCHICAL FIX: Component {symbol_data.get('reference')} has {len(user_instances)} user-set instance(s)")
192
+ # Build instances sexp from user data
193
+ instances_sexp = [sexpdata.Symbol("instances")]
194
+ for inst in user_instances:
195
+ project = inst.get('project', getattr(self, 'project_name', 'circuit'))
196
+ path = inst.get('path', '/')
197
+ reference = inst.get('reference', symbol_data.get('reference', 'U?'))
198
+ unit = inst.get('unit', 1)
199
+
200
+ logger.debug(f" Instance: project={project}, path={path}, ref={reference}, unit={unit}")
201
+
202
+ instances_sexp.append([
203
+ sexpdata.Symbol("project"),
204
+ project,
205
+ [
206
+ sexpdata.Symbol("path"),
207
+ path, # PRESERVE user-set hierarchical path!
208
+ [sexpdata.Symbol("reference"), reference],
209
+ [sexpdata.Symbol("unit"), unit],
210
+ ],
211
+ ])
212
+ sexp.append(instances_sexp)
202
213
  else:
203
- # Fallback: use root_uuid or schematic_uuid for flat designs
204
- root_uuid = (
205
- symbol_data.get("properties", {}).get("root_uuid")
206
- or schematic_uuid
207
- or str(uuid.uuid4())
208
- )
209
- instance_path = f"/{root_uuid}"
214
+ # No user-set instances - generate default (backward compatibility)
215
+ logger.debug(f"🔍 HIERARCHICAL FIX: Component {symbol_data.get('reference')} has NO user instances, generating default")
216
+
217
+ # Get project name from config or properties
218
+ project_name = symbol_data.get("properties", {}).get("project_name")
219
+ if not project_name:
220
+ project_name = getattr(self, "project_name", config.defaults.project_name)
221
+
222
+ # CRITICAL FIX: Use the FULL hierarchy_path from properties if available
223
+ # For hierarchical schematics, this contains the complete path: /root_uuid/sheet_symbol_uuid/...
224
+ # This ensures KiCad can properly annotate components in sub-sheets
225
+ hierarchy_path = symbol_data.get("properties", {}).get("hierarchy_path")
226
+ if hierarchy_path:
227
+ # Use the full hierarchical path (includes root + all sheet symbols)
228
+ instance_path = hierarchy_path
229
+ logger.debug(
230
+ f"🔧 Using FULL hierarchy_path: {instance_path} for component {symbol_data.get('reference', 'unknown')}"
231
+ )
232
+ else:
233
+ # Fallback: use root_uuid or schematic_uuid for flat designs
234
+ root_uuid = (
235
+ symbol_data.get("properties", {}).get("root_uuid")
236
+ or schematic_uuid
237
+ or str(uuid.uuid4())
238
+ )
239
+ instance_path = f"/{root_uuid}"
240
+ logger.debug(
241
+ f"🔧 Using root UUID path: {instance_path} for component {symbol_data.get('reference', 'unknown')}"
242
+ )
243
+
210
244
  logger.debug(
211
- f"🔧 Using root UUID path: {instance_path} for component {symbol_data.get('reference', 'unknown')}"
245
+ f"🔧 Component properties keys: {list(symbol_data.get('properties', {}).keys())}"
212
246
  )
247
+ logger.debug(f"🔧 Using project name: '{project_name}'")
213
248
 
214
- logger.debug(
215
- f"🔧 Component properties keys: {list(symbol_data.get('properties', {}).keys())}"
216
- )
217
- logger.debug(f"🔧 Using project name: '{project_name}'")
218
-
219
- sexp.append(
220
- [
221
- sexpdata.Symbol("instances"),
249
+ sexp.append(
222
250
  [
223
- sexpdata.Symbol("project"),
224
- project_name,
251
+ sexpdata.Symbol("instances"),
225
252
  [
226
- sexpdata.Symbol("path"),
227
- instance_path,
228
- [sexpdata.Symbol("reference"), symbol_data.get("reference", "U?")],
229
- [sexpdata.Symbol("unit"), symbol_data.get("unit", 1)],
253
+ sexpdata.Symbol("project"),
254
+ project_name,
255
+ [
256
+ sexpdata.Symbol("path"),
257
+ instance_path,
258
+ [sexpdata.Symbol("reference"), symbol_data.get("reference", "U?")],
259
+ [sexpdata.Symbol("unit"), symbol_data.get("unit", 1)],
260
+ ],
230
261
  ],
231
- ],
232
- ]
233
- )
262
+ ]
263
+ )
234
264
 
235
265
  return sexp
236
266
 
@@ -11,8 +11,21 @@ from dataclasses import dataclass
11
11
  from enum import Enum
12
12
  from typing import Any, Dict, List, Optional, Set, Union
13
13
 
14
+ # Import ValidationError from new exception hierarchy for backward compatibility
15
+ from ..core.exceptions import ValidationError
16
+
14
17
  logger = logging.getLogger(__name__)
15
18
 
19
+ # Export list for public API
20
+ __all__ = [
21
+ 'ValidationError',
22
+ 'ValidationIssue',
23
+ 'ValidationLevel',
24
+ 'SchematicValidator',
25
+ 'validate_schematic_file',
26
+ 'collect_validation_errors',
27
+ ]
28
+
16
29
 
17
30
  class ValidationLevel(Enum):
18
31
  """Validation issue severity levels."""
@@ -43,28 +56,9 @@ class ValidationIssue:
43
56
  return f"{self.level.value.upper()}: {self.category}: {self.message}{context_str}{suggestion_str}"
44
57
 
45
58
 
46
- class ValidationError(Exception):
47
- """Exception raised when critical validation errors are found."""
48
-
49
- def __init__(self, message: str, issues: List[ValidationIssue] = None):
50
- super().__init__(message)
51
- self.issues = issues or []
52
-
53
- def add_issue(self, issue: ValidationIssue):
54
- """Add a validation issue to this error."""
55
- self.issues.append(issue)
56
-
57
- def get_errors(self) -> List[ValidationIssue]:
58
- """Get only error-level issues."""
59
- return [
60
- issue
61
- for issue in self.issues
62
- if issue.level in (ValidationLevel.ERROR, ValidationLevel.CRITICAL)
63
- ]
64
-
65
- def get_warnings(self) -> List[ValidationIssue]:
66
- """Get only warning-level issues."""
67
- return [issue for issue in self.issues if issue.level == ValidationLevel.WARNING]
59
+ # ValidationError is now imported from core.exceptions (see imports at top)
60
+ # This provides backward compatibility for existing code while using the new
61
+ # exception hierarchy
68
62
 
69
63
 
70
64
  class SchematicValidator:
@@ -0,0 +1,14 @@
1
+ """
2
+ Wrapper classes for schematic elements.
3
+
4
+ Provides enhanced element access with validation, parent tracking,
5
+ and automatic change notification.
6
+ """
7
+
8
+ from .base import ElementWrapper
9
+ from .wire import WireWrapper
10
+
11
+ __all__ = [
12
+ "ElementWrapper",
13
+ "WireWrapper",
14
+ ]
@@ -0,0 +1,89 @@
1
+ """Base wrapper class for schematic elements."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import TYPE_CHECKING, Any, Generic, Optional, TypeVar
5
+
6
+ if TYPE_CHECKING:
7
+ from ..collections.base import IndexedCollection
8
+
9
+ # Type variable for the wrapped data type
10
+ T = TypeVar("T")
11
+
12
+
13
+ class ElementWrapper(ABC, Generic[T]):
14
+ """Base class for all schematic element wrappers.
15
+
16
+ Wrappers enhance raw dataclasses with:
17
+ - Validation on property setters
18
+ - Parent collection tracking for automatic index updates
19
+ - Convenient methods and computed properties
20
+ - Consistent API across different element types
21
+ """
22
+
23
+ def __init__(self, data: T, parent_collection: Optional["IndexedCollection[Any]"]):
24
+ """Initialize the wrapper.
25
+
26
+ Args:
27
+ data: The underlying dataclass instance
28
+ parent_collection: The collection this element belongs to (can be None)
29
+ """
30
+ self._data = data
31
+ self._collection = parent_collection
32
+
33
+ @property
34
+ def data(self) -> T:
35
+ """Get the underlying data object.
36
+
37
+ Returns:
38
+ The wrapped dataclass instance
39
+ """
40
+ return self._data
41
+
42
+ @property
43
+ @abstractmethod
44
+ def uuid(self) -> str:
45
+ """Get the UUID of the element.
46
+
47
+ Returns:
48
+ UUID string
49
+ """
50
+ pass
51
+
52
+ def __eq__(self, other: object) -> bool:
53
+ """Compare wrappers by UUID.
54
+
55
+ Args:
56
+ other: Another wrapper to compare with
57
+
58
+ Returns:
59
+ True if UUIDs match
60
+ """
61
+ if not isinstance(other, ElementWrapper):
62
+ return False
63
+ return self.uuid == other.uuid
64
+
65
+ def __hash__(self) -> int:
66
+ """Hash wrapper by UUID.
67
+
68
+ Returns:
69
+ Hash of UUID
70
+ """
71
+ return hash(self.uuid)
72
+
73
+ def __repr__(self) -> str:
74
+ """Get string representation of wrapper.
75
+
76
+ Returns:
77
+ String representation
78
+ """
79
+ return f"{self.__class__.__name__}({self._data})"
80
+
81
+ def _mark_modified(self) -> None:
82
+ """Mark the parent collection as modified."""
83
+ if self._collection is not None:
84
+ self._collection._mark_modified()
85
+
86
+ def _invalidate_indexes(self) -> None:
87
+ """Invalidate parent collection indexes."""
88
+ if self._collection is not None:
89
+ self._collection._dirty_indexes = True
@@ -0,0 +1,198 @@
1
+ """Wire wrapper class for enhanced wire manipulation."""
2
+
3
+ from typing import TYPE_CHECKING, List, Optional
4
+
5
+ from ..core.types import Point, Wire, WireType
6
+ from .base import ElementWrapper
7
+
8
+ if TYPE_CHECKING:
9
+ from ..collections.wires import WireCollection
10
+
11
+
12
+ class WireWrapper(ElementWrapper[Wire]):
13
+ """Enhanced wrapper for Wire with validation and parent tracking.
14
+
15
+ Provides:
16
+ - Validation on property setters (e.g., minimum 2 points for wires)
17
+ - Automatic parent collection notification on changes
18
+ - Convenient access to wire properties
19
+ - Type-safe operations
20
+ """
21
+
22
+ def __init__(self, wire: Wire, parent_collection: Optional["WireCollection"] = None):
23
+ """Initialize wire wrapper.
24
+
25
+ Args:
26
+ wire: The underlying Wire dataclass
27
+ parent_collection: Parent collection for modification tracking (optional)
28
+ """
29
+ super().__init__(wire, parent_collection)
30
+
31
+ @property
32
+ def uuid(self) -> str:
33
+ """Get wire UUID.
34
+
35
+ Returns:
36
+ Wire UUID string
37
+ """
38
+ return self._data.uuid
39
+
40
+ @property
41
+ def points(self) -> List[Point]:
42
+ """Get wire points.
43
+
44
+ Returns:
45
+ List of Point objects defining the wire path
46
+ """
47
+ return self._data.points
48
+
49
+ @points.setter
50
+ def points(self, value: List[Point]) -> None:
51
+ """Set wire points with validation.
52
+
53
+ Args:
54
+ value: List of points (must have at least 2 points)
55
+
56
+ Raises:
57
+ ValueError: If less than 2 points provided
58
+ """
59
+ if len(value) < 2:
60
+ raise ValueError("Wire must have at least 2 points")
61
+
62
+ # Create new Wire with updated points
63
+ self._data = Wire(
64
+ uuid=self._data.uuid,
65
+ points=value,
66
+ wire_type=self._data.wire_type,
67
+ stroke_width=self._data.stroke_width,
68
+ stroke_type=self._data.stroke_type,
69
+ )
70
+ self._mark_modified()
71
+
72
+ @property
73
+ def start(self) -> Point:
74
+ """Get start point (first point of wire).
75
+
76
+ Returns:
77
+ First point in the wire path
78
+ """
79
+ return self._data.points[0]
80
+
81
+ @property
82
+ def end(self) -> Point:
83
+ """Get end point (last point of wire).
84
+
85
+ Returns:
86
+ Last point in the wire path
87
+ """
88
+ return self._data.points[-1]
89
+
90
+ @property
91
+ def wire_type(self) -> WireType:
92
+ """Get wire type (WIRE or BUS).
93
+
94
+ Returns:
95
+ WireType enum value
96
+ """
97
+ return self._data.wire_type
98
+
99
+ @wire_type.setter
100
+ def wire_type(self, value: WireType) -> None:
101
+ """Set wire type.
102
+
103
+ Args:
104
+ value: WireType enum value (WIRE or BUS)
105
+ """
106
+ self._data = Wire(
107
+ uuid=self._data.uuid,
108
+ points=self._data.points,
109
+ wire_type=value,
110
+ stroke_width=self._data.stroke_width,
111
+ stroke_type=self._data.stroke_type,
112
+ )
113
+ self._mark_modified()
114
+
115
+ @property
116
+ def stroke_width(self) -> float:
117
+ """Get stroke width.
118
+
119
+ Returns:
120
+ Stroke width in mm
121
+ """
122
+ return self._data.stroke_width
123
+
124
+ @stroke_width.setter
125
+ def stroke_width(self, value: float) -> None:
126
+ """Set stroke width.
127
+
128
+ Args:
129
+ value: Stroke width in mm
130
+ """
131
+ self._data = Wire(
132
+ uuid=self._data.uuid,
133
+ points=self._data.points,
134
+ wire_type=self._data.wire_type,
135
+ stroke_width=value,
136
+ stroke_type=self._data.stroke_type,
137
+ )
138
+ self._mark_modified()
139
+
140
+ @property
141
+ def stroke_type(self) -> str:
142
+ """Get stroke type.
143
+
144
+ Returns:
145
+ Stroke type string
146
+ """
147
+ return self._data.stroke_type
148
+
149
+ @stroke_type.setter
150
+ def stroke_type(self, value: str) -> None:
151
+ """Set stroke type.
152
+
153
+ Args:
154
+ value: Stroke type string
155
+ """
156
+ self._data = Wire(
157
+ uuid=self._data.uuid,
158
+ points=self._data.points,
159
+ wire_type=self._data.wire_type,
160
+ stroke_width=self._data.stroke_width,
161
+ stroke_type=value,
162
+ )
163
+ self._mark_modified()
164
+
165
+ # Delegate methods to underlying Wire dataclass
166
+
167
+ @property
168
+ def length(self) -> float:
169
+ """Get total wire length.
170
+
171
+ Returns:
172
+ Total length of all wire segments in mm
173
+ """
174
+ return self._data.length
175
+
176
+ def is_simple(self) -> bool:
177
+ """Check if wire is a simple 2-point wire.
178
+
179
+ Returns:
180
+ True if wire has exactly 2 points, False otherwise
181
+ """
182
+ return self._data.is_simple()
183
+
184
+ def is_horizontal(self) -> bool:
185
+ """Check if wire is horizontal (delegates to Wire).
186
+
187
+ Returns:
188
+ True if wire is horizontal
189
+ """
190
+ return self._data.is_horizontal()
191
+
192
+ def is_vertical(self) -> bool:
193
+ """Check if wire is vertical (delegates to Wire).
194
+
195
+ Returns:
196
+ True if wire is vertical
197
+ """
198
+ return self._data.is_vertical()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kicad-sch-api
3
- Version: 0.4.4
3
+ Version: 0.4.5
4
4
  Summary: Professional KiCAD schematic manipulation library with exact format preservation
5
5
  Author-email: Circuit-Synth <shane@circuit-synth.com>
6
6
  Maintainer-email: Circuit-Synth <shane@circuit-synth.com>
@@ -1,4 +1,4 @@
1
- kicad_sch_api/__init__.py,sha256=RqnJXbVVuA10zIozkuqnqv42nWBF-opcjChSAPne6nw,2919
1
+ kicad_sch_api/__init__.py,sha256=BK1u1-hOS-wEXOchr4Gvz62HMSMXj1QCgCf1gsKMXtM,3190
2
2
  kicad_sch_api/cli.py,sha256=ZzmwzfHEvPgGfCiQBU4G2LBAyRtMNiBRoY21pivJSYc,7621
3
3
  kicad_sch_api/py.typed,sha256=e4ldqxwpY7pNDG1olbvj4HSKr8sZ9vxgA_2ek8xXn-Q,70
4
4
  kicad_sch_api/cli/__init__.py,sha256=mflSYoGLMJw2EFEByw9GD79pgj_Hebi6htJy7cKV5qM,952
@@ -14,10 +14,11 @@ kicad_sch_api/collections/components.py,sha256=ofH3P38Ube3uxVvk_28gJwJPsdL6KA37r
14
14
  kicad_sch_api/collections/junctions.py,sha256=4bjuUKm-tCWSJJTtuPwgjYh0Xglg2SEG8IOAvhULgtg,11728
15
15
  kicad_sch_api/collections/labels.py,sha256=zg6_xe4ifwIbc8M1E5MDGTh8Ps57oBeqbS9MclrvxKU,12266
16
16
  kicad_sch_api/collections/wires.py,sha256=o2Y_KIwOmFMytdOc2MjgnoUrK4Woj7wR9ROj4uRTuY0,12394
17
- kicad_sch_api/core/__init__.py,sha256=ur_KeYBlGKl-e1hLpLdxAhGV2A-PCCGkcqd0r6KSeBA,566
17
+ kicad_sch_api/core/__init__.py,sha256=YsNGh03VpriQbLIv6CLDey8NzTgwm3FeOvJVKd9K9T8,1285
18
18
  kicad_sch_api/core/component_bounds.py,sha256=Qc-Fazq_RvASVZ57eGg99NahCIgnWq8FFg1J4HfggK0,17917
19
19
  kicad_sch_api/core/components.py,sha256=powrqJGgqUBvMevMwhWSobrK6n8W525H7vzpPYCUXTs,28448
20
20
  kicad_sch_api/core/config.py,sha256=ECR6WLrC8i0itxRLJAZN3TDthbtOfd5NwNbiU6TSlW4,6224
21
+ kicad_sch_api/core/exceptions.py,sha256=ceV6nKbxk4BjQAS-recAI7yNyiqyvtdnJoDePPuU2ek,4694
21
22
  kicad_sch_api/core/formatter.py,sha256=ci-URhZFVHdSV4rEfOgTGtJu1tvW5MZEzSI1j2vtexE,22910
22
23
  kicad_sch_api/core/geometry.py,sha256=27SgN0padLbQuTi8MV6UUCp6Pyaiv8V9gmYDOhfwny8,2947
23
24
  kicad_sch_api/core/ic_manager.py,sha256=Kg0HIOMU-TGXiIkrnwcHFQ1Kfv_3rW2U1cwBKJsKopc,7219
@@ -28,23 +29,24 @@ kicad_sch_api/core/no_connects.py,sha256=6HCXzdVO124AdUUsQbCv9Y0f7KWmObkQ4OZLimY
28
29
  kicad_sch_api/core/parser.py,sha256=uvFVdSVzv5-RZmD_OKnqm51DIatFfsKF7NzLcWx4gfg,28761
29
30
  kicad_sch_api/core/parsing_utils.py,sha256=tqQpuiNePNRx6Izsp9pnCBtlgiEYfMPkVFjkE5Uuofg,1776
30
31
  kicad_sch_api/core/pin_utils.py,sha256=XGEow3HzBTyT8a0B_ZC8foMvwzYaENSaqTUwDW1rz24,5417
31
- kicad_sch_api/core/schematic.py,sha256=W3URcKzjUrsTy3MfDejGuhuLmV2WM2YmFnGTihzGgt0,56489
32
+ kicad_sch_api/core/schematic.py,sha256=C6ujNEdftSmcpRhhGRg_g9IdY0gbYMHztczigy-z8L4,57816
32
33
  kicad_sch_api/core/texts.py,sha256=kBwUxwftqncl-yauiJjdnLXauV6EWq2oLhKw2bAxD_c,9990
33
- kicad_sch_api/core/types.py,sha256=Rx3Q9PTmzP2RYEJhnb3bw0Gs2N1Gvn27KOPtqTph0hU,15853
34
+ kicad_sch_api/core/types.py,sha256=SQNvl4JLxgHYht7c3W4jsCll99OdkptURyMjBhWyg-8,15974
34
35
  kicad_sch_api/core/wires.py,sha256=lLqcpRodErD4fD4_VMB1HgC9ViPnYPGcT8b-N9S0q-g,7139
35
36
  kicad_sch_api/core/collections/__init__.py,sha256=i75_p9v330S2hxi4LrAGLmcVjrF8O0nTlGjCCjNbkq0,118
36
37
  kicad_sch_api/core/collections/base.py,sha256=H-g1SC7hKD5QvbXPKVQotCK2e5pn04-y1ipkBQGcy3I,7076
37
38
  kicad_sch_api/core/factories/__init__.py,sha256=qFx_rAgcTgWRZ572hvzzKNAze-jE0BOLqLrSFzj7T6M,130
38
39
  kicad_sch_api/core/factories/element_factory.py,sha256=zUmV1dnMXJl3fxXHdU9OJhqsHf_0D_DBOzWRLFYdG0c,8059
39
- kicad_sch_api/core/managers/__init__.py,sha256=Ec9H9RSBFt2MeJIhnZFUN9sa2ql0BSrO8FNOa1XsXeU,731
40
- kicad_sch_api/core/managers/file_io.py,sha256=k7yAfOvS5SqHrn1bCOH_wmOqDQnQe25wvhSPDmI8Cjk,7670
41
- kicad_sch_api/core/managers/format_sync.py,sha256=ar0FfhwXrU2l5ATV0aW5KtVRfIM2iM1WoyGvXQh-ShY,16963
42
- kicad_sch_api/core/managers/graphics.py,sha256=-jd-JL1TOuDntNEQcSeJ56Ccrkuudmj1dJkRtLUtRog,18370
43
- kicad_sch_api/core/managers/metadata.py,sha256=9oJK8UgnzpjDwZ-46I1eW3_P9ZoJsDvutEsgROFmTwc,8004
44
- kicad_sch_api/core/managers/sheet.py,sha256=jnnEBon0QmMh22-Ls7ZP5A_4FxM7bCyaJOF9AzZT1Kg,14816
45
- kicad_sch_api/core/managers/text_elements.py,sha256=utZIg488x9NLQID6ALbOcI0ZM2PfvgADVJ1jcbGP9jk,17319
46
- kicad_sch_api/core/managers/validation.py,sha256=-ZP_9odFsBmFG4CkNc00ZJq6bm_VtyjJHYDkMDku3l4,15757
47
- kicad_sch_api/core/managers/wire.py,sha256=WZSycnkI7CCBTsYKai7TPBULXi0gs2XE1Xxs-d87IfU,12027
40
+ kicad_sch_api/core/managers/__init__.py,sha256=bofU_wrxSOU08dPxKlXMa9BNp_x7_O-hbrVo7JytLxo,780
41
+ kicad_sch_api/core/managers/base.py,sha256=xLKP3Y_sImUA9EdsH0xwjT3dst6tIoh4vFk1ZzDpg18,2275
42
+ kicad_sch_api/core/managers/file_io.py,sha256=4Ne9uRuEbGdBcKojkROxhVvbB4dPM_Luh-5JhZqkTPk,7740
43
+ kicad_sch_api/core/managers/format_sync.py,sha256=SEOU9Ppv-0V5-4kA1MR1Haz04TYuMu-OmUpmfc6uWcY,17011
44
+ kicad_sch_api/core/managers/graphics.py,sha256=TKffEW9IKXztSfDLassRGHiV8qBr8MYe3Hf6zhLI7Ww,18418
45
+ kicad_sch_api/core/managers/metadata.py,sha256=pqIINUbaJACEmVfUruTUekN8mFra0uHbWz7_VPdBRy0,8053
46
+ kicad_sch_api/core/managers/sheet.py,sha256=5rRY4xC5oCk4wMRyOFD6DH904-X7g-LUYt9sRXaJNls,14864
47
+ kicad_sch_api/core/managers/text_elements.py,sha256=vqBsj6zkE7139xLUk2vDinMm2Jq42cqOh2ZzU_B6_ns,17367
48
+ kicad_sch_api/core/managers/validation.py,sha256=n4bj6h6yTOuWrTQH8PIhzH1l84brxKPVzd1fJitDZWU,15805
49
+ kicad_sch_api/core/managers/wire.py,sha256=dfviwrg-LHSp0jMevjXVQqyhhynBHh6g-f7DqqCACQw,12075
48
50
  kicad_sch_api/discovery/__init__.py,sha256=qSuCsnC-hVtaLYE8fwd-Gea6JKwEVGPQ-hSNDNJYsIU,329
49
51
  kicad_sch_api/discovery/search_index.py,sha256=KgQT8ipT9OU6ktUwhDZ37Mao0Cba0fJOsxUk9m8ZKbY,15856
50
52
  kicad_sch_api/geometry/__init__.py,sha256=hTBXkn8mZZCjzDIrtPv67QsnCYB77L67JjthQgEIX7o,716
@@ -66,7 +68,7 @@ kicad_sch_api/parsers/elements/label_parser.py,sha256=KL_AV_-BhA_KV1FLlqNpMj1Nu-
66
68
  kicad_sch_api/parsers/elements/library_parser.py,sha256=qHQMI3PatLgHtUWvWhQxbKC-NXJqgOVW33hVkMQ9sEU,6321
67
69
  kicad_sch_api/parsers/elements/metadata_parser.py,sha256=IFpgk5eLqp1kcjhpZB-jPThBCVyvdgsLo4YRydlvYD4,1897
68
70
  kicad_sch_api/parsers/elements/sheet_parser.py,sha256=xZld-yzW7r1FKABaK89K7U-Zenqz2cHUqtMnADhxCeQ,14246
69
- kicad_sch_api/parsers/elements/symbol_parser.py,sha256=G_N1kbTY2kCBG08gBHuXtmsFcw_dWMzwmVxOaO7F3Gw,12433
71
+ kicad_sch_api/parsers/elements/symbol_parser.py,sha256=9eidmBsL0fC3DPXHBCnWiyYnDRsB8hzlx-zHdS4PaMs,14175
70
72
  kicad_sch_api/parsers/elements/text_parser.py,sha256=n5G_3czchmPCEdvTVoQsATTW1PQW7KcSjzglqzPlOKQ,9820
71
73
  kicad_sch_api/parsers/elements/wire_parser.py,sha256=geWI3jMXM7lZF26k7n7c6RQ55x4gsO4j8Ou0fk0O2j8,8271
72
74
  kicad_sch_api/symbols/__init__.py,sha256=NfakJ5-8AQxq5vi8nZVuaUtDpWHfwHm5AD4rC-p9BZI,501
@@ -74,15 +76,18 @@ kicad_sch_api/symbols/cache.py,sha256=pKFjmuyId-9gGPAue-1Rzy4MRsecoC4TMn0hn2n0Yq
74
76
  kicad_sch_api/symbols/resolver.py,sha256=w0jqepp9RwWqlwQHX28FpTn1aSz8iOf7kcJUOBMHwqE,12074
75
77
  kicad_sch_api/symbols/validators.py,sha256=5Ryt4rLHxDF3ALViaNLRrI3pYvOreVT51gpvhECTZms,17216
76
78
  kicad_sch_api/utils/__init__.py,sha256=1V_yGgI7jro6MUc4Pviux_WIeJ1wmiYFID186SZwWLQ,277
77
- kicad_sch_api/utils/validation.py,sha256=XlWGRZJb3cOPYpU9sLQQgC_NASwbi6W-LCN7PzUmaPY,15626
79
+ kicad_sch_api/utils/validation.py,sha256=xszXVD9a1x5TuAq-X8h1djVBOTuFs7AnHzGcnTSikUI,15300
78
80
  kicad_sch_api/validation/__init__.py,sha256=YZ2KDTpBN9BloD7I__gsw6IiW-g5tZPux0kgyXb3TjU,524
79
81
  kicad_sch_api/validation/erc.py,sha256=6WMHyMakKpn1-jXkfO9ltayLRA0OaU1N7GQm-pbZTLg,5160
80
82
  kicad_sch_api/validation/erc_models.py,sha256=wFYMH-cbcdRX1j9LPn28IGUYBLioPDr4lP0m2QuhAnI,6628
81
83
  kicad_sch_api/validation/pin_matrix.py,sha256=AdBCvEmOByXACOUoskYxYMcwKvIasxrtJOqWM-WgVi0,7456
82
84
  kicad_sch_api/validation/validators.py,sha256=3txkQpR3qAUzp0-m-FnDYf3-_GGj6iaGZj4kSYu-Fe4,13166
83
- kicad_sch_api-0.4.4.dist-info/licenses/LICENSE,sha256=Em65Nvte1G9MHc0rHqtYuGkCPcshD588itTa358J6gs,1070
84
- kicad_sch_api-0.4.4.dist-info/METADATA,sha256=JlL-yhkt577MRogTnhRj441fplcHMNrejWxKDOksXq4,17953
85
- kicad_sch_api-0.4.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
86
- kicad_sch_api-0.4.4.dist-info/entry_points.txt,sha256=VWKsFi2Jv7G_tmio3cNVhhIBfv_OZFaKa-T_ED84lc8,57
87
- kicad_sch_api-0.4.4.dist-info/top_level.txt,sha256=n0ex4gOJ1b_fARowcGqRzyOGZcHRhc5LZa6_vVgGxcI,14
88
- kicad_sch_api-0.4.4.dist-info/RECORD,,
85
+ kicad_sch_api/wrappers/__init__.py,sha256=Leu_fgWwyeKMfqKv5K4nYbzaIQD5T8-u6HXTMq7Ccr8,271
86
+ kicad_sch_api/wrappers/base.py,sha256=VZlfyRBrhGXtgg7RbZYpIRKRYRBR8j4uiaXfcP8TgKM,2386
87
+ kicad_sch_api/wrappers/wire.py,sha256=cK3zkQJdujRQ_jKYU1Jxai7alhrVwzeHzgcnl4KTvi8,5102
88
+ kicad_sch_api-0.4.5.dist-info/licenses/LICENSE,sha256=Em65Nvte1G9MHc0rHqtYuGkCPcshD588itTa358J6gs,1070
89
+ kicad_sch_api-0.4.5.dist-info/METADATA,sha256=KVxIh6lOV00hCMUAINehj1GFrIg-52iYBwInBAqx3jY,17953
90
+ kicad_sch_api-0.4.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
91
+ kicad_sch_api-0.4.5.dist-info/entry_points.txt,sha256=VWKsFi2Jv7G_tmio3cNVhhIBfv_OZFaKa-T_ED84lc8,57
92
+ kicad_sch_api-0.4.5.dist-info/top_level.txt,sha256=n0ex4gOJ1b_fARowcGqRzyOGZcHRhc5LZa6_vVgGxcI,14
93
+ kicad_sch_api-0.4.5.dist-info/RECORD,,