kicad-sch-api 0.3.4__py3-none-any.whl → 0.3.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.

Files changed (34) hide show
  1. kicad_sch_api/collections/__init__.py +21 -0
  2. kicad_sch_api/collections/base.py +296 -0
  3. kicad_sch_api/collections/components.py +422 -0
  4. kicad_sch_api/collections/junctions.py +378 -0
  5. kicad_sch_api/collections/labels.py +412 -0
  6. kicad_sch_api/collections/wires.py +407 -0
  7. kicad_sch_api/core/labels.py +348 -0
  8. kicad_sch_api/core/nets.py +310 -0
  9. kicad_sch_api/core/no_connects.py +274 -0
  10. kicad_sch_api/core/schematic.py +136 -2
  11. kicad_sch_api/core/texts.py +343 -0
  12. kicad_sch_api/core/types.py +12 -0
  13. kicad_sch_api/geometry/symbol_bbox.py +26 -32
  14. kicad_sch_api/interfaces/__init__.py +17 -0
  15. kicad_sch_api/interfaces/parser.py +76 -0
  16. kicad_sch_api/interfaces/repository.py +70 -0
  17. kicad_sch_api/interfaces/resolver.py +117 -0
  18. kicad_sch_api/parsers/__init__.py +14 -0
  19. kicad_sch_api/parsers/base.py +148 -0
  20. kicad_sch_api/parsers/label_parser.py +254 -0
  21. kicad_sch_api/parsers/registry.py +153 -0
  22. kicad_sch_api/parsers/symbol_parser.py +227 -0
  23. kicad_sch_api/parsers/wire_parser.py +99 -0
  24. kicad_sch_api/symbols/__init__.py +18 -0
  25. kicad_sch_api/symbols/cache.py +470 -0
  26. kicad_sch_api/symbols/resolver.py +367 -0
  27. kicad_sch_api/symbols/validators.py +453 -0
  28. {kicad_sch_api-0.3.4.dist-info → kicad_sch_api-0.3.5.dist-info}/METADATA +1 -1
  29. kicad_sch_api-0.3.5.dist-info/RECORD +58 -0
  30. kicad_sch_api-0.3.4.dist-info/RECORD +0 -34
  31. {kicad_sch_api-0.3.4.dist-info → kicad_sch_api-0.3.5.dist-info}/WHEEL +0 -0
  32. {kicad_sch_api-0.3.4.dist-info → kicad_sch_api-0.3.5.dist-info}/entry_points.txt +0 -0
  33. {kicad_sch_api-0.3.4.dist-info → kicad_sch_api-0.3.5.dist-info}/licenses/LICENSE +0 -0
  34. {kicad_sch_api-0.3.4.dist-info → kicad_sch_api-0.3.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,21 @@
1
+ """
2
+ Modern collection architecture for KiCAD schematic elements.
3
+
4
+ This module provides a unified collection framework that eliminates code duplication
5
+ across component, wire, and junction collections while providing consistent
6
+ indexing, performance optimization, and management capabilities.
7
+ """
8
+
9
+ from .base import IndexedCollection
10
+ from .components import ComponentCollection
11
+ from .wires import WireCollection
12
+ from .junctions import JunctionCollection
13
+ from .labels import LabelCollection
14
+
15
+ __all__ = [
16
+ "IndexedCollection",
17
+ "ComponentCollection",
18
+ "WireCollection",
19
+ "JunctionCollection",
20
+ "LabelCollection",
21
+ ]
@@ -0,0 +1,296 @@
1
+ """
2
+ Base collection class for unified schematic element management.
3
+
4
+ Provides common functionality for indexing, searching, and managing
5
+ collections of schematic elements with performance optimization.
6
+ """
7
+
8
+ import logging
9
+ from abc import ABC, abstractmethod
10
+ from typing import Any, Callable, Dict, Generic, Iterator, List, Optional, TypeVar, Union
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ T = TypeVar('T') # Type variable for collection items
15
+
16
+
17
+ class IndexedCollection(Generic[T], ABC):
18
+ """
19
+ Base class for all schematic element collections with automatic indexing.
20
+
21
+ Provides unified functionality for:
22
+ - UUID-based fast lookups
23
+ - Automatic index management
24
+ - Modification tracking
25
+ - Consistent iteration interface
26
+ - Performance optimization for large collections
27
+ """
28
+
29
+ def __init__(self, items: Optional[List[T]] = None):
30
+ """
31
+ Initialize indexed collection.
32
+
33
+ Args:
34
+ items: Initial list of items to add to collection
35
+ """
36
+ self._items: List[T] = []
37
+ self._uuid_index: Dict[str, int] = {}
38
+ self._modified = False
39
+ self._dirty_indexes = False
40
+
41
+ # Add initial items if provided
42
+ if items:
43
+ for item in items:
44
+ self._add_item_to_collection(item)
45
+
46
+ logger.debug(f"{self.__class__.__name__} initialized with {len(self._items)} items")
47
+
48
+ # Abstract methods for subclasses to implement
49
+ @abstractmethod
50
+ def _get_item_uuid(self, item: T) -> str:
51
+ """
52
+ Extract UUID from an item.
53
+
54
+ Args:
55
+ item: Item to extract UUID from
56
+
57
+ Returns:
58
+ UUID string for the item
59
+ """
60
+ pass
61
+
62
+ @abstractmethod
63
+ def _create_item(self, **kwargs) -> T:
64
+ """
65
+ Create a new item with given parameters.
66
+
67
+ Args:
68
+ **kwargs: Parameters for item creation
69
+
70
+ Returns:
71
+ Newly created item
72
+ """
73
+ pass
74
+
75
+ @abstractmethod
76
+ def _build_additional_indexes(self) -> None:
77
+ """
78
+ Build any additional indexes specific to the collection type.
79
+
80
+ Called after UUID index is rebuilt. Subclasses should implement
81
+ this to maintain their own specialized indexes.
82
+ """
83
+ pass
84
+
85
+ # Core collection operations
86
+ def add(self, item: T) -> T:
87
+ """
88
+ Add an item to the collection.
89
+
90
+ Args:
91
+ item: Item to add
92
+
93
+ Returns:
94
+ The added item
95
+
96
+ Raises:
97
+ ValueError: If item with same UUID already exists
98
+ """
99
+ uuid_str = self._get_item_uuid(item)
100
+
101
+ # Ensure indexes are current before checking for duplicates
102
+ self._ensure_indexes_current()
103
+
104
+ # Check for duplicate UUID
105
+ if uuid_str in self._uuid_index:
106
+ raise ValueError(f"Item with UUID {uuid_str} already exists")
107
+
108
+ return self._add_item_to_collection(item)
109
+
110
+ def remove(self, identifier: Union[str, T]) -> bool:
111
+ """
112
+ Remove an item from the collection.
113
+
114
+ Args:
115
+ identifier: UUID string or item instance to remove
116
+
117
+ Returns:
118
+ True if item was removed, False if not found
119
+ """
120
+ self._ensure_indexes_current()
121
+
122
+ if isinstance(identifier, str):
123
+ # Remove by UUID
124
+ if identifier not in self._uuid_index:
125
+ return False
126
+
127
+ index = self._uuid_index[identifier]
128
+ item = self._items[index]
129
+ else:
130
+ # Remove by item instance
131
+ item = identifier
132
+ uuid_str = self._get_item_uuid(item)
133
+ if uuid_str not in self._uuid_index:
134
+ return False
135
+
136
+ index = self._uuid_index[uuid_str]
137
+
138
+ # Remove from main list
139
+ self._items.pop(index)
140
+ self._mark_modified()
141
+ self._mark_indexes_dirty()
142
+
143
+ logger.debug(f"Removed item with UUID {self._get_item_uuid(item)}")
144
+ return True
145
+
146
+ def get(self, uuid: str) -> Optional[T]:
147
+ """
148
+ Get an item by UUID.
149
+
150
+ Args:
151
+ uuid: UUID to search for
152
+
153
+ Returns:
154
+ Item if found, None otherwise
155
+ """
156
+ self._ensure_indexes_current()
157
+
158
+ if uuid in self._uuid_index:
159
+ index = self._uuid_index[uuid]
160
+ return self._items[index]
161
+
162
+ return None
163
+
164
+ def find(self, predicate: Callable[[T], bool]) -> List[T]:
165
+ """
166
+ Find all items matching a predicate.
167
+
168
+ Args:
169
+ predicate: Function that returns True for matching items
170
+
171
+ Returns:
172
+ List of matching items
173
+ """
174
+ return [item for item in self._items if predicate(item)]
175
+
176
+ def filter(self, **criteria) -> List[T]:
177
+ """
178
+ Filter items by attribute criteria.
179
+
180
+ Args:
181
+ **criteria: Attribute name/value pairs to match
182
+
183
+ Returns:
184
+ List of matching items
185
+ """
186
+ def matches_criteria(item: T) -> bool:
187
+ for attr, value in criteria.items():
188
+ if not hasattr(item, attr) or getattr(item, attr) != value:
189
+ return False
190
+ return True
191
+
192
+ return self.find(matches_criteria)
193
+
194
+ def clear(self) -> None:
195
+ """Clear all items from the collection."""
196
+ self._items.clear()
197
+ self._uuid_index.clear()
198
+ self._mark_modified()
199
+ logger.debug(f"Cleared all items from {self.__class__.__name__}")
200
+
201
+ # Collection interface methods
202
+ def __len__(self) -> int:
203
+ """Number of items in collection."""
204
+ return len(self._items)
205
+
206
+ def __iter__(self) -> Iterator[T]:
207
+ """Iterate over items in collection."""
208
+ return iter(self._items)
209
+
210
+ def __contains__(self, item: Union[str, T]) -> bool:
211
+ """Check if item or UUID is in collection."""
212
+ if isinstance(item, str):
213
+ # Check by UUID
214
+ self._ensure_indexes_current()
215
+ return item in self._uuid_index
216
+ else:
217
+ # Check by item instance
218
+ uuid_str = self._get_item_uuid(item)
219
+ self._ensure_indexes_current()
220
+ return uuid_str in self._uuid_index
221
+
222
+ def __getitem__(self, index: int) -> T:
223
+ """Get item by index."""
224
+ return self._items[index]
225
+
226
+ # Internal methods
227
+ def _add_item_to_collection(self, item: T) -> T:
228
+ """
229
+ Internal method to add item to collection.
230
+
231
+ Args:
232
+ item: Item to add
233
+
234
+ Returns:
235
+ The added item
236
+ """
237
+ self._items.append(item)
238
+ self._mark_modified()
239
+ self._mark_indexes_dirty()
240
+
241
+ logger.debug(f"Added item with UUID {self._get_item_uuid(item)}")
242
+ return item
243
+
244
+ def _mark_modified(self) -> None:
245
+ """Mark collection as modified."""
246
+ self._modified = True
247
+
248
+ def _mark_indexes_dirty(self) -> None:
249
+ """Mark indexes as needing rebuild."""
250
+ self._dirty_indexes = True
251
+
252
+ def _ensure_indexes_current(self) -> None:
253
+ """Ensure all indexes are current."""
254
+ if self._dirty_indexes:
255
+ self._rebuild_indexes()
256
+
257
+ def _rebuild_indexes(self) -> None:
258
+ """Rebuild all indexes."""
259
+ # Rebuild UUID index
260
+ self._uuid_index = {
261
+ self._get_item_uuid(item): i
262
+ for i, item in enumerate(self._items)
263
+ }
264
+
265
+ # Let subclasses rebuild their additional indexes
266
+ self._build_additional_indexes()
267
+
268
+ self._dirty_indexes = False
269
+ logger.debug(f"Rebuilt indexes for {self.__class__.__name__}")
270
+
271
+ # Collection statistics and debugging
272
+ def get_statistics(self) -> Dict[str, Any]:
273
+ """
274
+ Get collection statistics for debugging and monitoring.
275
+
276
+ Returns:
277
+ Dictionary with collection statistics
278
+ """
279
+ self._ensure_indexes_current()
280
+ return {
281
+ "item_count": len(self._items),
282
+ "uuid_index_size": len(self._uuid_index),
283
+ "modified": self._modified,
284
+ "indexes_dirty": self._dirty_indexes,
285
+ "collection_type": self.__class__.__name__
286
+ }
287
+
288
+ @property
289
+ def is_modified(self) -> bool:
290
+ """Whether collection has been modified."""
291
+ return self._modified
292
+
293
+ def mark_clean(self) -> None:
294
+ """Mark collection as clean (not modified)."""
295
+ self._modified = False
296
+ logger.debug(f"Marked {self.__class__.__name__} as clean")