kicad-sch-api 0.3.4__py3-none-any.whl → 0.4.0__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/collections/__init__.py +21 -0
- kicad_sch_api/collections/base.py +294 -0
- kicad_sch_api/collections/components.py +434 -0
- kicad_sch_api/collections/junctions.py +366 -0
- kicad_sch_api/collections/labels.py +404 -0
- kicad_sch_api/collections/wires.py +406 -0
- kicad_sch_api/core/components.py +5 -0
- kicad_sch_api/core/formatter.py +3 -1
- kicad_sch_api/core/labels.py +348 -0
- kicad_sch_api/core/managers/__init__.py +26 -0
- kicad_sch_api/core/managers/file_io.py +243 -0
- kicad_sch_api/core/managers/format_sync.py +501 -0
- kicad_sch_api/core/managers/graphics.py +579 -0
- kicad_sch_api/core/managers/metadata.py +268 -0
- kicad_sch_api/core/managers/sheet.py +454 -0
- kicad_sch_api/core/managers/text_elements.py +536 -0
- kicad_sch_api/core/managers/validation.py +474 -0
- kicad_sch_api/core/managers/wire.py +346 -0
- kicad_sch_api/core/nets.py +310 -0
- kicad_sch_api/core/no_connects.py +276 -0
- kicad_sch_api/core/parser.py +75 -41
- kicad_sch_api/core/schematic.py +904 -1074
- kicad_sch_api/core/texts.py +343 -0
- kicad_sch_api/core/types.py +13 -4
- kicad_sch_api/geometry/font_metrics.py +3 -1
- kicad_sch_api/geometry/symbol_bbox.py +56 -43
- kicad_sch_api/interfaces/__init__.py +17 -0
- kicad_sch_api/interfaces/parser.py +76 -0
- kicad_sch_api/interfaces/repository.py +70 -0
- kicad_sch_api/interfaces/resolver.py +117 -0
- kicad_sch_api/parsers/__init__.py +14 -0
- kicad_sch_api/parsers/base.py +145 -0
- kicad_sch_api/parsers/label_parser.py +254 -0
- kicad_sch_api/parsers/registry.py +155 -0
- kicad_sch_api/parsers/symbol_parser.py +222 -0
- kicad_sch_api/parsers/wire_parser.py +99 -0
- kicad_sch_api/symbols/__init__.py +18 -0
- kicad_sch_api/symbols/cache.py +467 -0
- kicad_sch_api/symbols/resolver.py +361 -0
- kicad_sch_api/symbols/validators.py +504 -0
- {kicad_sch_api-0.3.4.dist-info → kicad_sch_api-0.4.0.dist-info}/METADATA +1 -1
- kicad_sch_api-0.4.0.dist-info/RECORD +67 -0
- kicad_sch_api-0.3.4.dist-info/RECORD +0 -34
- {kicad_sch_api-0.3.4.dist-info → kicad_sch_api-0.4.0.dist-info}/WHEEL +0 -0
- {kicad_sch_api-0.3.4.dist-info → kicad_sch_api-0.4.0.dist-info}/entry_points.txt +0 -0
- {kicad_sch_api-0.3.4.dist-info → kicad_sch_api-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {kicad_sch_api-0.3.4.dist-info → kicad_sch_api-0.4.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Symbol validation for KiCAD symbol definitions.
|
|
3
|
+
|
|
4
|
+
Provides comprehensive validation for symbol definitions, inheritance chains,
|
|
5
|
+
and symbol data integrity.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Any, Dict, List, Optional, Set, Tuple
|
|
10
|
+
|
|
11
|
+
from ..library.cache import SymbolDefinition
|
|
12
|
+
from ..utils.validation import ValidationError, ValidationIssue
|
|
13
|
+
from .cache import ISymbolCache
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SymbolValidator:
|
|
19
|
+
"""
|
|
20
|
+
Comprehensive validator for symbol definitions and inheritance.
|
|
21
|
+
|
|
22
|
+
Provides detailed validation with specific error reporting for
|
|
23
|
+
symbol issues that could affect schematic generation.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, cache: Optional[ISymbolCache] = None):
|
|
27
|
+
"""
|
|
28
|
+
Initialize symbol validator.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
cache: Optional symbol cache for inheritance validation
|
|
32
|
+
"""
|
|
33
|
+
self._cache = cache
|
|
34
|
+
self._validation_rules = self._initialize_validation_rules()
|
|
35
|
+
|
|
36
|
+
def validate_symbol(self, symbol: SymbolDefinition) -> List[ValidationIssue]:
|
|
37
|
+
"""
|
|
38
|
+
Validate a symbol definition comprehensively.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
symbol: Symbol to validate
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
List of validation issues found
|
|
45
|
+
"""
|
|
46
|
+
issues = []
|
|
47
|
+
|
|
48
|
+
# Run all validation rules
|
|
49
|
+
for rule_name, rule_func in self._validation_rules.items():
|
|
50
|
+
try:
|
|
51
|
+
rule_issues = rule_func(symbol)
|
|
52
|
+
issues.extend(rule_issues)
|
|
53
|
+
except Exception as e:
|
|
54
|
+
issues.append(
|
|
55
|
+
ValidationIssue(
|
|
56
|
+
category="validation",
|
|
57
|
+
message=f"Validation rule '{rule_name}' failed: {e}",
|
|
58
|
+
level="error",
|
|
59
|
+
context={"symbol": symbol.lib_id, "rule": rule_name},
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return issues
|
|
64
|
+
|
|
65
|
+
def validate_lib_id(self, lib_id: str) -> bool:
|
|
66
|
+
"""
|
|
67
|
+
Validate library ID format.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
lib_id: Library identifier to validate
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
True if lib_id format is valid
|
|
74
|
+
"""
|
|
75
|
+
if not lib_id or not isinstance(lib_id, str):
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
if ":" not in lib_id:
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
parts = lib_id.split(":")
|
|
82
|
+
if len(parts) != 2:
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
library_name, symbol_name = parts
|
|
86
|
+
return bool(library_name.strip() and symbol_name.strip())
|
|
87
|
+
|
|
88
|
+
def validate_inheritance_chain(self, symbol: SymbolDefinition) -> List[ValidationIssue]:
|
|
89
|
+
"""
|
|
90
|
+
Validate symbol inheritance chain for cycles and missing parents.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
symbol: Symbol to validate inheritance for
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
List of inheritance-related validation issues
|
|
97
|
+
"""
|
|
98
|
+
issues = []
|
|
99
|
+
|
|
100
|
+
if not symbol.extends:
|
|
101
|
+
return issues
|
|
102
|
+
|
|
103
|
+
if not self._cache:
|
|
104
|
+
issues.append(
|
|
105
|
+
ValidationIssue(
|
|
106
|
+
category="inheritance",
|
|
107
|
+
message="Cannot validate inheritance without cache",
|
|
108
|
+
level="warning",
|
|
109
|
+
context={"symbol": symbol.lib_id},
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
return issues
|
|
113
|
+
|
|
114
|
+
# Check for inheritance chain issues
|
|
115
|
+
visited = set()
|
|
116
|
+
current_lib_id = symbol.lib_id
|
|
117
|
+
|
|
118
|
+
while current_lib_id:
|
|
119
|
+
if current_lib_id in visited:
|
|
120
|
+
issues.append(
|
|
121
|
+
ValidationIssue(
|
|
122
|
+
category="inheritance",
|
|
123
|
+
message=f"Circular inheritance detected in chain starting from {symbol.lib_id}",
|
|
124
|
+
level="error",
|
|
125
|
+
context={"symbol": symbol.lib_id, "cycle_point": current_lib_id},
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
break
|
|
129
|
+
|
|
130
|
+
visited.add(current_lib_id)
|
|
131
|
+
current_symbol = self._cache.get_symbol(current_lib_id)
|
|
132
|
+
|
|
133
|
+
if not current_symbol:
|
|
134
|
+
issues.append(
|
|
135
|
+
ValidationIssue(
|
|
136
|
+
category="inheritance",
|
|
137
|
+
message=f"Missing symbol in inheritance chain: {current_lib_id}",
|
|
138
|
+
level="error",
|
|
139
|
+
context={"symbol": symbol.lib_id, "missing": current_lib_id},
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
break
|
|
143
|
+
|
|
144
|
+
if not current_symbol.extends:
|
|
145
|
+
break
|
|
146
|
+
|
|
147
|
+
# Resolve parent lib_id
|
|
148
|
+
parent_name = current_symbol.extends
|
|
149
|
+
if ":" in parent_name:
|
|
150
|
+
current_lib_id = parent_name
|
|
151
|
+
else:
|
|
152
|
+
current_lib_id = f"{current_symbol.library}:{parent_name}"
|
|
153
|
+
|
|
154
|
+
# Check if parent exists
|
|
155
|
+
if not self._cache.has_symbol(current_lib_id):
|
|
156
|
+
issues.append(
|
|
157
|
+
ValidationIssue(
|
|
158
|
+
category="inheritance",
|
|
159
|
+
message=f"Parent symbol not found: {current_lib_id}",
|
|
160
|
+
level="error",
|
|
161
|
+
context={"symbol": symbol.lib_id, "parent": current_lib_id},
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
break
|
|
165
|
+
|
|
166
|
+
return issues
|
|
167
|
+
|
|
168
|
+
def validate_symbol_integrity(self, symbol: SymbolDefinition) -> List[ValidationIssue]:
|
|
169
|
+
"""
|
|
170
|
+
Validate symbol data integrity and consistency.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
symbol: Symbol to validate
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
List of integrity validation issues
|
|
177
|
+
"""
|
|
178
|
+
issues = []
|
|
179
|
+
|
|
180
|
+
# Validate pin integrity
|
|
181
|
+
pin_issues = self._validate_pins(symbol)
|
|
182
|
+
issues.extend(pin_issues)
|
|
183
|
+
|
|
184
|
+
# Validate graphic elements
|
|
185
|
+
graphics_issues = self._validate_graphics(symbol)
|
|
186
|
+
issues.extend(graphics_issues)
|
|
187
|
+
|
|
188
|
+
# Validate units
|
|
189
|
+
units_issues = self._validate_units(symbol)
|
|
190
|
+
issues.extend(units_issues)
|
|
191
|
+
|
|
192
|
+
return issues
|
|
193
|
+
|
|
194
|
+
def _initialize_validation_rules(self) -> Dict[str, callable]:
|
|
195
|
+
"""Initialize all validation rules."""
|
|
196
|
+
return {
|
|
197
|
+
"lib_id_format": self._validate_lib_id_format,
|
|
198
|
+
"required_fields": self._validate_required_fields,
|
|
199
|
+
"reference_prefix": self._validate_reference_prefix,
|
|
200
|
+
"pin_consistency": self._validate_pin_consistency,
|
|
201
|
+
"pin_details": self._validate_pins,
|
|
202
|
+
"unit_consistency": self._validate_unit_consistency,
|
|
203
|
+
"unit_details": self._validate_units,
|
|
204
|
+
"extends_format": self._validate_extends_format,
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
def _validate_lib_id_format(self, symbol: SymbolDefinition) -> List[ValidationIssue]:
|
|
208
|
+
"""Validate lib_id format."""
|
|
209
|
+
issues = []
|
|
210
|
+
|
|
211
|
+
if not self.validate_lib_id(symbol.lib_id):
|
|
212
|
+
issues.append(
|
|
213
|
+
ValidationIssue(
|
|
214
|
+
category="lib_id",
|
|
215
|
+
message=f"Invalid lib_id format: {symbol.lib_id}",
|
|
216
|
+
level="error",
|
|
217
|
+
context={"symbol": symbol.lib_id},
|
|
218
|
+
)
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
return issues
|
|
222
|
+
|
|
223
|
+
def _validate_required_fields(self, symbol: SymbolDefinition) -> List[ValidationIssue]:
|
|
224
|
+
"""Validate required symbol fields."""
|
|
225
|
+
issues = []
|
|
226
|
+
|
|
227
|
+
if not symbol.name:
|
|
228
|
+
issues.append(
|
|
229
|
+
ValidationIssue(
|
|
230
|
+
category="required_fields",
|
|
231
|
+
message="Symbol name is required",
|
|
232
|
+
level="error",
|
|
233
|
+
context={"symbol": symbol.lib_id},
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
if not symbol.library:
|
|
238
|
+
issues.append(
|
|
239
|
+
ValidationIssue(
|
|
240
|
+
category="required_fields",
|
|
241
|
+
message="Symbol library is required",
|
|
242
|
+
level="error",
|
|
243
|
+
context={"symbol": symbol.lib_id},
|
|
244
|
+
)
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
if not symbol.reference_prefix:
|
|
248
|
+
issues.append(
|
|
249
|
+
ValidationIssue(
|
|
250
|
+
category="required_fields",
|
|
251
|
+
message="Symbol reference prefix is missing",
|
|
252
|
+
level="warning",
|
|
253
|
+
context={"symbol": symbol.lib_id},
|
|
254
|
+
)
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
return issues
|
|
258
|
+
|
|
259
|
+
def _validate_reference_prefix(self, symbol: SymbolDefinition) -> List[ValidationIssue]:
|
|
260
|
+
"""Validate reference prefix format."""
|
|
261
|
+
issues = []
|
|
262
|
+
|
|
263
|
+
if symbol.reference_prefix:
|
|
264
|
+
# Check for invalid characters
|
|
265
|
+
invalid_chars = set(symbol.reference_prefix) - set(
|
|
266
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
|
|
267
|
+
)
|
|
268
|
+
if invalid_chars:
|
|
269
|
+
issues.append(
|
|
270
|
+
ValidationIssue(
|
|
271
|
+
category="reference_prefix",
|
|
272
|
+
message=f"Reference prefix contains invalid characters: {invalid_chars}",
|
|
273
|
+
level="warning",
|
|
274
|
+
context={"symbol": symbol.lib_id, "prefix": symbol.reference_prefix},
|
|
275
|
+
)
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
# Check for common patterns
|
|
279
|
+
if symbol.reference_prefix.lower() in ["u", "ic"] and not symbol.description:
|
|
280
|
+
issues.append(
|
|
281
|
+
ValidationIssue(
|
|
282
|
+
category="reference_prefix",
|
|
283
|
+
message="Generic IC prefix 'U' - consider adding description",
|
|
284
|
+
level="info",
|
|
285
|
+
context={"symbol": symbol.lib_id},
|
|
286
|
+
)
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
return issues
|
|
290
|
+
|
|
291
|
+
def _validate_pin_consistency(self, symbol: SymbolDefinition) -> List[ValidationIssue]:
|
|
292
|
+
"""Validate pin consistency and numbering."""
|
|
293
|
+
issues = []
|
|
294
|
+
|
|
295
|
+
if not symbol.pins:
|
|
296
|
+
issues.append(
|
|
297
|
+
ValidationIssue(
|
|
298
|
+
category="symbol",
|
|
299
|
+
level="warning",
|
|
300
|
+
message="Symbol has no pins defined",
|
|
301
|
+
context={"symbol": symbol.lib_id},
|
|
302
|
+
)
|
|
303
|
+
)
|
|
304
|
+
return issues
|
|
305
|
+
|
|
306
|
+
# Check for duplicate pin numbers
|
|
307
|
+
pin_numbers = [pin.number for pin in symbol.pins]
|
|
308
|
+
duplicates = set([num for num in pin_numbers if pin_numbers.count(num) > 1])
|
|
309
|
+
|
|
310
|
+
if duplicates:
|
|
311
|
+
issues.append(
|
|
312
|
+
ValidationIssue(
|
|
313
|
+
category="symbol",
|
|
314
|
+
level="error",
|
|
315
|
+
message=f"Duplicate pin numbers found: {duplicates}",
|
|
316
|
+
context={"symbol": symbol.lib_id, "duplicates": list(duplicates)},
|
|
317
|
+
)
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
# Check for pins with same position
|
|
321
|
+
pin_positions = [(pin.position.x, pin.position.y) for pin in symbol.pins]
|
|
322
|
+
for i, pos1 in enumerate(pin_positions):
|
|
323
|
+
for j, pos2 in enumerate(pin_positions[i + 1 :], i + 1):
|
|
324
|
+
if pos1 == pos2:
|
|
325
|
+
issues.append(
|
|
326
|
+
ValidationIssue(
|
|
327
|
+
category="symbol",
|
|
328
|
+
level="warning",
|
|
329
|
+
message=f"Pins at same position: {symbol.pins[i].number} and {symbol.pins[j].number}",
|
|
330
|
+
context={"symbol": symbol.lib_id, "position": pos1},
|
|
331
|
+
)
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
return issues
|
|
335
|
+
|
|
336
|
+
def _validate_unit_consistency(self, symbol: SymbolDefinition) -> List[ValidationIssue]:
|
|
337
|
+
"""Validate unit consistency."""
|
|
338
|
+
issues = []
|
|
339
|
+
|
|
340
|
+
if symbol.units < 1:
|
|
341
|
+
issues.append(
|
|
342
|
+
ValidationIssue(
|
|
343
|
+
category="symbol",
|
|
344
|
+
level="error",
|
|
345
|
+
message=f"Invalid unit count: {symbol.units}",
|
|
346
|
+
context={"symbol": symbol.lib_id},
|
|
347
|
+
)
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
# Check unit names consistency
|
|
351
|
+
if symbol.unit_names:
|
|
352
|
+
for unit_num in symbol.unit_names:
|
|
353
|
+
if unit_num < 1 or unit_num > symbol.units:
|
|
354
|
+
issues.append(
|
|
355
|
+
ValidationIssue(
|
|
356
|
+
category="symbol",
|
|
357
|
+
level="warning",
|
|
358
|
+
message=f"Unit name defined for invalid unit number: {unit_num}",
|
|
359
|
+
context={"symbol": symbol.lib_id, "unit": unit_num},
|
|
360
|
+
)
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
return issues
|
|
364
|
+
|
|
365
|
+
def _validate_extends_format(self, symbol: SymbolDefinition) -> List[ValidationIssue]:
|
|
366
|
+
"""Validate extends directive format."""
|
|
367
|
+
issues = []
|
|
368
|
+
|
|
369
|
+
if symbol.extends is not None:
|
|
370
|
+
# Check extends format
|
|
371
|
+
if not symbol.extends.strip():
|
|
372
|
+
issues.append(
|
|
373
|
+
ValidationIssue(
|
|
374
|
+
category="symbol",
|
|
375
|
+
level="error",
|
|
376
|
+
message="Empty extends directive",
|
|
377
|
+
context={"symbol": symbol.lib_id},
|
|
378
|
+
)
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
# Check for self-reference
|
|
382
|
+
if symbol.extends == symbol.name:
|
|
383
|
+
issues.append(
|
|
384
|
+
ValidationIssue(
|
|
385
|
+
category="symbol",
|
|
386
|
+
level="error",
|
|
387
|
+
message="Symbol cannot extend itself",
|
|
388
|
+
context={"symbol": symbol.lib_id},
|
|
389
|
+
)
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
return issues
|
|
393
|
+
|
|
394
|
+
def _validate_pins(self, symbol: SymbolDefinition) -> List[ValidationIssue]:
|
|
395
|
+
"""Validate pin definitions."""
|
|
396
|
+
issues = []
|
|
397
|
+
|
|
398
|
+
for pin in symbol.pins:
|
|
399
|
+
# Validate pin number
|
|
400
|
+
if not pin.number:
|
|
401
|
+
issues.append(
|
|
402
|
+
ValidationIssue(
|
|
403
|
+
category="symbol",
|
|
404
|
+
level="error",
|
|
405
|
+
message="Pin missing number",
|
|
406
|
+
context={"symbol": symbol.lib_id},
|
|
407
|
+
)
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
# Validate pin name
|
|
411
|
+
if not pin.name:
|
|
412
|
+
issues.append(
|
|
413
|
+
ValidationIssue(
|
|
414
|
+
category="symbol",
|
|
415
|
+
level="warning",
|
|
416
|
+
message=f"Pin {pin.number} missing name",
|
|
417
|
+
context={"symbol": symbol.lib_id, "pin": pin.number},
|
|
418
|
+
)
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
# Validate pin type
|
|
422
|
+
if not hasattr(pin, "pin_type") or not pin.pin_type:
|
|
423
|
+
issues.append(
|
|
424
|
+
ValidationIssue(
|
|
425
|
+
category="symbol",
|
|
426
|
+
level="warning",
|
|
427
|
+
message=f"Pin {pin.number} missing pin type",
|
|
428
|
+
context={"symbol": symbol.lib_id, "pin": pin.number},
|
|
429
|
+
)
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
return issues
|
|
433
|
+
|
|
434
|
+
def _validate_graphics(self, symbol: SymbolDefinition) -> List[ValidationIssue]:
|
|
435
|
+
"""Validate graphic elements."""
|
|
436
|
+
issues = []
|
|
437
|
+
|
|
438
|
+
if not symbol.graphic_elements:
|
|
439
|
+
issues.append(
|
|
440
|
+
ValidationIssue(
|
|
441
|
+
category="symbol",
|
|
442
|
+
level="info",
|
|
443
|
+
message="Symbol has no graphic elements",
|
|
444
|
+
context={"symbol": symbol.lib_id},
|
|
445
|
+
)
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
# Could add more graphic validation here
|
|
449
|
+
# - Check for overlapping elements
|
|
450
|
+
# - Validate coordinate ranges
|
|
451
|
+
# - Check fill/stroke consistency
|
|
452
|
+
|
|
453
|
+
return issues
|
|
454
|
+
|
|
455
|
+
def _validate_units(self, symbol: SymbolDefinition) -> List[ValidationIssue]:
|
|
456
|
+
"""Validate unit definitions."""
|
|
457
|
+
issues = []
|
|
458
|
+
|
|
459
|
+
# Check if pins are distributed across units correctly
|
|
460
|
+
if symbol.units > 1:
|
|
461
|
+
unit_pins = {}
|
|
462
|
+
for pin in symbol.pins:
|
|
463
|
+
unit = getattr(pin, "unit", 1)
|
|
464
|
+
if unit not in unit_pins:
|
|
465
|
+
unit_pins[unit] = []
|
|
466
|
+
unit_pins[unit].append(pin)
|
|
467
|
+
|
|
468
|
+
# Check for empty units
|
|
469
|
+
for unit_num in range(1, symbol.units + 1):
|
|
470
|
+
if unit_num not in unit_pins:
|
|
471
|
+
issues.append(
|
|
472
|
+
ValidationIssue(
|
|
473
|
+
category="symbol",
|
|
474
|
+
level="warning",
|
|
475
|
+
message=f"Unit {unit_num} has no pins",
|
|
476
|
+
context={"symbol": symbol.lib_id, "unit": unit_num},
|
|
477
|
+
)
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
return issues
|
|
481
|
+
|
|
482
|
+
def get_validation_summary(self, issues: List[ValidationIssue]) -> Dict[str, Any]:
|
|
483
|
+
"""
|
|
484
|
+
Get validation summary statistics.
|
|
485
|
+
|
|
486
|
+
Args:
|
|
487
|
+
issues: List of validation issues
|
|
488
|
+
|
|
489
|
+
Returns:
|
|
490
|
+
Summary dictionary with issue counts and severity
|
|
491
|
+
"""
|
|
492
|
+
summary = {
|
|
493
|
+
"total_issues": len(issues),
|
|
494
|
+
"error_count": len([i for i in issues if i.level.value == "error"]),
|
|
495
|
+
"warning_count": len([i for i in issues if i.level.value == "warning"]),
|
|
496
|
+
"info_count": len([i for i in issues if i.level.value == "info"]),
|
|
497
|
+
"severity": (
|
|
498
|
+
"error"
|
|
499
|
+
if any(i.level.value == "error" for i in issues)
|
|
500
|
+
else "warning" if any(i.level.value == "warning" for i in issues) else "info"
|
|
501
|
+
),
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return summary
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kicad-sch-api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
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>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
kicad_sch_api/__init__.py,sha256=87qLx-VMQTBHDVq_CiXW-6wyKtvLFc3AsKQaoLKsIbs,2919
|
|
2
|
+
kicad_sch_api/cli.py,sha256=ZzmwzfHEvPgGfCiQBU4G2LBAyRtMNiBRoY21pivJSYc,7621
|
|
3
|
+
kicad_sch_api/py.typed,sha256=e4ldqxwpY7pNDG1olbvj4HSKr8sZ9vxgA_2ek8xXn-Q,70
|
|
4
|
+
kicad_sch_api/collections/__init__.py,sha256=I3DSWk0XmGE8J1fcTtiYOETaWrY-fFJPlz3UGUK2qXY,627
|
|
5
|
+
kicad_sch_api/collections/base.py,sha256=7p-WVLg-2_uBS7z7mzwkOpCMwlR_VJZGr2iW_Hd1K3Q,8386
|
|
6
|
+
kicad_sch_api/collections/components.py,sha256=ofH3P38Ube3uxVvk_28gJwJPsdL6KA37rgxWNig6BGM,14023
|
|
7
|
+
kicad_sch_api/collections/junctions.py,sha256=4bjuUKm-tCWSJJTtuPwgjYh0Xglg2SEG8IOAvhULgtg,11728
|
|
8
|
+
kicad_sch_api/collections/labels.py,sha256=zg6_xe4ifwIbc8M1E5MDGTh8Ps57oBeqbS9MclrvxKU,12266
|
|
9
|
+
kicad_sch_api/collections/wires.py,sha256=o2Y_KIwOmFMytdOc2MjgnoUrK4Woj7wR9ROj4uRTuY0,12394
|
|
10
|
+
kicad_sch_api/core/__init__.py,sha256=ur_KeYBlGKl-e1hLpLdxAhGV2A-PCCGkcqd0r6KSeBA,566
|
|
11
|
+
kicad_sch_api/core/component_bounds.py,sha256=BFYJYULyzs5it2hN7bHTimyS9Vet4dxsMklRStob-F4,17509
|
|
12
|
+
kicad_sch_api/core/components.py,sha256=ilwZfrWUXQjtT3yB3yxT-H0X64izU3aXP6TdPZdki2k,25393
|
|
13
|
+
kicad_sch_api/core/config.py,sha256=itw0j3DeIEHaFVf8p3mfAS1SP6jclBwvMv7NPdkThE4,4309
|
|
14
|
+
kicad_sch_api/core/formatter.py,sha256=RGN1Y1Ne5uBhKU4XRINVBb0ph6pm5Khycdv352r3cDw,22207
|
|
15
|
+
kicad_sch_api/core/geometry.py,sha256=27SgN0padLbQuTi8MV6UUCp6Pyaiv8V9gmYDOhfwny8,2947
|
|
16
|
+
kicad_sch_api/core/ic_manager.py,sha256=Kg0HIOMU-TGXiIkrnwcHFQ1Kfv_3rW2U1cwBKJsKopc,7219
|
|
17
|
+
kicad_sch_api/core/junctions.py,sha256=Ay6BsWX_DLs-wB0eMA2CytKKq0N8Ja41ZubJWpAqNgM,6122
|
|
18
|
+
kicad_sch_api/core/labels.py,sha256=hkqZVd7wudl1-uil9jHt4k3vy1ZSX5JAyTFXktGauLo,11094
|
|
19
|
+
kicad_sch_api/core/manhattan_routing.py,sha256=t_T2u0zsQB-a8dTijFmY-qFq-oDt2qDebYyXzD_pBWI,15989
|
|
20
|
+
kicad_sch_api/core/nets.py,sha256=9bO8A1dzSuJ8aZ26-PHbUiWi9KYx6LqoXQHEKTCR7VA,9588
|
|
21
|
+
kicad_sch_api/core/no_connects.py,sha256=LM__SeeK-0YPOLjkh1RXlBbaKnqehhbox7A8F6cUq0o,9426
|
|
22
|
+
kicad_sch_api/core/parser.py,sha256=K3Dh5J3YswlXNakbR0FVcTPpuW4VK6Yg2e1a-yxH0xI,96227
|
|
23
|
+
kicad_sch_api/core/pin_utils.py,sha256=XGEow3HzBTyT8a0B_ZC8foMvwzYaENSaqTUwDW1rz24,5417
|
|
24
|
+
kicad_sch_api/core/schematic.py,sha256=OXOlC7GDfYFc1FlYHIUnHHp19DbzhRWBnNJjlma2NAk,56063
|
|
25
|
+
kicad_sch_api/core/simple_manhattan.py,sha256=CvIHvwmfABPF-COzhblYxEgRoR_R_eD-lmBFHHjDuMI,7241
|
|
26
|
+
kicad_sch_api/core/texts.py,sha256=el_9pr9VN-8v7miSG0HvOVsGEsMrkXNKpzJIRNxZmQ0,10727
|
|
27
|
+
kicad_sch_api/core/types.py,sha256=tieyn5-L_hW6EnKR-bLVB_jtXV6NCNb0SbdYE2srW4s,14140
|
|
28
|
+
kicad_sch_api/core/wire_routing.py,sha256=G-C7S-ntQxwuu1z3OaaYlkURXwKE4r4xmhbbi6cvvaI,12830
|
|
29
|
+
kicad_sch_api/core/wires.py,sha256=608t9oH4UzppdGgNgUd-ABK6T-ahyETZwhO_-CuKFO8,8319
|
|
30
|
+
kicad_sch_api/core/managers/__init__.py,sha256=Ec9H9RSBFt2MeJIhnZFUN9sa2ql0BSrO8FNOa1XsXeU,731
|
|
31
|
+
kicad_sch_api/core/managers/file_io.py,sha256=dHHoi0BhIdpWmrpMQ0tdZJpbNd7dseytnY3UO-p5xWY,7605
|
|
32
|
+
kicad_sch_api/core/managers/format_sync.py,sha256=ar0FfhwXrU2l5ATV0aW5KtVRfIM2iM1WoyGvXQh-ShY,16963
|
|
33
|
+
kicad_sch_api/core/managers/graphics.py,sha256=-jd-JL1TOuDntNEQcSeJ56Ccrkuudmj1dJkRtLUtRog,18370
|
|
34
|
+
kicad_sch_api/core/managers/metadata.py,sha256=Mjv-YjdaWRq-XlDZNlqgcWTyE1v8H7yLUXGWc9f9g7Q,8058
|
|
35
|
+
kicad_sch_api/core/managers/sheet.py,sha256=jnnEBon0QmMh22-Ls7ZP5A_4FxM7bCyaJOF9AzZT1Kg,14816
|
|
36
|
+
kicad_sch_api/core/managers/text_elements.py,sha256=utZIg488x9NLQID6ALbOcI0ZM2PfvgADVJ1jcbGP9jk,17319
|
|
37
|
+
kicad_sch_api/core/managers/validation.py,sha256=Rz6e1m14Qxw1Izbqm7fAGySFOimfx9m5BWdnuomueg8,15792
|
|
38
|
+
kicad_sch_api/core/managers/wire.py,sha256=Uvrqnacl0SjEfLmBwHVoyq0KXTM5lz7TRPdIffmKaG4,11596
|
|
39
|
+
kicad_sch_api/discovery/__init__.py,sha256=qSuCsnC-hVtaLYE8fwd-Gea6JKwEVGPQ-hSNDNJYsIU,329
|
|
40
|
+
kicad_sch_api/discovery/search_index.py,sha256=KgQT8ipT9OU6ktUwhDZ37Mao0Cba0fJOsxUk9m8ZKbY,15856
|
|
41
|
+
kicad_sch_api/geometry/__init__.py,sha256=hTBXkn8mZZCjzDIrtPv67QsnCYB77L67JjthQgEIX7o,716
|
|
42
|
+
kicad_sch_api/geometry/font_metrics.py,sha256=3f5_9ifxtDUigLDiafglO2pCgPE7JFDKqa-0uhLPkoQ,839
|
|
43
|
+
kicad_sch_api/geometry/symbol_bbox.py,sha256=3zd9-M9ehue5_-Gpdm_Yht4W49CbE0YRsAhAzfqJGEg,24161
|
|
44
|
+
kicad_sch_api/interfaces/__init__.py,sha256=ukuLgCT16e0sPLmrGb4HB_3DhGU1YriDEkeRgEBsQIA,435
|
|
45
|
+
kicad_sch_api/interfaces/parser.py,sha256=J-b0czz1q_O81HlvIp3dHx53cm8UGSgQTd4xe5tBszk,2008
|
|
46
|
+
kicad_sch_api/interfaces/repository.py,sha256=wFnWUCut6wPV9yDDE9k6zkhhijRnflbLteeXYtcU-gM,1763
|
|
47
|
+
kicad_sch_api/interfaces/resolver.py,sha256=2_3et6VVnyZZ8VI5i-Q4C-4bBXKTAgYAEFxAFuOWEGw,2936
|
|
48
|
+
kicad_sch_api/library/__init__.py,sha256=NG9UTdcpn25Bl9tPsYs9ED7bvpaVPVdtLMbnxkQkOnU,250
|
|
49
|
+
kicad_sch_api/library/cache.py,sha256=7na88grl465WHwUOGuOzYrrWwjsMBXhXVtxhnaJ9GBY,33208
|
|
50
|
+
kicad_sch_api/parsers/__init__.py,sha256=4TFc2duJMy6iRuk2dYbW4b7s-9H525iAsGU674_2a-E,357
|
|
51
|
+
kicad_sch_api/parsers/base.py,sha256=vsKGxZkdzTNqrdbRPx11D4b3ebDm3LW65v2Xf4Q06_c,4569
|
|
52
|
+
kicad_sch_api/parsers/label_parser.py,sha256=E9A2FY_BBZtFRxoT7g0Pg09J0J87nHAlMuTK4selsGQ,8692
|
|
53
|
+
kicad_sch_api/parsers/registry.py,sha256=LoPvWIiGYysvTRtFfd8kWCXE0i535hoKYpdKs49Ygjs,5098
|
|
54
|
+
kicad_sch_api/parsers/symbol_parser.py,sha256=RpTPMMOfNVHjJbXOrlJyTFWeVBH64Fq-dndG1F9Y_70,8411
|
|
55
|
+
kicad_sch_api/parsers/wire_parser.py,sha256=xHSG-baXS0ncnv3TLjLnd9poXB4rhBdHmz6q99RadX8,3420
|
|
56
|
+
kicad_sch_api/symbols/__init__.py,sha256=NfakJ5-8AQxq5vi8nZVuaUtDpWHfwHm5AD4rC-p9BZI,501
|
|
57
|
+
kicad_sch_api/symbols/cache.py,sha256=pKFjmuyId-9gGPAue-1Rzy4MRsecoC4TMn0hn2n0Yqk,16204
|
|
58
|
+
kicad_sch_api/symbols/resolver.py,sha256=w0jqepp9RwWqlwQHX28FpTn1aSz8iOf7kcJUOBMHwqE,12074
|
|
59
|
+
kicad_sch_api/symbols/validators.py,sha256=5Ryt4rLHxDF3ALViaNLRrI3pYvOreVT51gpvhECTZms,17216
|
|
60
|
+
kicad_sch_api/utils/__init__.py,sha256=1V_yGgI7jro6MUc4Pviux_WIeJ1wmiYFID186SZwWLQ,277
|
|
61
|
+
kicad_sch_api/utils/validation.py,sha256=XlWGRZJb3cOPYpU9sLQQgC_NASwbi6W-LCN7PzUmaPY,15626
|
|
62
|
+
kicad_sch_api-0.4.0.dist-info/licenses/LICENSE,sha256=Em65Nvte1G9MHc0rHqtYuGkCPcshD588itTa358J6gs,1070
|
|
63
|
+
kicad_sch_api-0.4.0.dist-info/METADATA,sha256=my1z7F-NiRJw7Kh1c99d5bh0-ohJ8CuDNTfbxylPxwI,17183
|
|
64
|
+
kicad_sch_api-0.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
65
|
+
kicad_sch_api-0.4.0.dist-info/entry_points.txt,sha256=VWKsFi2Jv7G_tmio3cNVhhIBfv_OZFaKa-T_ED84lc8,57
|
|
66
|
+
kicad_sch_api-0.4.0.dist-info/top_level.txt,sha256=n0ex4gOJ1b_fARowcGqRzyOGZcHRhc5LZa6_vVgGxcI,14
|
|
67
|
+
kicad_sch_api-0.4.0.dist-info/RECORD,,
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
kicad_sch_api/__init__.py,sha256=87qLx-VMQTBHDVq_CiXW-6wyKtvLFc3AsKQaoLKsIbs,2919
|
|
2
|
-
kicad_sch_api/cli.py,sha256=ZzmwzfHEvPgGfCiQBU4G2LBAyRtMNiBRoY21pivJSYc,7621
|
|
3
|
-
kicad_sch_api/py.typed,sha256=e4ldqxwpY7pNDG1olbvj4HSKr8sZ9vxgA_2ek8xXn-Q,70
|
|
4
|
-
kicad_sch_api/core/__init__.py,sha256=ur_KeYBlGKl-e1hLpLdxAhGV2A-PCCGkcqd0r6KSeBA,566
|
|
5
|
-
kicad_sch_api/core/component_bounds.py,sha256=BFYJYULyzs5it2hN7bHTimyS9Vet4dxsMklRStob-F4,17509
|
|
6
|
-
kicad_sch_api/core/components.py,sha256=tXRL18GObl2u94wl5jP-1ID56s_UD9F1gQ_iRIyZ_Kw,25290
|
|
7
|
-
kicad_sch_api/core/config.py,sha256=itw0j3DeIEHaFVf8p3mfAS1SP6jclBwvMv7NPdkThE4,4309
|
|
8
|
-
kicad_sch_api/core/formatter.py,sha256=zzZi0f06C1YWUy5l0WFS9G4KRTEzmAY3rFK3XGocvCo,22185
|
|
9
|
-
kicad_sch_api/core/geometry.py,sha256=27SgN0padLbQuTi8MV6UUCp6Pyaiv8V9gmYDOhfwny8,2947
|
|
10
|
-
kicad_sch_api/core/ic_manager.py,sha256=Kg0HIOMU-TGXiIkrnwcHFQ1Kfv_3rW2U1cwBKJsKopc,7219
|
|
11
|
-
kicad_sch_api/core/junctions.py,sha256=Ay6BsWX_DLs-wB0eMA2CytKKq0N8Ja41ZubJWpAqNgM,6122
|
|
12
|
-
kicad_sch_api/core/manhattan_routing.py,sha256=t_T2u0zsQB-a8dTijFmY-qFq-oDt2qDebYyXzD_pBWI,15989
|
|
13
|
-
kicad_sch_api/core/parser.py,sha256=UY_GNX1yHd3xgTVqZ9TZe1u94q4YZBo-NibsSH8Jy44,94983
|
|
14
|
-
kicad_sch_api/core/pin_utils.py,sha256=XGEow3HzBTyT8a0B_ZC8foMvwzYaENSaqTUwDW1rz24,5417
|
|
15
|
-
kicad_sch_api/core/schematic.py,sha256=B2n0tf3HyyVzTJOPABpzJGVbd5yBAsI9CE5OVZnSCoI,62027
|
|
16
|
-
kicad_sch_api/core/simple_manhattan.py,sha256=CvIHvwmfABPF-COzhblYxEgRoR_R_eD-lmBFHHjDuMI,7241
|
|
17
|
-
kicad_sch_api/core/types.py,sha256=D8VGvE7N2nj-xqnWSnTl98WaAbWh6JhQsn-pZCiLFfE,13974
|
|
18
|
-
kicad_sch_api/core/wire_routing.py,sha256=G-C7S-ntQxwuu1z3OaaYlkURXwKE4r4xmhbbi6cvvaI,12830
|
|
19
|
-
kicad_sch_api/core/wires.py,sha256=608t9oH4UzppdGgNgUd-ABK6T-ahyETZwhO_-CuKFO8,8319
|
|
20
|
-
kicad_sch_api/discovery/__init__.py,sha256=qSuCsnC-hVtaLYE8fwd-Gea6JKwEVGPQ-hSNDNJYsIU,329
|
|
21
|
-
kicad_sch_api/discovery/search_index.py,sha256=KgQT8ipT9OU6ktUwhDZ37Mao0Cba0fJOsxUk9m8ZKbY,15856
|
|
22
|
-
kicad_sch_api/geometry/__init__.py,sha256=hTBXkn8mZZCjzDIrtPv67QsnCYB77L67JjthQgEIX7o,716
|
|
23
|
-
kicad_sch_api/geometry/font_metrics.py,sha256=qqnfBuRqiLQDnGkk64rKzdyvuSNU0uBfdp0TKEgzXds,831
|
|
24
|
-
kicad_sch_api/geometry/symbol_bbox.py,sha256=5oMVmmimyqF4ITj8wS9yeU3jXdpqG0XhCIvAHlYr9Rg,24688
|
|
25
|
-
kicad_sch_api/library/__init__.py,sha256=NG9UTdcpn25Bl9tPsYs9ED7bvpaVPVdtLMbnxkQkOnU,250
|
|
26
|
-
kicad_sch_api/library/cache.py,sha256=7na88grl465WHwUOGuOzYrrWwjsMBXhXVtxhnaJ9GBY,33208
|
|
27
|
-
kicad_sch_api/utils/__init__.py,sha256=1V_yGgI7jro6MUc4Pviux_WIeJ1wmiYFID186SZwWLQ,277
|
|
28
|
-
kicad_sch_api/utils/validation.py,sha256=XlWGRZJb3cOPYpU9sLQQgC_NASwbi6W-LCN7PzUmaPY,15626
|
|
29
|
-
kicad_sch_api-0.3.4.dist-info/licenses/LICENSE,sha256=Em65Nvte1G9MHc0rHqtYuGkCPcshD588itTa358J6gs,1070
|
|
30
|
-
kicad_sch_api-0.3.4.dist-info/METADATA,sha256=4RHjg7LWStLaT3wRrAkmDhBTLR5l-TRYoc2eXP5zZHs,17183
|
|
31
|
-
kicad_sch_api-0.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
32
|
-
kicad_sch_api-0.3.4.dist-info/entry_points.txt,sha256=VWKsFi2Jv7G_tmio3cNVhhIBfv_OZFaKa-T_ED84lc8,57
|
|
33
|
-
kicad_sch_api-0.3.4.dist-info/top_level.txt,sha256=n0ex4gOJ1b_fARowcGqRzyOGZcHRhc5LZa6_vVgGxcI,14
|
|
34
|
-
kicad_sch_api-0.3.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|