kicad-sch-api 0.3.0__py3-none-any.whl → 0.5.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 (112) hide show
  1. kicad_sch_api/__init__.py +68 -3
  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/kicad_to_python.py +169 -0
  8. kicad_sch_api/cli/netlist.py +94 -0
  9. kicad_sch_api/cli/types.py +43 -0
  10. kicad_sch_api/collections/__init__.py +36 -0
  11. kicad_sch_api/collections/base.py +604 -0
  12. kicad_sch_api/collections/components.py +1623 -0
  13. kicad_sch_api/collections/junctions.py +206 -0
  14. kicad_sch_api/collections/labels.py +508 -0
  15. kicad_sch_api/collections/wires.py +292 -0
  16. kicad_sch_api/core/__init__.py +37 -2
  17. kicad_sch_api/core/collections/__init__.py +5 -0
  18. kicad_sch_api/core/collections/base.py +248 -0
  19. kicad_sch_api/core/component_bounds.py +34 -7
  20. kicad_sch_api/core/components.py +213 -52
  21. kicad_sch_api/core/config.py +110 -15
  22. kicad_sch_api/core/connectivity.py +692 -0
  23. kicad_sch_api/core/exceptions.py +175 -0
  24. kicad_sch_api/core/factories/__init__.py +5 -0
  25. kicad_sch_api/core/factories/element_factory.py +278 -0
  26. kicad_sch_api/core/formatter.py +60 -9
  27. kicad_sch_api/core/geometry.py +94 -5
  28. kicad_sch_api/core/junctions.py +26 -75
  29. kicad_sch_api/core/labels.py +324 -0
  30. kicad_sch_api/core/managers/__init__.py +30 -0
  31. kicad_sch_api/core/managers/base.py +76 -0
  32. kicad_sch_api/core/managers/file_io.py +246 -0
  33. kicad_sch_api/core/managers/format_sync.py +502 -0
  34. kicad_sch_api/core/managers/graphics.py +580 -0
  35. kicad_sch_api/core/managers/hierarchy.py +661 -0
  36. kicad_sch_api/core/managers/metadata.py +271 -0
  37. kicad_sch_api/core/managers/sheet.py +492 -0
  38. kicad_sch_api/core/managers/text_elements.py +537 -0
  39. kicad_sch_api/core/managers/validation.py +476 -0
  40. kicad_sch_api/core/managers/wire.py +410 -0
  41. kicad_sch_api/core/nets.py +305 -0
  42. kicad_sch_api/core/no_connects.py +252 -0
  43. kicad_sch_api/core/parser.py +194 -970
  44. kicad_sch_api/core/parsing_utils.py +63 -0
  45. kicad_sch_api/core/pin_utils.py +103 -9
  46. kicad_sch_api/core/schematic.py +1328 -1079
  47. kicad_sch_api/core/texts.py +316 -0
  48. kicad_sch_api/core/types.py +159 -23
  49. kicad_sch_api/core/wires.py +27 -75
  50. kicad_sch_api/exporters/__init__.py +10 -0
  51. kicad_sch_api/exporters/python_generator.py +610 -0
  52. kicad_sch_api/exporters/templates/default.py.jinja2 +65 -0
  53. kicad_sch_api/geometry/__init__.py +38 -0
  54. kicad_sch_api/geometry/font_metrics.py +22 -0
  55. kicad_sch_api/geometry/routing.py +211 -0
  56. kicad_sch_api/geometry/symbol_bbox.py +608 -0
  57. kicad_sch_api/interfaces/__init__.py +17 -0
  58. kicad_sch_api/interfaces/parser.py +76 -0
  59. kicad_sch_api/interfaces/repository.py +70 -0
  60. kicad_sch_api/interfaces/resolver.py +117 -0
  61. kicad_sch_api/parsers/__init__.py +14 -0
  62. kicad_sch_api/parsers/base.py +145 -0
  63. kicad_sch_api/parsers/elements/__init__.py +22 -0
  64. kicad_sch_api/parsers/elements/graphics_parser.py +564 -0
  65. kicad_sch_api/parsers/elements/label_parser.py +216 -0
  66. kicad_sch_api/parsers/elements/library_parser.py +165 -0
  67. kicad_sch_api/parsers/elements/metadata_parser.py +58 -0
  68. kicad_sch_api/parsers/elements/sheet_parser.py +352 -0
  69. kicad_sch_api/parsers/elements/symbol_parser.py +485 -0
  70. kicad_sch_api/parsers/elements/text_parser.py +250 -0
  71. kicad_sch_api/parsers/elements/wire_parser.py +242 -0
  72. kicad_sch_api/parsers/registry.py +155 -0
  73. kicad_sch_api/parsers/utils.py +80 -0
  74. kicad_sch_api/symbols/__init__.py +18 -0
  75. kicad_sch_api/symbols/cache.py +467 -0
  76. kicad_sch_api/symbols/resolver.py +361 -0
  77. kicad_sch_api/symbols/validators.py +504 -0
  78. kicad_sch_api/utils/logging.py +555 -0
  79. kicad_sch_api/utils/logging_decorators.py +587 -0
  80. kicad_sch_api/utils/validation.py +16 -22
  81. kicad_sch_api/validation/__init__.py +25 -0
  82. kicad_sch_api/validation/erc.py +171 -0
  83. kicad_sch_api/validation/erc_models.py +203 -0
  84. kicad_sch_api/validation/pin_matrix.py +243 -0
  85. kicad_sch_api/validation/validators.py +391 -0
  86. kicad_sch_api/wrappers/__init__.py +14 -0
  87. kicad_sch_api/wrappers/base.py +89 -0
  88. kicad_sch_api/wrappers/wire.py +198 -0
  89. kicad_sch_api-0.5.1.dist-info/METADATA +540 -0
  90. kicad_sch_api-0.5.1.dist-info/RECORD +114 -0
  91. kicad_sch_api-0.5.1.dist-info/entry_points.txt +4 -0
  92. {kicad_sch_api-0.3.0.dist-info → kicad_sch_api-0.5.1.dist-info}/top_level.txt +1 -0
  93. mcp_server/__init__.py +34 -0
  94. mcp_server/example_logging_integration.py +506 -0
  95. mcp_server/models.py +252 -0
  96. mcp_server/server.py +357 -0
  97. mcp_server/tools/__init__.py +32 -0
  98. mcp_server/tools/component_tools.py +516 -0
  99. mcp_server/tools/connectivity_tools.py +532 -0
  100. mcp_server/tools/consolidated_tools.py +1216 -0
  101. mcp_server/tools/pin_discovery.py +333 -0
  102. mcp_server/utils/__init__.py +38 -0
  103. mcp_server/utils/logging.py +127 -0
  104. mcp_server/utils.py +36 -0
  105. kicad_sch_api/core/manhattan_routing.py +0 -430
  106. kicad_sch_api/core/simple_manhattan.py +0 -228
  107. kicad_sch_api/core/wire_routing.py +0 -380
  108. kicad_sch_api-0.3.0.dist-info/METADATA +0 -483
  109. kicad_sch_api-0.3.0.dist-info/RECORD +0 -31
  110. kicad_sch_api-0.3.0.dist-info/entry_points.txt +0 -2
  111. {kicad_sch_api-0.3.0.dist-info → kicad_sch_api-0.5.1.dist-info}/WHEEL +0 -0
  112. {kicad_sch_api-0.3.0.dist-info → kicad_sch_api-0.5.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,252 @@
1
+ """
2
+ No-connect element management for KiCAD schematics.
3
+
4
+ This module provides collection classes for managing no-connect elements,
5
+ featuring fast lookup, bulk operations, and validation.
6
+ """
7
+
8
+ import logging
9
+ import uuid
10
+ from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Union
11
+
12
+ from ..utils.validation import SchematicValidator, ValidationError, ValidationIssue
13
+ from .collections import BaseCollection
14
+ from .types import NoConnect, Point
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class NoConnectElement:
20
+ """
21
+ Enhanced wrapper for schematic no-connect elements with modern API.
22
+
23
+ Provides intuitive access to no-connect properties and operations
24
+ while maintaining exact format preservation.
25
+ """
26
+
27
+ def __init__(self, no_connect_data: NoConnect, parent_collection: "NoConnectCollection"):
28
+ """
29
+ Initialize no-connect element wrapper.
30
+
31
+ Args:
32
+ no_connect_data: Underlying no-connect data
33
+ parent_collection: Parent collection for updates
34
+ """
35
+ self._data = no_connect_data
36
+ self._collection = parent_collection
37
+ self._validator = SchematicValidator()
38
+
39
+ # Core properties with validation
40
+ @property
41
+ def uuid(self) -> str:
42
+ """No-connect element UUID."""
43
+ return self._data.uuid
44
+
45
+ @property
46
+ def position(self) -> Point:
47
+ """No-connect position."""
48
+ return self._data.position
49
+
50
+ @position.setter
51
+ def position(self, value: Union[Point, Tuple[float, float]]):
52
+ """Set no-connect position."""
53
+ if isinstance(value, tuple):
54
+ value = Point(value[0], value[1])
55
+ elif not isinstance(value, Point):
56
+ raise ValidationError(f"Position must be Point or tuple, got {type(value)}")
57
+ self._data.position = value
58
+ self._collection._mark_modified()
59
+
60
+ def validate(self) -> List[ValidationIssue]:
61
+ """Validate this no-connect element."""
62
+ return self._validator.validate_no_connect(self._data.__dict__)
63
+
64
+ def to_dict(self) -> Dict[str, Any]:
65
+ """Convert no-connect element to dictionary representation."""
66
+ return {
67
+ "uuid": self.uuid,
68
+ "position": {"x": self.position.x, "y": self.position.y},
69
+ }
70
+
71
+ def __str__(self) -> str:
72
+ """String representation."""
73
+ return f"<NoConnect @ {self.position}>"
74
+
75
+
76
+ class NoConnectCollection(BaseCollection[NoConnectElement]):
77
+ """
78
+ Collection class for efficient no-connect element management.
79
+
80
+ Inherits from BaseCollection for standard operations and adds no-connect-specific
81
+ functionality including position-based indexing.
82
+
83
+ Provides fast lookup, filtering, and bulk operations for schematic no-connect elements.
84
+ """
85
+
86
+ def __init__(self, no_connects: List[NoConnect] = None):
87
+ """
88
+ Initialize no-connect collection.
89
+
90
+ Args:
91
+ no_connects: Initial list of no-connect data
92
+ """
93
+ # Initialize base collection
94
+ super().__init__([], collection_name="no_connects")
95
+
96
+ # Additional no-connect-specific index
97
+ self._position_index: Dict[Tuple[float, float], List[NoConnectElement]] = {}
98
+
99
+ # Add initial no-connects
100
+ if no_connects:
101
+ for no_connect_data in no_connects:
102
+ self._add_to_indexes(NoConnectElement(no_connect_data, self))
103
+
104
+ def add(
105
+ self,
106
+ position: Union[Point, Tuple[float, float]],
107
+ no_connect_uuid: Optional[str] = None,
108
+ ) -> NoConnectElement:
109
+ """
110
+ Add a new no-connect element to the schematic.
111
+
112
+ Args:
113
+ position: No-connect position
114
+ no_connect_uuid: Specific UUID for no-connect (auto-generated if None)
115
+
116
+ Returns:
117
+ Newly created NoConnectElement
118
+
119
+ Raises:
120
+ ValidationError: If no-connect data is invalid
121
+ """
122
+ # Validate inputs
123
+ if isinstance(position, tuple):
124
+ position = Point(position[0], position[1])
125
+ elif not isinstance(position, Point):
126
+ raise ValidationError(f"Position must be Point or tuple, got {type(position)}")
127
+
128
+ # Generate UUID if not provided
129
+ if not no_connect_uuid:
130
+ no_connect_uuid = str(uuid.uuid4())
131
+
132
+ # Check for duplicate UUID
133
+ if no_connect_uuid in self._uuid_index:
134
+ raise ValidationError(f"NoConnect UUID {no_connect_uuid} already exists")
135
+
136
+ # Create no-connect data
137
+ no_connect_data = NoConnect(
138
+ uuid=no_connect_uuid,
139
+ position=position,
140
+ )
141
+
142
+ # Create wrapper and add to collection
143
+ no_connect_element = NoConnectElement(no_connect_data, self)
144
+ self._add_to_indexes(no_connect_element)
145
+ self._mark_modified()
146
+
147
+ logger.debug(f"Added no-connect: {no_connect_element}")
148
+ return no_connect_element
149
+
150
+ # get() method inherited from BaseCollection
151
+
152
+ def remove(self, no_connect_uuid: str) -> bool:
153
+ """
154
+ Remove no-connect by UUID.
155
+
156
+ Args:
157
+ no_connect_uuid: UUID of no-connect to remove
158
+
159
+ Returns:
160
+ True if no-connect was removed, False if not found
161
+ """
162
+ no_connect_element = self.get(no_connect_uuid)
163
+ if not no_connect_element:
164
+ return False
165
+
166
+ # Remove from position index
167
+ pos_key = (no_connect_element.position.x, no_connect_element.position.y)
168
+ if pos_key in self._position_index:
169
+ self._position_index[pos_key].remove(no_connect_element)
170
+ if not self._position_index[pos_key]:
171
+ del self._position_index[pos_key]
172
+
173
+ # Remove using base class method
174
+ super().remove(no_connect_uuid)
175
+
176
+ logger.debug(f"Removed no-connect: {no_connect_element}")
177
+ return True
178
+
179
+ def find_at_position(
180
+ self, position: Union[Point, Tuple[float, float]], tolerance: float = 0.1
181
+ ) -> List[NoConnectElement]:
182
+ """
183
+ Find no-connects at or near a position.
184
+
185
+ Args:
186
+ position: Position to search around
187
+ tolerance: Search tolerance in mm
188
+
189
+ Returns:
190
+ List of matching no-connect elements
191
+ """
192
+ if isinstance(position, tuple):
193
+ position = Point(position[0], position[1])
194
+
195
+ matches = []
196
+ for no_connect_element in self._items:
197
+ distance = no_connect_element.position.distance_to(position)
198
+ if distance <= tolerance:
199
+ matches.append(no_connect_element)
200
+ return matches
201
+
202
+ def filter(self, predicate: Callable[[NoConnectElement], bool]) -> List[NoConnectElement]:
203
+ """
204
+ Filter no-connects by predicate function (delegates to base class find).
205
+
206
+ Args:
207
+ predicate: Function that returns True for no-connects to include
208
+
209
+ Returns:
210
+ List of no-connects matching predicate
211
+ """
212
+ return self.find(predicate)
213
+
214
+ def bulk_update(self, criteria: Callable[[NoConnectElement], bool], updates: Dict[str, Any]):
215
+ """
216
+ Update multiple no-connects matching criteria.
217
+
218
+ Args:
219
+ criteria: Function to select no-connects to update
220
+ updates: Dictionary of property updates
221
+ """
222
+ updated_count = 0
223
+ for no_connect_element in self._items:
224
+ if criteria(no_connect_element):
225
+ for prop, value in updates.items():
226
+ if hasattr(no_connect_element, prop):
227
+ setattr(no_connect_element, prop, value)
228
+ updated_count += 1
229
+
230
+ if updated_count > 0:
231
+ self._mark_modified()
232
+ logger.debug(f"Bulk updated {updated_count} no-connect properties")
233
+
234
+ def clear(self):
235
+ """Remove all no-connects from collection."""
236
+ self._position_index.clear()
237
+ super().clear()
238
+
239
+ def _add_to_indexes(self, no_connect_element: NoConnectElement):
240
+ """Add no-connect to internal indexes (base + position index)."""
241
+ self._add_item(no_connect_element)
242
+
243
+ # Add to position index
244
+ pos_key = (no_connect_element.position.x, no_connect_element.position.y)
245
+ if pos_key not in self._position_index:
246
+ self._position_index[pos_key] = []
247
+ self._position_index[pos_key].append(no_connect_element)
248
+
249
+ # Collection interface methods - __len__, __iter__, __getitem__ inherited from BaseCollection
250
+ def __bool__(self) -> bool:
251
+ """Return True if collection has no-connects."""
252
+ return len(self._items) > 0