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.
Files changed (66) hide show
  1. kicad_sch_api/__init__.py +67 -2
  2. kicad_sch_api/cli/kicad_to_python.py +169 -0
  3. kicad_sch_api/collections/__init__.py +23 -8
  4. kicad_sch_api/collections/base.py +369 -59
  5. kicad_sch_api/collections/components.py +1376 -187
  6. kicad_sch_api/collections/junctions.py +129 -289
  7. kicad_sch_api/collections/labels.py +391 -287
  8. kicad_sch_api/collections/wires.py +202 -316
  9. kicad_sch_api/core/__init__.py +37 -2
  10. kicad_sch_api/core/component_bounds.py +34 -12
  11. kicad_sch_api/core/components.py +146 -7
  12. kicad_sch_api/core/config.py +25 -12
  13. kicad_sch_api/core/connectivity.py +692 -0
  14. kicad_sch_api/core/exceptions.py +175 -0
  15. kicad_sch_api/core/factories/element_factory.py +3 -1
  16. kicad_sch_api/core/formatter.py +24 -7
  17. kicad_sch_api/core/geometry.py +94 -5
  18. kicad_sch_api/core/managers/__init__.py +4 -0
  19. kicad_sch_api/core/managers/base.py +76 -0
  20. kicad_sch_api/core/managers/file_io.py +3 -1
  21. kicad_sch_api/core/managers/format_sync.py +3 -2
  22. kicad_sch_api/core/managers/graphics.py +3 -2
  23. kicad_sch_api/core/managers/hierarchy.py +661 -0
  24. kicad_sch_api/core/managers/metadata.py +4 -2
  25. kicad_sch_api/core/managers/sheet.py +52 -14
  26. kicad_sch_api/core/managers/text_elements.py +3 -2
  27. kicad_sch_api/core/managers/validation.py +3 -2
  28. kicad_sch_api/core/managers/wire.py +112 -54
  29. kicad_sch_api/core/parsing_utils.py +63 -0
  30. kicad_sch_api/core/pin_utils.py +103 -9
  31. kicad_sch_api/core/schematic.py +343 -29
  32. kicad_sch_api/core/types.py +79 -7
  33. kicad_sch_api/exporters/__init__.py +10 -0
  34. kicad_sch_api/exporters/python_generator.py +610 -0
  35. kicad_sch_api/exporters/templates/default.py.jinja2 +65 -0
  36. kicad_sch_api/geometry/__init__.py +15 -3
  37. kicad_sch_api/geometry/routing.py +211 -0
  38. kicad_sch_api/parsers/elements/label_parser.py +30 -8
  39. kicad_sch_api/parsers/elements/symbol_parser.py +255 -83
  40. kicad_sch_api/utils/logging.py +555 -0
  41. kicad_sch_api/utils/logging_decorators.py +587 -0
  42. kicad_sch_api/utils/validation.py +16 -22
  43. kicad_sch_api/wrappers/__init__.py +14 -0
  44. kicad_sch_api/wrappers/base.py +89 -0
  45. kicad_sch_api/wrappers/wire.py +198 -0
  46. kicad_sch_api-0.5.1.dist-info/METADATA +540 -0
  47. kicad_sch_api-0.5.1.dist-info/RECORD +114 -0
  48. kicad_sch_api-0.5.1.dist-info/entry_points.txt +4 -0
  49. {kicad_sch_api-0.4.1.dist-info → kicad_sch_api-0.5.1.dist-info}/top_level.txt +1 -0
  50. mcp_server/__init__.py +34 -0
  51. mcp_server/example_logging_integration.py +506 -0
  52. mcp_server/models.py +252 -0
  53. mcp_server/server.py +357 -0
  54. mcp_server/tools/__init__.py +32 -0
  55. mcp_server/tools/component_tools.py +516 -0
  56. mcp_server/tools/connectivity_tools.py +532 -0
  57. mcp_server/tools/consolidated_tools.py +1216 -0
  58. mcp_server/tools/pin_discovery.py +333 -0
  59. mcp_server/utils/__init__.py +38 -0
  60. mcp_server/utils/logging.py +127 -0
  61. mcp_server/utils.py +36 -0
  62. kicad_sch_api-0.4.1.dist-info/METADATA +0 -491
  63. kicad_sch_api-0.4.1.dist-info/RECORD +0 -87
  64. kicad_sch_api-0.4.1.dist-info/entry_points.txt +0 -2
  65. {kicad_sch_api-0.4.1.dist-info → kicad_sch_api-0.5.1.dist-info}/WHEEL +0 -0
  66. {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()