kicad-sch-api 0.4.1__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.
- kicad_sch_api/__init__.py +67 -2
- kicad_sch_api/cli/kicad_to_python.py +169 -0
- kicad_sch_api/collections/__init__.py +23 -8
- kicad_sch_api/collections/base.py +369 -59
- kicad_sch_api/collections/components.py +1376 -187
- kicad_sch_api/collections/junctions.py +129 -289
- kicad_sch_api/collections/labels.py +391 -287
- kicad_sch_api/collections/wires.py +202 -316
- kicad_sch_api/core/__init__.py +37 -2
- kicad_sch_api/core/component_bounds.py +34 -12
- kicad_sch_api/core/components.py +146 -7
- kicad_sch_api/core/config.py +25 -12
- kicad_sch_api/core/connectivity.py +692 -0
- kicad_sch_api/core/exceptions.py +175 -0
- kicad_sch_api/core/factories/element_factory.py +3 -1
- kicad_sch_api/core/formatter.py +24 -7
- kicad_sch_api/core/geometry.py +94 -5
- kicad_sch_api/core/managers/__init__.py +4 -0
- kicad_sch_api/core/managers/base.py +76 -0
- kicad_sch_api/core/managers/file_io.py +3 -1
- kicad_sch_api/core/managers/format_sync.py +3 -2
- kicad_sch_api/core/managers/graphics.py +3 -2
- kicad_sch_api/core/managers/hierarchy.py +661 -0
- kicad_sch_api/core/managers/metadata.py +4 -2
- kicad_sch_api/core/managers/sheet.py +52 -14
- kicad_sch_api/core/managers/text_elements.py +3 -2
- kicad_sch_api/core/managers/validation.py +3 -2
- kicad_sch_api/core/managers/wire.py +112 -54
- kicad_sch_api/core/parsing_utils.py +63 -0
- kicad_sch_api/core/pin_utils.py +103 -9
- kicad_sch_api/core/schematic.py +343 -29
- kicad_sch_api/core/types.py +79 -7
- kicad_sch_api/exporters/__init__.py +10 -0
- kicad_sch_api/exporters/python_generator.py +610 -0
- kicad_sch_api/exporters/templates/default.py.jinja2 +65 -0
- kicad_sch_api/geometry/__init__.py +15 -3
- kicad_sch_api/geometry/routing.py +211 -0
- kicad_sch_api/parsers/elements/label_parser.py +30 -8
- kicad_sch_api/parsers/elements/symbol_parser.py +255 -83
- kicad_sch_api/utils/logging.py +555 -0
- kicad_sch_api/utils/logging_decorators.py +587 -0
- kicad_sch_api/utils/validation.py +16 -22
- kicad_sch_api/wrappers/__init__.py +14 -0
- kicad_sch_api/wrappers/base.py +89 -0
- kicad_sch_api/wrappers/wire.py +198 -0
- kicad_sch_api-0.5.1.dist-info/METADATA +540 -0
- kicad_sch_api-0.5.1.dist-info/RECORD +114 -0
- kicad_sch_api-0.5.1.dist-info/entry_points.txt +4 -0
- {kicad_sch_api-0.4.1.dist-info → kicad_sch_api-0.5.1.dist-info}/top_level.txt +1 -0
- mcp_server/__init__.py +34 -0
- mcp_server/example_logging_integration.py +506 -0
- mcp_server/models.py +252 -0
- mcp_server/server.py +357 -0
- mcp_server/tools/__init__.py +32 -0
- mcp_server/tools/component_tools.py +516 -0
- mcp_server/tools/connectivity_tools.py +532 -0
- mcp_server/tools/consolidated_tools.py +1216 -0
- mcp_server/tools/pin_discovery.py +333 -0
- mcp_server/utils/__init__.py +38 -0
- mcp_server/utils/logging.py +127 -0
- mcp_server/utils.py +36 -0
- kicad_sch_api-0.4.1.dist-info/METADATA +0 -491
- kicad_sch_api-0.4.1.dist-info/RECORD +0 -87
- kicad_sch_api-0.4.1.dist-info/entry_points.txt +0 -2
- {kicad_sch_api-0.4.1.dist-info → kicad_sch_api-0.5.1.dist-info}/WHEEL +0 -0
- {kicad_sch_api-0.4.1.dist-info → kicad_sch_api-0.5.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -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()
|