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.
- kicad_sch_api/__init__.py +68 -3
- kicad_sch_api/cli/__init__.py +45 -0
- kicad_sch_api/cli/base.py +302 -0
- kicad_sch_api/cli/bom.py +164 -0
- kicad_sch_api/cli/erc.py +229 -0
- kicad_sch_api/cli/export_docs.py +289 -0
- kicad_sch_api/cli/kicad_to_python.py +169 -0
- kicad_sch_api/cli/netlist.py +94 -0
- kicad_sch_api/cli/types.py +43 -0
- kicad_sch_api/collections/__init__.py +36 -0
- kicad_sch_api/collections/base.py +604 -0
- kicad_sch_api/collections/components.py +1623 -0
- kicad_sch_api/collections/junctions.py +206 -0
- kicad_sch_api/collections/labels.py +508 -0
- kicad_sch_api/collections/wires.py +292 -0
- kicad_sch_api/core/__init__.py +37 -2
- kicad_sch_api/core/collections/__init__.py +5 -0
- kicad_sch_api/core/collections/base.py +248 -0
- kicad_sch_api/core/component_bounds.py +34 -7
- kicad_sch_api/core/components.py +213 -52
- kicad_sch_api/core/config.py +110 -15
- kicad_sch_api/core/connectivity.py +692 -0
- kicad_sch_api/core/exceptions.py +175 -0
- kicad_sch_api/core/factories/__init__.py +5 -0
- kicad_sch_api/core/factories/element_factory.py +278 -0
- kicad_sch_api/core/formatter.py +60 -9
- kicad_sch_api/core/geometry.py +94 -5
- kicad_sch_api/core/junctions.py +26 -75
- kicad_sch_api/core/labels.py +324 -0
- kicad_sch_api/core/managers/__init__.py +30 -0
- kicad_sch_api/core/managers/base.py +76 -0
- kicad_sch_api/core/managers/file_io.py +246 -0
- kicad_sch_api/core/managers/format_sync.py +502 -0
- kicad_sch_api/core/managers/graphics.py +580 -0
- kicad_sch_api/core/managers/hierarchy.py +661 -0
- kicad_sch_api/core/managers/metadata.py +271 -0
- kicad_sch_api/core/managers/sheet.py +492 -0
- kicad_sch_api/core/managers/text_elements.py +537 -0
- kicad_sch_api/core/managers/validation.py +476 -0
- kicad_sch_api/core/managers/wire.py +410 -0
- kicad_sch_api/core/nets.py +305 -0
- kicad_sch_api/core/no_connects.py +252 -0
- kicad_sch_api/core/parser.py +194 -970
- kicad_sch_api/core/parsing_utils.py +63 -0
- kicad_sch_api/core/pin_utils.py +103 -9
- kicad_sch_api/core/schematic.py +1328 -1079
- kicad_sch_api/core/texts.py +316 -0
- kicad_sch_api/core/types.py +159 -23
- kicad_sch_api/core/wires.py +27 -75
- 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 +38 -0
- kicad_sch_api/geometry/font_metrics.py +22 -0
- kicad_sch_api/geometry/routing.py +211 -0
- kicad_sch_api/geometry/symbol_bbox.py +608 -0
- kicad_sch_api/interfaces/__init__.py +17 -0
- kicad_sch_api/interfaces/parser.py +76 -0
- kicad_sch_api/interfaces/repository.py +70 -0
- kicad_sch_api/interfaces/resolver.py +117 -0
- kicad_sch_api/parsers/__init__.py +14 -0
- kicad_sch_api/parsers/base.py +145 -0
- kicad_sch_api/parsers/elements/__init__.py +22 -0
- kicad_sch_api/parsers/elements/graphics_parser.py +564 -0
- kicad_sch_api/parsers/elements/label_parser.py +216 -0
- kicad_sch_api/parsers/elements/library_parser.py +165 -0
- kicad_sch_api/parsers/elements/metadata_parser.py +58 -0
- kicad_sch_api/parsers/elements/sheet_parser.py +352 -0
- kicad_sch_api/parsers/elements/symbol_parser.py +485 -0
- kicad_sch_api/parsers/elements/text_parser.py +250 -0
- kicad_sch_api/parsers/elements/wire_parser.py +242 -0
- kicad_sch_api/parsers/registry.py +155 -0
- kicad_sch_api/parsers/utils.py +80 -0
- kicad_sch_api/symbols/__init__.py +18 -0
- kicad_sch_api/symbols/cache.py +467 -0
- kicad_sch_api/symbols/resolver.py +361 -0
- kicad_sch_api/symbols/validators.py +504 -0
- 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/validation/__init__.py +25 -0
- kicad_sch_api/validation/erc.py +171 -0
- kicad_sch_api/validation/erc_models.py +203 -0
- kicad_sch_api/validation/pin_matrix.py +243 -0
- kicad_sch_api/validation/validators.py +391 -0
- 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.3.0.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/core/manhattan_routing.py +0 -430
- kicad_sch_api/core/simple_manhattan.py +0 -228
- kicad_sch_api/core/wire_routing.py +0 -380
- kicad_sch_api-0.3.0.dist-info/METADATA +0 -483
- kicad_sch_api-0.3.0.dist-info/RECORD +0 -31
- kicad_sch_api-0.3.0.dist-info/entry_points.txt +0 -2
- {kicad_sch_api-0.3.0.dist-info → kicad_sch_api-0.5.1.dist-info}/WHEEL +0 -0
- {kicad_sch_api-0.3.0.dist-info → kicad_sch_api-0.5.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Base manager class for schematic operations.
|
|
2
|
+
|
|
3
|
+
Provides a consistent interface for all manager classes and enforces
|
|
4
|
+
common patterns for validation and data access.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC
|
|
8
|
+
from typing import Any, Dict, List, Optional, TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from ...utils.validation import ValidationIssue
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from ..schematic import Schematic
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BaseManager(ABC):
|
|
17
|
+
"""Base class for all schematic managers.
|
|
18
|
+
|
|
19
|
+
Managers encapsulate complex operations and keep Schematic focused.
|
|
20
|
+
This base class provides a consistent interface and common utilities
|
|
21
|
+
for all managers.
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
_data: Reference to schematic data (optional, varies by manager)
|
|
25
|
+
_schematic: Reference to parent Schematic instance (optional)
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, schematic_data: Optional[Dict[str, Any]] = None, **kwargs):
|
|
29
|
+
"""Initialize manager with schematic data reference.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
schematic_data: Reference to schematic data dictionary (optional)
|
|
33
|
+
**kwargs: Additional manager-specific parameters
|
|
34
|
+
"""
|
|
35
|
+
self._data = schematic_data
|
|
36
|
+
self._schematic: Optional["Schematic"] = None
|
|
37
|
+
|
|
38
|
+
def set_schematic(self, schematic: "Schematic") -> None:
|
|
39
|
+
"""Set reference to parent schematic.
|
|
40
|
+
|
|
41
|
+
This is called by Schematic after manager initialization to establish
|
|
42
|
+
bidirectional relationship.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
schematic: The parent Schematic instance
|
|
46
|
+
"""
|
|
47
|
+
self._schematic = schematic
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def schematic(self) -> Optional["Schematic"]:
|
|
51
|
+
"""Get the parent schematic instance.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
The parent Schematic, or None if not set
|
|
55
|
+
"""
|
|
56
|
+
return self._schematic
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def data(self) -> Optional[Dict[str, Any]]:
|
|
60
|
+
"""Get the schematic data dictionary.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
The schematic data, or None if not set
|
|
64
|
+
"""
|
|
65
|
+
return self._data
|
|
66
|
+
|
|
67
|
+
def validate(self) -> List[ValidationIssue]:
|
|
68
|
+
"""Validate managed elements.
|
|
69
|
+
|
|
70
|
+
This is an optional method that managers can override to provide
|
|
71
|
+
validation. Default implementation returns empty list (no issues).
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
List of validation issues found
|
|
75
|
+
"""
|
|
76
|
+
return []
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"""
|
|
2
|
+
File I/O Manager for KiCAD schematic operations.
|
|
3
|
+
|
|
4
|
+
Handles all file system interactions including loading, saving, and backup operations
|
|
5
|
+
while maintaining exact format preservation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import time
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Dict, Optional, Union
|
|
12
|
+
|
|
13
|
+
from ...utils.validation import ValidationError
|
|
14
|
+
from ..config import config
|
|
15
|
+
from ..formatter import ExactFormatter
|
|
16
|
+
from ..parser import SExpressionParser
|
|
17
|
+
from .base import BaseManager
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class FileIOManager(BaseManager):
|
|
23
|
+
"""
|
|
24
|
+
Manages file I/O operations for KiCAD schematics.
|
|
25
|
+
|
|
26
|
+
Responsible for:
|
|
27
|
+
- Loading schematic files with validation
|
|
28
|
+
- Saving with format preservation
|
|
29
|
+
- Creating backup files
|
|
30
|
+
- Managing file paths and metadata
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self):
|
|
34
|
+
"""Initialize the FileIOManager."""
|
|
35
|
+
super().__init__()
|
|
36
|
+
self._parser = SExpressionParser(preserve_format=True)
|
|
37
|
+
self._formatter = ExactFormatter()
|
|
38
|
+
|
|
39
|
+
def load_schematic(self, file_path: Union[str, Path]) -> Dict[str, Any]:
|
|
40
|
+
"""
|
|
41
|
+
Load a KiCAD schematic file.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
file_path: Path to .kicad_sch file
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Parsed schematic data
|
|
48
|
+
|
|
49
|
+
Raises:
|
|
50
|
+
FileNotFoundError: If file doesn't exist
|
|
51
|
+
ValidationError: If file is invalid or corrupted
|
|
52
|
+
"""
|
|
53
|
+
start_time = time.time()
|
|
54
|
+
file_path = Path(file_path)
|
|
55
|
+
|
|
56
|
+
if not file_path.exists():
|
|
57
|
+
raise FileNotFoundError(f"Schematic file not found: {file_path}")
|
|
58
|
+
|
|
59
|
+
if not file_path.suffix == ".kicad_sch":
|
|
60
|
+
raise ValidationError(f"Not a KiCAD schematic file: {file_path}")
|
|
61
|
+
|
|
62
|
+
logger.info(f"Loading schematic: {file_path}")
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
schematic_data = self._parser.parse_file(file_path)
|
|
66
|
+
load_time = time.time() - start_time
|
|
67
|
+
logger.info(f"Loaded schematic in {load_time:.3f}s")
|
|
68
|
+
|
|
69
|
+
return schematic_data
|
|
70
|
+
|
|
71
|
+
except Exception as e:
|
|
72
|
+
logger.error(f"Failed to load schematic {file_path}: {e}")
|
|
73
|
+
raise ValidationError(f"Invalid schematic file: {e}") from e
|
|
74
|
+
|
|
75
|
+
def save_schematic(
|
|
76
|
+
self,
|
|
77
|
+
schematic_data: Dict[str, Any],
|
|
78
|
+
file_path: Union[str, Path],
|
|
79
|
+
preserve_format: bool = True,
|
|
80
|
+
) -> None:
|
|
81
|
+
"""
|
|
82
|
+
Save schematic data to file.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
schematic_data: Schematic data to save
|
|
86
|
+
file_path: Target file path
|
|
87
|
+
preserve_format: Whether to preserve exact formatting
|
|
88
|
+
|
|
89
|
+
Raises:
|
|
90
|
+
PermissionError: If file cannot be written
|
|
91
|
+
ValidationError: If data is invalid
|
|
92
|
+
"""
|
|
93
|
+
start_time = time.time()
|
|
94
|
+
file_path = Path(file_path)
|
|
95
|
+
|
|
96
|
+
logger.info(f"Saving schematic: {file_path}")
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
# Ensure parent directory exists
|
|
100
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
101
|
+
|
|
102
|
+
# Convert to S-expression format and save
|
|
103
|
+
sexp_data = self._parser._schematic_data_to_sexp(schematic_data)
|
|
104
|
+
formatted_content = self._formatter.format(sexp_data)
|
|
105
|
+
|
|
106
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
107
|
+
f.write(formatted_content)
|
|
108
|
+
|
|
109
|
+
save_time = time.time() - start_time
|
|
110
|
+
logger.info(f"Saved schematic in {save_time:.3f}s")
|
|
111
|
+
|
|
112
|
+
except PermissionError as e:
|
|
113
|
+
logger.error(f"Permission denied saving to {file_path}: {e}")
|
|
114
|
+
raise
|
|
115
|
+
except Exception as e:
|
|
116
|
+
logger.error(f"Failed to save schematic to {file_path}: {e}")
|
|
117
|
+
raise ValidationError(f"Save failed: {e}") from e
|
|
118
|
+
|
|
119
|
+
def create_backup(self, file_path: Union[str, Path], suffix: str = ".backup") -> Path:
|
|
120
|
+
"""
|
|
121
|
+
Create a backup copy of the schematic file.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
file_path: Source file to backup
|
|
125
|
+
suffix: Backup file suffix
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Path to backup file
|
|
129
|
+
|
|
130
|
+
Raises:
|
|
131
|
+
FileNotFoundError: If source file doesn't exist
|
|
132
|
+
PermissionError: If backup cannot be created
|
|
133
|
+
"""
|
|
134
|
+
file_path = Path(file_path)
|
|
135
|
+
|
|
136
|
+
if not file_path.exists():
|
|
137
|
+
raise FileNotFoundError(f"Cannot backup non-existent file: {file_path}")
|
|
138
|
+
|
|
139
|
+
# Create backup with timestamp if suffix doesn't include one
|
|
140
|
+
if suffix == ".backup":
|
|
141
|
+
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
|
142
|
+
backup_path = file_path.with_suffix(f".{timestamp}.backup")
|
|
143
|
+
else:
|
|
144
|
+
backup_path = file_path.with_suffix(f"{file_path.suffix}{suffix}")
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
# Copy file content
|
|
148
|
+
backup_path.write_bytes(file_path.read_bytes())
|
|
149
|
+
logger.info(f"Created backup: {backup_path}")
|
|
150
|
+
return backup_path
|
|
151
|
+
|
|
152
|
+
except Exception as e:
|
|
153
|
+
logger.error(f"Failed to create backup {backup_path}: {e}")
|
|
154
|
+
raise PermissionError(f"Backup failed: {e}") from e
|
|
155
|
+
|
|
156
|
+
def validate_file_path(self, file_path: Union[str, Path]) -> Path:
|
|
157
|
+
"""
|
|
158
|
+
Validate and normalize a file path for schematic operations.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
file_path: Path to validate
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Normalized Path object
|
|
165
|
+
|
|
166
|
+
Raises:
|
|
167
|
+
ValidationError: If path is invalid
|
|
168
|
+
"""
|
|
169
|
+
file_path = Path(file_path)
|
|
170
|
+
|
|
171
|
+
# Ensure .kicad_sch extension
|
|
172
|
+
if not file_path.suffix:
|
|
173
|
+
file_path = file_path.with_suffix(".kicad_sch")
|
|
174
|
+
elif file_path.suffix != ".kicad_sch":
|
|
175
|
+
raise ValidationError(f"Invalid schematic file extension: {file_path.suffix}")
|
|
176
|
+
|
|
177
|
+
# Validate path characters
|
|
178
|
+
try:
|
|
179
|
+
file_path.resolve()
|
|
180
|
+
except (OSError, ValueError) as e:
|
|
181
|
+
raise ValidationError(f"Invalid file path: {e}") from e
|
|
182
|
+
|
|
183
|
+
return file_path
|
|
184
|
+
|
|
185
|
+
def get_file_info(self, file_path: Union[str, Path]) -> Dict[str, Any]:
|
|
186
|
+
"""
|
|
187
|
+
Get file system information about a schematic file.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
file_path: Path to analyze
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Dictionary with file information
|
|
194
|
+
|
|
195
|
+
Raises:
|
|
196
|
+
FileNotFoundError: If file doesn't exist
|
|
197
|
+
"""
|
|
198
|
+
file_path = Path(file_path)
|
|
199
|
+
|
|
200
|
+
if not file_path.exists():
|
|
201
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|
|
202
|
+
|
|
203
|
+
stat = file_path.stat()
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
"path": str(file_path.resolve()),
|
|
207
|
+
"size": stat.st_size,
|
|
208
|
+
"modified": stat.st_mtime,
|
|
209
|
+
"created": getattr(stat, "st_birthtime", stat.st_ctime),
|
|
210
|
+
"readable": file_path.is_file() and file_path.exists(),
|
|
211
|
+
"writable": file_path.parent.exists() and file_path.parent.is_dir(),
|
|
212
|
+
"extension": file_path.suffix,
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
def create_empty_schematic_data(self) -> Dict[str, Any]:
|
|
216
|
+
"""
|
|
217
|
+
Create empty schematic data structure.
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
Empty schematic data dictionary
|
|
221
|
+
"""
|
|
222
|
+
return {
|
|
223
|
+
"kicad_sch": {
|
|
224
|
+
"version": 20230819,
|
|
225
|
+
"generator": config.file_format.generator_default,
|
|
226
|
+
"uuid": None, # Will be set by calling code
|
|
227
|
+
"paper": config.paper.default,
|
|
228
|
+
"lib_symbols": {},
|
|
229
|
+
"symbol": [],
|
|
230
|
+
"wire": [],
|
|
231
|
+
"junction": [],
|
|
232
|
+
"label": [],
|
|
233
|
+
"hierarchical_label": [],
|
|
234
|
+
"global_label": [],
|
|
235
|
+
"text": [],
|
|
236
|
+
"text_box": [],
|
|
237
|
+
"polyline": [],
|
|
238
|
+
"rectangle": [],
|
|
239
|
+
"circle": [],
|
|
240
|
+
"arc": [],
|
|
241
|
+
"image": [],
|
|
242
|
+
"sheet": [],
|
|
243
|
+
"sheet_instances": [],
|
|
244
|
+
"symbol_instances": [],
|
|
245
|
+
}
|
|
246
|
+
}
|