kicad-sch-api 0.4.0__py3-none-any.whl → 0.4.1__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.
Files changed (56) hide show
  1. kicad_sch_api/__init__.py +2 -2
  2. kicad_sch_api/cli/__init__.py +45 -0
  3. kicad_sch_api/cli/base.py +302 -0
  4. kicad_sch_api/cli/bom.py +164 -0
  5. kicad_sch_api/cli/erc.py +229 -0
  6. kicad_sch_api/cli/export_docs.py +289 -0
  7. kicad_sch_api/cli/netlist.py +94 -0
  8. kicad_sch_api/cli/types.py +43 -0
  9. kicad_sch_api/core/collections/__init__.py +5 -0
  10. kicad_sch_api/core/collections/base.py +248 -0
  11. kicad_sch_api/core/component_bounds.py +5 -0
  12. kicad_sch_api/core/components.py +62 -45
  13. kicad_sch_api/core/config.py +85 -3
  14. kicad_sch_api/core/factories/__init__.py +5 -0
  15. kicad_sch_api/core/factories/element_factory.py +276 -0
  16. kicad_sch_api/core/junctions.py +26 -75
  17. kicad_sch_api/core/labels.py +28 -52
  18. kicad_sch_api/core/managers/file_io.py +3 -2
  19. kicad_sch_api/core/managers/metadata.py +6 -5
  20. kicad_sch_api/core/managers/validation.py +3 -2
  21. kicad_sch_api/core/managers/wire.py +7 -1
  22. kicad_sch_api/core/nets.py +38 -43
  23. kicad_sch_api/core/no_connects.py +29 -53
  24. kicad_sch_api/core/parser.py +75 -1765
  25. kicad_sch_api/core/schematic.py +211 -148
  26. kicad_sch_api/core/texts.py +28 -55
  27. kicad_sch_api/core/types.py +59 -18
  28. kicad_sch_api/core/wires.py +27 -75
  29. kicad_sch_api/parsers/elements/__init__.py +22 -0
  30. kicad_sch_api/parsers/elements/graphics_parser.py +564 -0
  31. kicad_sch_api/parsers/elements/label_parser.py +194 -0
  32. kicad_sch_api/parsers/elements/library_parser.py +165 -0
  33. kicad_sch_api/parsers/elements/metadata_parser.py +58 -0
  34. kicad_sch_api/parsers/elements/sheet_parser.py +352 -0
  35. kicad_sch_api/parsers/elements/symbol_parser.py +313 -0
  36. kicad_sch_api/parsers/elements/text_parser.py +250 -0
  37. kicad_sch_api/parsers/elements/wire_parser.py +242 -0
  38. kicad_sch_api/parsers/utils.py +80 -0
  39. kicad_sch_api/validation/__init__.py +25 -0
  40. kicad_sch_api/validation/erc.py +171 -0
  41. kicad_sch_api/validation/erc_models.py +203 -0
  42. kicad_sch_api/validation/pin_matrix.py +243 -0
  43. kicad_sch_api/validation/validators.py +391 -0
  44. {kicad_sch_api-0.4.0.dist-info → kicad_sch_api-0.4.1.dist-info}/METADATA +17 -9
  45. kicad_sch_api-0.4.1.dist-info/RECORD +87 -0
  46. kicad_sch_api/core/manhattan_routing.py +0 -430
  47. kicad_sch_api/core/simple_manhattan.py +0 -228
  48. kicad_sch_api/core/wire_routing.py +0 -380
  49. kicad_sch_api/parsers/label_parser.py +0 -254
  50. kicad_sch_api/parsers/symbol_parser.py +0 -222
  51. kicad_sch_api/parsers/wire_parser.py +0 -99
  52. kicad_sch_api-0.4.0.dist-info/RECORD +0 -67
  53. {kicad_sch_api-0.4.0.dist-info → kicad_sch_api-0.4.1.dist-info}/WHEEL +0 -0
  54. {kicad_sch_api-0.4.0.dist-info → kicad_sch_api-0.4.1.dist-info}/entry_points.txt +0 -0
  55. {kicad_sch_api-0.4.0.dist-info → kicad_sch_api-0.4.1.dist-info}/licenses/LICENSE +0 -0
  56. {kicad_sch_api-0.4.0.dist-info → kicad_sch_api-0.4.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,248 @@
1
+ """
2
+ Base collection class for schematic elements.
3
+
4
+ Provides common functionality for all collection types including UUID indexing,
5
+ modification tracking, and standard collection operations.
6
+ """
7
+
8
+ import logging
9
+ from typing import Any, Callable, Dict, Generic, Iterator, List, Optional, Protocol, TypeVar
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class HasUUID(Protocol):
15
+ """Protocol for objects that have a UUID attribute."""
16
+
17
+ @property
18
+ def uuid(self) -> str:
19
+ """UUID of the object."""
20
+ ...
21
+
22
+
23
+ T = TypeVar("T", bound=HasUUID)
24
+
25
+
26
+ class BaseCollection(Generic[T]):
27
+ """
28
+ Generic base class for schematic element collections.
29
+
30
+ Provides common functionality:
31
+ - UUID-based indexing for fast lookup
32
+ - Modification tracking
33
+ - Standard collection operations (__len__, __iter__, __getitem__)
34
+ - Index rebuilding and management
35
+
36
+ Type parameter T must implement the HasUUID protocol.
37
+ """
38
+
39
+ def __init__(self, items: Optional[List[T]] = None, collection_name: str = "items") -> None:
40
+ """
41
+ Initialize base collection.
42
+
43
+ Args:
44
+ items: Initial list of items
45
+ collection_name: Name for logging (e.g., "wires", "junctions")
46
+ """
47
+ self._items: List[T] = items or []
48
+ self._uuid_index: Dict[str, int] = {}
49
+ self._modified = False
50
+ self._collection_name = collection_name
51
+
52
+ # Build UUID index
53
+ self._rebuild_index()
54
+
55
+ logger.debug(f"{collection_name} collection initialized with {len(self._items)} items")
56
+
57
+ def _rebuild_index(self) -> None:
58
+ """Rebuild UUID index for fast lookups."""
59
+ self._uuid_index = {item.uuid: i for i, item in enumerate(self._items)}
60
+
61
+ def _mark_modified(self) -> None:
62
+ """Mark collection as modified."""
63
+ self._modified = True
64
+
65
+ def is_modified(self) -> bool:
66
+ """Check if collection has been modified."""
67
+ return self._modified
68
+
69
+ def reset_modified_flag(self) -> None:
70
+ """Reset modified flag (typically after save)."""
71
+ self._modified = False
72
+
73
+ # Standard collection protocol methods
74
+ def __len__(self) -> int:
75
+ """Return number of items in collection."""
76
+ return len(self._items)
77
+
78
+ def __iter__(self) -> Iterator[T]:
79
+ """Iterate over items in collection."""
80
+ return iter(self._items)
81
+
82
+ def __getitem__(self, key: Any) -> T:
83
+ """
84
+ Get item by UUID or index.
85
+
86
+ Args:
87
+ key: UUID string or integer index
88
+
89
+ Returns:
90
+ Item at the specified location
91
+
92
+ Raises:
93
+ KeyError: If UUID not found
94
+ IndexError: If index out of range
95
+ TypeError: If key is neither string nor int
96
+ """
97
+ if isinstance(key, str):
98
+ # UUID lookup
99
+ if key not in self._uuid_index:
100
+ raise KeyError(f"Item with UUID '{key}' not found")
101
+ return self._items[self._uuid_index[key]]
102
+ elif isinstance(key, int):
103
+ # Index lookup
104
+ return self._items[key]
105
+ else:
106
+ raise TypeError(f"Key must be string (UUID) or int (index), got {type(key)}")
107
+
108
+ def __contains__(self, key: Any) -> bool:
109
+ """
110
+ Check if item exists in collection.
111
+
112
+ Args:
113
+ key: UUID string or item object
114
+
115
+ Returns:
116
+ True if item exists
117
+ """
118
+ if isinstance(key, str):
119
+ return key in self._uuid_index
120
+ elif hasattr(key, "uuid"):
121
+ return key.uuid in self._uuid_index
122
+ return False
123
+
124
+ def get(self, uuid: str) -> Optional[T]:
125
+ """
126
+ Get item by UUID.
127
+
128
+ Args:
129
+ uuid: Item UUID
130
+
131
+ Returns:
132
+ Item if found, None otherwise
133
+ """
134
+ if uuid not in self._uuid_index:
135
+ return None
136
+ return self._items[self._uuid_index[uuid]]
137
+
138
+ def remove(self, uuid: str) -> bool:
139
+ """
140
+ Remove item by UUID.
141
+
142
+ Args:
143
+ uuid: UUID of item to remove
144
+
145
+ Returns:
146
+ True if item was removed, False if not found
147
+ """
148
+ if uuid not in self._uuid_index:
149
+ return False
150
+
151
+ index = self._uuid_index[uuid]
152
+ del self._items[index]
153
+ self._rebuild_index()
154
+ self._mark_modified()
155
+
156
+ logger.debug(f"Removed item with UUID {uuid} from {self._collection_name}")
157
+ return True
158
+
159
+ def clear(self) -> None:
160
+ """Remove all items from collection."""
161
+ self._items.clear()
162
+ self._uuid_index.clear()
163
+ self._mark_modified()
164
+ logger.debug(f"Cleared all items from {self._collection_name}")
165
+
166
+ def find(self, predicate: Callable[[T], bool]) -> List[T]:
167
+ """
168
+ Find all items matching a predicate.
169
+
170
+ Args:
171
+ predicate: Function that returns True for matching items
172
+
173
+ Returns:
174
+ List of matching items
175
+ """
176
+ return [item for item in self._items if predicate(item)]
177
+
178
+ def filter(self, **criteria) -> List[T]:
179
+ """
180
+ Filter items by attribute values.
181
+
182
+ Args:
183
+ **criteria: Attribute name/value pairs to match
184
+
185
+ Returns:
186
+ List of matching items
187
+
188
+ Example:
189
+ collection.filter(wire_type=WireType.BUS, stroke_width=0.5)
190
+ """
191
+ matches = []
192
+ for item in self._items:
193
+ if all(getattr(item, key, None) == value for key, value in criteria.items()):
194
+ matches.append(item)
195
+ return matches
196
+
197
+ def get_statistics(self) -> Dict[str, Any]:
198
+ """
199
+ Get collection statistics.
200
+
201
+ Returns:
202
+ Dictionary with statistics
203
+ """
204
+ return {
205
+ "total_items": len(self._items),
206
+ "modified": self._modified,
207
+ "indexed_items": len(self._uuid_index),
208
+ }
209
+
210
+ def _add_item(self, item: T) -> None:
211
+ """
212
+ Add item to internal storage and index.
213
+
214
+ Args:
215
+ item: Item to add
216
+
217
+ Raises:
218
+ ValueError: If item UUID already exists
219
+ """
220
+ if item.uuid in self._uuid_index:
221
+ raise ValueError(f"Item with UUID '{item.uuid}' already exists")
222
+
223
+ self._items.append(item)
224
+ self._uuid_index[item.uuid] = len(self._items) - 1
225
+ self._mark_modified()
226
+
227
+ def bulk_update(self, criteria: Dict[str, Any], updates: Dict[str, Any]) -> int:
228
+ """
229
+ Update multiple items matching criteria.
230
+
231
+ Args:
232
+ criteria: Attribute name/value pairs to match
233
+ updates: Attribute name/value pairs to update
234
+
235
+ Returns:
236
+ Number of items updated
237
+ """
238
+ matching_items = self.filter(**criteria)
239
+ for item in matching_items:
240
+ for key, value in updates.items():
241
+ if hasattr(item, key):
242
+ setattr(item, key, value)
243
+
244
+ if matching_items:
245
+ self._mark_modified()
246
+
247
+ logger.debug(f"Bulk updated {len(matching_items)} items in {self._collection_name}")
248
+ return len(matching_items)
@@ -384,6 +384,11 @@ def get_component_bounding_box(
384
384
 
385
385
  # Transform to world coordinates
386
386
  # TODO: Handle component rotation in the future
387
+ # NOTE: Currently assumes 0° rotation. For rotated components, bounding box
388
+ # would need to be recalculated after applying rotation matrix. This is a
389
+ # known limitation but doesn't affect most use cases since components are
390
+ # typically placed without rotation, and routing avoids components regardless.
391
+ # Priority: LOW - Would improve accuracy for rotated component placement validation
387
392
  world_bbox = BoundingBox(
388
393
  component.position.x + symbol_bbox.min_x,
389
394
  component.position.y + symbol_bbox.min_y,
@@ -11,6 +11,7 @@ from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Union
11
11
 
12
12
  from ..library.cache import SymbolDefinition, get_symbol_cache
13
13
  from ..utils.validation import SchematicValidator, ValidationError, ValidationIssue
14
+ from .collections import BaseCollection
14
15
  from .ic_manager import ICManager
15
16
  from .types import Point, SchematicPin, SchematicSymbol
16
17
 
@@ -261,10 +262,13 @@ class Component:
261
262
  )
262
263
 
263
264
 
264
- class ComponentCollection:
265
+ class ComponentCollection(BaseCollection[Component]):
265
266
  """
266
267
  Collection class for efficient component management.
267
268
 
269
+ Inherits from BaseCollection for standard operations and adds component-specific
270
+ functionality including reference, lib_id, and value-based indexing.
271
+
268
272
  Provides fast lookup, filtering, and bulk operations for schematic components.
269
273
  Optimized for schematics with hundreds or thousands of components.
270
274
  """
@@ -276,19 +280,19 @@ class ComponentCollection:
276
280
  Args:
277
281
  components: Initial list of component data
278
282
  """
279
- self._components: List[Component] = []
283
+ # Initialize base collection
284
+ super().__init__([], collection_name="components")
285
+
286
+ # Additional component-specific indexes
280
287
  self._reference_index: Dict[str, Component] = {}
281
288
  self._lib_id_index: Dict[str, List[Component]] = {}
282
289
  self._value_index: Dict[str, List[Component]] = {}
283
- self._modified = False
284
290
 
285
291
  # Add initial components
286
292
  if components:
287
293
  for comp_data in components:
288
294
  self._add_to_indexes(Component(comp_data, self))
289
295
 
290
- logger.debug(f"ComponentCollection initialized with {len(self._components)} components")
291
-
292
296
  def add(
293
297
  self,
294
298
  lib_id: str,
@@ -375,7 +379,7 @@ class ComponentCollection:
375
379
 
376
380
  # Add to collection
377
381
  self._add_to_indexes(component)
378
- self._modified = True
382
+ self._mark_modified()
379
383
 
380
384
  logger.info(f"Added component: {reference} ({lib_id})")
381
385
  return component
@@ -432,7 +436,7 @@ class ComponentCollection:
432
436
  component = Component(component_data, self)
433
437
  self._add_to_indexes(component)
434
438
 
435
- self._modified = True
439
+ self._mark_modified()
436
440
  logger.info(
437
441
  f"Added multi-unit IC: {reference_prefix} ({lib_id}) with {len(unit_components)} units"
438
442
  )
@@ -453,9 +457,11 @@ class ComponentCollection:
453
457
  if not component:
454
458
  return False
455
459
 
456
- # Remove from all indexes
460
+ # Remove from component-specific indexes
457
461
  self._remove_from_indexes(component)
458
- self._modified = True
462
+
463
+ # Remove from base collection using UUID
464
+ super().remove(component.uuid)
459
465
 
460
466
  logger.info(f"Removed component: {reference}")
461
467
  return True
@@ -479,7 +485,7 @@ class ComponentCollection:
479
485
  Returns:
480
486
  List of matching components
481
487
  """
482
- results = list(self._components)
488
+ results = list(self._items)
483
489
 
484
490
  # Apply filters
485
491
  if "lib_id" in criteria:
@@ -517,7 +523,7 @@ class ComponentCollection:
517
523
  def filter_by_type(self, component_type: str) -> List[Component]:
518
524
  """Filter components by type (e.g., 'R' for resistors)."""
519
525
  return [
520
- c for c in self._components if c.symbol_name.upper().startswith(component_type.upper())
526
+ c for c in self._items if c.symbol_name.upper().startswith(component_type.upper())
521
527
  ]
522
528
 
523
529
  def in_area(self, x1: float, y1: float, x2: float, y2: float) -> List[Component]:
@@ -532,7 +538,7 @@ class ComponentCollection:
532
538
  point = Point(point[0], point[1])
533
539
 
534
540
  results = []
535
- for component in self._components:
541
+ for component in self._items:
536
542
  if component.position.distance_to(point) <= radius:
537
543
  results.append(component)
538
544
  return results
@@ -564,21 +570,21 @@ class ComponentCollection:
564
570
  component.set_property(key, str(value))
565
571
 
566
572
  if matching:
567
- self._modified = True
573
+ self._mark_modified()
568
574
 
569
575
  logger.info(f"Bulk updated {len(matching)} components")
570
576
  return len(matching)
571
577
 
572
578
  def sort_by_reference(self):
573
579
  """Sort components by reference designator."""
574
- self._components.sort(key=lambda c: c.reference)
580
+ self._items.sort(key=lambda c: c.reference)
575
581
 
576
582
  def sort_by_position(self, by_x: bool = True):
577
583
  """Sort components by position."""
578
584
  if by_x:
579
- self._components.sort(key=lambda c: (c.position.x, c.position.y))
585
+ self._items.sort(key=lambda c: (c.position.x, c.position.y))
580
586
  else:
581
- self._components.sort(key=lambda c: (c.position.y, c.position.x))
587
+ self._items.sort(key=lambda c: (c.position.y, c.position.x))
582
588
 
583
589
  def validate_all(self) -> List[ValidationIssue]:
584
590
  """Validate all components in collection."""
@@ -586,12 +592,12 @@ class ComponentCollection:
586
592
  validator = SchematicValidator()
587
593
 
588
594
  # Validate individual components
589
- for component in self._components:
595
+ for component in self._items:
590
596
  issues = component.validate()
591
597
  all_issues.extend(issues)
592
598
 
593
599
  # Validate collection-level rules
594
- references = [c.reference for c in self._components]
600
+ references = [c.reference for c in self._items]
595
601
  if len(references) != len(set(references)):
596
602
  # Find duplicates
597
603
  seen = set()
@@ -615,7 +621,7 @@ class ComponentCollection:
615
621
  lib_counts = {}
616
622
  value_counts = {}
617
623
 
618
- for component in self._components:
624
+ for component in self._items:
619
625
  # Count by library
620
626
  lib = component.library
621
627
  lib_counts[lib] = lib_counts.get(lib, 0) + 1
@@ -626,36 +632,47 @@ class ComponentCollection:
626
632
  value_counts[value] = value_counts.get(value, 0) + 1
627
633
 
628
634
  return {
629
- "total_components": len(self._components),
635
+ "total_components": len(self._items),
630
636
  "unique_references": len(self._reference_index),
631
637
  "libraries_used": len(lib_counts),
632
638
  "library_breakdown": lib_counts,
633
639
  "most_common_values": sorted(value_counts.items(), key=lambda x: x[1], reverse=True)[
634
640
  :10
635
641
  ],
636
- "modified": self._modified,
642
+ "modified": self.is_modified(),
637
643
  }
638
644
 
639
645
  # Collection interface
640
- def __len__(self) -> int:
641
- """Number of components."""
642
- return len(self._components)
643
-
644
- def __iter__(self) -> Iterator[Component]:
645
- """Iterate over components."""
646
- return iter(self._components)
646
+ # __len__, __iter__ inherited from BaseCollection
647
647
 
648
648
  def __getitem__(self, key: Union[int, str]) -> Component:
649
- """Get component by index or reference."""
650
- if isinstance(key, int):
651
- return self._components[key]
652
- elif isinstance(key, str):
649
+ """
650
+ Get component by index, UUID, or reference.
651
+
652
+ Args:
653
+ key: Integer index, UUID string, or reference string
654
+
655
+ Returns:
656
+ Component at the specified location
657
+
658
+ Raises:
659
+ KeyError: If UUID or reference not found
660
+ IndexError: If index out of range
661
+ TypeError: If key is invalid type
662
+ """
663
+ if isinstance(key, str):
664
+ # Try reference first (most common use case)
653
665
  component = self._reference_index.get(key)
654
- if component is None:
666
+ if component is not None:
667
+ return component
668
+ # Fall back to UUID lookup (from base class)
669
+ try:
670
+ return super().__getitem__(key)
671
+ except KeyError:
655
672
  raise KeyError(f"Component not found: {key}")
656
- return component
657
673
  else:
658
- raise TypeError(f"Invalid key type: {type(key)}")
674
+ # Integer index (from base class)
675
+ return super().__getitem__(key)
659
676
 
660
677
  def __contains__(self, reference: str) -> bool:
661
678
  """Check if reference exists."""
@@ -663,8 +680,11 @@ class ComponentCollection:
663
680
 
664
681
  # Internal methods
665
682
  def _add_to_indexes(self, component: Component):
666
- """Add component to all indexes."""
667
- self._components.append(component)
683
+ """Add component to all indexes (base + component-specific)."""
684
+ # Add to base collection (UUID index)
685
+ self._add_item(component)
686
+
687
+ # Add to reference index
668
688
  self._reference_index[component.reference] = component
669
689
 
670
690
  # Add to lib_id index
@@ -681,8 +701,8 @@ class ComponentCollection:
681
701
  self._value_index[value].append(component)
682
702
 
683
703
  def _remove_from_indexes(self, component: Component):
684
- """Remove component from all indexes."""
685
- self._components.remove(component)
704
+ """Remove component from component-specific indexes (not base UUID index)."""
705
+ # Remove from reference index
686
706
  del self._reference_index[component.reference]
687
707
 
688
708
  # Remove from lib_id index
@@ -705,10 +725,7 @@ class ComponentCollection:
705
725
  component = self._reference_index[old_ref]
706
726
  del self._reference_index[old_ref]
707
727
  self._reference_index[new_ref] = component
708
-
709
- def _mark_modified(self):
710
- """Mark collection as modified."""
711
- self._modified = True
728
+ # Note: UUID doesn't change when reference changes, so base index is unaffected
712
729
 
713
730
  def _generate_reference(self, lib_id: str) -> str:
714
731
  """Generate unique reference for component."""
@@ -730,7 +747,7 @@ class ComponentCollection:
730
747
  grid_size = 10.0 # 10mm grid
731
748
  max_per_row = 10
732
749
 
733
- row = len(self._components) // max_per_row
734
- col = len(self._components) % max_per_row
750
+ row = len(self._items) // max_per_row
751
+ col = len(self._items) % max_per_row
735
752
 
736
753
  return Point(col * grid_size, row * grid_size)
@@ -6,8 +6,8 @@ This module centralizes all magic numbers and configuration values
6
6
  to make them easily configurable and maintainable.
7
7
  """
8
8
 
9
- from dataclasses import dataclass
10
- from typing import Any, Dict, Tuple
9
+ from dataclasses import dataclass, field
10
+ from typing import Any, Dict, List, Tuple
11
11
 
12
12
 
13
13
  @dataclass
@@ -57,20 +57,102 @@ class DefaultValues:
57
57
 
58
58
  project_name: str = "untitled"
59
59
  stroke_width: float = 0.0
60
+ stroke_type: str = "default"
61
+ fill_type: str = "none"
60
62
  font_size: float = 1.27
61
63
  pin_name_size: float = 1.27
62
64
  pin_number_size: float = 1.27
63
65
 
64
66
 
67
+ @dataclass
68
+ class FileFormatConstants:
69
+ """KiCAD file format identifiers and version strings."""
70
+
71
+ file_type: str = "kicad_sch"
72
+ generator_default: str = "eeschema"
73
+ version_default: str = "20250114"
74
+ generator_version_default: str = "9.0"
75
+
76
+
77
+ @dataclass
78
+ class PaperSizeConstants:
79
+ """Standard paper size definitions."""
80
+
81
+ default: str = "A4"
82
+ valid_sizes: List[str] = field(
83
+ default_factory=lambda: ["A4", "A3", "A2", "A1", "A0", "Letter", "Legal", "Tabloid"]
84
+ )
85
+
86
+
87
+ @dataclass
88
+ class FieldNames:
89
+ """Common S-expression field names to avoid typos."""
90
+
91
+ # File structure
92
+ version: str = "version"
93
+ generator: str = "generator"
94
+ generator_version: str = "generator_version"
95
+ uuid: str = "uuid"
96
+ paper: str = "paper"
97
+
98
+ # Positioning
99
+ at: str = "at"
100
+ xy: str = "xy"
101
+ pts: str = "pts"
102
+ start: str = "start"
103
+ end: str = "end"
104
+ mid: str = "mid"
105
+ center: str = "center"
106
+ radius: str = "radius"
107
+
108
+ # Styling
109
+ stroke: str = "stroke"
110
+ fill: str = "fill"
111
+ width: str = "width"
112
+ type: str = "type"
113
+ color: str = "color"
114
+
115
+ # Text/Font
116
+ font: str = "font"
117
+ size: str = "size"
118
+ effects: str = "effects"
119
+
120
+ # Components
121
+ pin: str = "pin"
122
+ property: str = "property"
123
+ symbol: str = "symbol"
124
+ lib_id: str = "lib_id"
125
+
126
+ # Graphics
127
+ polyline: str = "polyline"
128
+ arc: str = "arc"
129
+ circle: str = "circle"
130
+ rectangle: str = "rectangle"
131
+ bezier: str = "bezier"
132
+
133
+ # Connection elements
134
+ wire: str = "wire"
135
+ junction: str = "junction"
136
+ no_connect: str = "no_connect"
137
+ label: str = "label"
138
+
139
+ # Hierarchical
140
+ sheet: str = "sheet"
141
+ sheet_instances: str = "sheet_instances"
142
+
143
+
65
144
  class KiCADConfig:
66
145
  """Central configuration class for KiCAD schematic API."""
67
146
 
68
- def __init__(self):
147
+ def __init__(self) -> None:
69
148
  self.properties = PropertyOffsets()
70
149
  self.grid = GridSettings()
71
150
  self.sheet = SheetSettings()
72
151
  self.tolerance = ToleranceSettings()
73
152
  self.defaults = DefaultValues()
153
+ self.file_format = FileFormatConstants()
154
+ self.paper = PaperSizeConstants()
155
+ self.fields = FieldNames()
74
156
 
75
157
  # Names that should not generate title_block (for backward compatibility)
76
158
  # Include test schematic names to maintain reference compatibility
@@ -0,0 +1,5 @@
1
+ """Factory classes for creating schematic elements."""
2
+
3
+ from .element_factory import ElementFactory
4
+
5
+ __all__ = ["ElementFactory"]