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,70 @@
1
+ """
2
+ Repository interfaces for schematic data persistence.
3
+
4
+ These interfaces define the contract for loading and saving schematic data,
5
+ abstracting away the specific file format and storage mechanisms.
6
+ """
7
+
8
+ from abc import ABC, abstractmethod
9
+ from pathlib import Path
10
+ from typing import Any, Dict, Protocol, Union
11
+
12
+
13
+ class ISchematicRepository(Protocol):
14
+ """Interface for schematic data persistence operations."""
15
+
16
+ def load(self, filepath: Union[str, Path]) -> Dict[str, Any]:
17
+ """
18
+ Load schematic data from a file.
19
+
20
+ Args:
21
+ filepath: Path to the schematic file
22
+
23
+ Returns:
24
+ Complete schematic data structure
25
+
26
+ Raises:
27
+ FileNotFoundError: If file doesn't exist
28
+ RepositoryError: If file cannot be loaded
29
+ """
30
+ ...
31
+
32
+ def save(self, data: Dict[str, Any], filepath: Union[str, Path]) -> None:
33
+ """
34
+ Save schematic data to a file.
35
+
36
+ Args:
37
+ data: Complete schematic data structure
38
+ filepath: Path where to save the file
39
+
40
+ Raises:
41
+ RepositoryError: If file cannot be saved
42
+ """
43
+ ...
44
+
45
+ def exists(self, filepath: Union[str, Path]) -> bool:
46
+ """
47
+ Check if a schematic file exists.
48
+
49
+ Args:
50
+ filepath: Path to check
51
+
52
+ Returns:
53
+ True if file exists and is accessible
54
+ """
55
+ ...
56
+
57
+ def validate_format(self, filepath: Union[str, Path]) -> bool:
58
+ """
59
+ Validate that a file is in the correct format.
60
+
61
+ Args:
62
+ filepath: Path to the file to validate
63
+
64
+ Returns:
65
+ True if file format is valid
66
+
67
+ Raises:
68
+ FileNotFoundError: If file doesn't exist
69
+ """
70
+ ...
@@ -0,0 +1,117 @@
1
+ """
2
+ Symbol resolver interfaces for symbol library operations.
3
+
4
+ These interfaces define the contract for resolving symbols from libraries,
5
+ handling inheritance chains, and managing symbol caching.
6
+ """
7
+
8
+ from abc import ABC, abstractmethod
9
+ from typing import Any, Dict, List, Optional, Protocol
10
+
11
+
12
+ class ISymbolResolver(Protocol):
13
+ """Interface for symbol resolution and inheritance operations."""
14
+
15
+ def resolve_symbol(self, lib_id: str) -> Optional[Dict[str, Any]]:
16
+ """
17
+ Resolve a symbol with full inheritance chain applied.
18
+
19
+ Args:
20
+ lib_id: Library identifier (e.g., "Device:R")
21
+
22
+ Returns:
23
+ Fully resolved symbol data with inheritance applied,
24
+ or None if symbol not found
25
+
26
+ Raises:
27
+ SymbolError: If symbol resolution fails
28
+ """
29
+ ...
30
+
31
+ def get_symbol_raw(self, lib_id: str) -> Optional[Dict[str, Any]]:
32
+ """
33
+ Get raw symbol data without inheritance resolution.
34
+
35
+ Args:
36
+ lib_id: Library identifier
37
+
38
+ Returns:
39
+ Raw symbol data as stored in library,
40
+ or None if symbol not found
41
+ """
42
+ ...
43
+
44
+ def resolve_inheritance_chain(self, lib_id: str) -> List[str]:
45
+ """
46
+ Get the complete inheritance chain for a symbol.
47
+
48
+ Args:
49
+ lib_id: Library identifier
50
+
51
+ Returns:
52
+ List of lib_ids in inheritance order (base to derived)
53
+
54
+ Raises:
55
+ SymbolError: If circular inheritance detected
56
+ """
57
+ ...
58
+
59
+ def is_symbol_available(self, lib_id: str) -> bool:
60
+ """
61
+ Check if a symbol is available in the libraries.
62
+
63
+ Args:
64
+ lib_id: Library identifier
65
+
66
+ Returns:
67
+ True if symbol exists and can be resolved
68
+ """
69
+ ...
70
+
71
+ def clear_cache(self) -> None:
72
+ """Clear any cached symbol data."""
73
+ ...
74
+
75
+
76
+ class ISymbolCache(Protocol):
77
+ """Interface for symbol library caching operations."""
78
+
79
+ def get_symbol(self, lib_id: str) -> Optional[Dict[str, Any]]:
80
+ """
81
+ Get a symbol from cache or load from library.
82
+
83
+ Args:
84
+ lib_id: Library identifier
85
+
86
+ Returns:
87
+ Symbol data or None if not found
88
+ """
89
+ ...
90
+
91
+ def cache_symbol(self, lib_id: str, symbol_data: Dict[str, Any]) -> None:
92
+ """
93
+ Cache symbol data.
94
+
95
+ Args:
96
+ lib_id: Library identifier
97
+ symbol_data: Symbol data to cache
98
+ """
99
+ ...
100
+
101
+ def invalidate(self, lib_id: Optional[str] = None) -> None:
102
+ """
103
+ Invalidate cache entries.
104
+
105
+ Args:
106
+ lib_id: Specific symbol to invalidate, or None for all
107
+ """
108
+ ...
109
+
110
+ def get_cache_stats(self) -> Dict[str, Any]:
111
+ """
112
+ Get cache statistics.
113
+
114
+ Returns:
115
+ Dictionary with cache statistics (hits, misses, size, etc.)
116
+ """
117
+ ...
@@ -0,0 +1,14 @@
1
+ """
2
+ Modular S-expression parsers for KiCAD elements.
3
+
4
+ This package provides specialized parsers for different types of KiCAD
5
+ S-expression elements, organized by responsibility and testable in isolation.
6
+ """
7
+
8
+ from .base import BaseElementParser
9
+ from .registry import ElementParserRegistry
10
+
11
+ __all__ = [
12
+ "ElementParserRegistry",
13
+ "BaseElementParser",
14
+ ]
@@ -0,0 +1,145 @@
1
+ """
2
+ Base parser implementation for S-expression elements.
3
+
4
+ Provides common functionality and utilities for all element parsers.
5
+ """
6
+
7
+ import logging
8
+ from typing import Any, Dict, List, Optional
9
+
10
+ from ..interfaces.parser import IElementParser
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class BaseElementParser(IElementParser):
16
+ """Base implementation for S-expression element parsers."""
17
+
18
+ def __init__(self, element_type: str):
19
+ """
20
+ Initialize base parser.
21
+
22
+ Args:
23
+ element_type: The S-expression element type this parser handles
24
+ """
25
+ self.element_type = element_type
26
+ self._logger = logger.getChild(self.__class__.__name__)
27
+
28
+ def can_parse(self, element: List[Any]) -> bool:
29
+ """Check if this parser can handle the given element type."""
30
+ if not element or not isinstance(element, list):
31
+ return False
32
+
33
+ element_type = element[0] if element else None
34
+ # Convert sexpdata.Symbol to string for comparison
35
+ element_type_str = str(element_type) if element_type else None
36
+ return element_type_str == self.element_type
37
+
38
+ def parse(self, element: List[Any]) -> Optional[Dict[str, Any]]:
39
+ """
40
+ Parse an S-expression element with error handling.
41
+
42
+ This method provides common error handling and validation,
43
+ then delegates to the specific parse_element implementation.
44
+ """
45
+ if not self.can_parse(element):
46
+ return None
47
+
48
+ try:
49
+ result = self.parse_element(element)
50
+ if result is not None:
51
+ self._logger.debug(f"Successfully parsed {self.element_type} element")
52
+ return result
53
+ except Exception as e:
54
+ self._logger.error(f"Failed to parse {self.element_type} element: {e}")
55
+ return None
56
+
57
+ def parse_element(self, element: List[Any]) -> Optional[Dict[str, Any]]:
58
+ """
59
+ Parse the specific element type.
60
+
61
+ This method should be implemented by subclasses to handle
62
+ the specific parsing logic for their element type.
63
+
64
+ Args:
65
+ element: S-expression element to parse
66
+
67
+ Returns:
68
+ Parsed element data or None if parsing failed
69
+
70
+ Raises:
71
+ NotImplementedError: If not implemented by subclass
72
+ """
73
+ raise NotImplementedError(f"parse_element not implemented for {self.element_type}")
74
+
75
+ def _extract_position(self, element: List[Any]) -> Optional[Dict[str, float]]:
76
+ """
77
+ Extract position information from common S-expression formats.
78
+
79
+ Many KiCAD elements have position info in formats like:
80
+ (at x y [angle])
81
+
82
+ Args:
83
+ element: S-expression sub-element containing position
84
+
85
+ Returns:
86
+ Dictionary with x, y, and optionally angle, or None if not found
87
+ """
88
+ if not isinstance(element, list) or len(element) < 3:
89
+ return None
90
+
91
+ if element[0] != "at":
92
+ return None
93
+
94
+ try:
95
+ result = {"x": float(element[1]), "y": float(element[2])}
96
+
97
+ # Optional angle parameter
98
+ if len(element) > 3:
99
+ result["angle"] = float(element[3])
100
+
101
+ return result
102
+ except (ValueError, IndexError):
103
+ return None
104
+
105
+ def _extract_property_list(
106
+ self, elements: List[Any], property_name: str
107
+ ) -> List[Dict[str, Any]]:
108
+ """
109
+ Extract all instances of a property from a list of elements.
110
+
111
+ Args:
112
+ elements: List of S-expression elements
113
+ property_name: Name of property to extract
114
+
115
+ Returns:
116
+ List of parsed property dictionaries
117
+ """
118
+ properties = []
119
+ for element in elements:
120
+ if isinstance(element, list) and len(element) > 0 and element[0] == property_name:
121
+ prop = self._parse_property_element(element)
122
+ if prop:
123
+ properties.append(prop)
124
+ return properties
125
+
126
+ def _parse_property_element(self, element: List[Any]) -> Optional[Dict[str, Any]]:
127
+ """
128
+ Parse a generic property element.
129
+
130
+ Override this method in subclasses for property-specific parsing.
131
+
132
+ Args:
133
+ element: S-expression property element
134
+
135
+ Returns:
136
+ Parsed property data or None
137
+ """
138
+ if len(element) < 2:
139
+ return None
140
+
141
+ return {
142
+ "type": element[0],
143
+ "value": element[1] if len(element) > 1 else None,
144
+ "raw": element,
145
+ }
@@ -0,0 +1,22 @@
1
+ """
2
+ Element-specific parsers for KiCAD schematic elements.
3
+
4
+ This module contains specialized parsers for different schematic element types,
5
+ extracted from the monolithic parser.py for better maintainability.
6
+ """
7
+
8
+ from .graphics_parser import GraphicsParser
9
+
10
+ # Additional element parsers will be imported here as they are created
11
+ # from .wire_parser import WireParser
12
+ # from .label_parser import LabelParser
13
+ # from .text_parser import TextParser
14
+ # from .sheet_parser import SheetParser
15
+ # from .library_parser import LibraryParser
16
+ # from .symbol_parser import SymbolParser
17
+ # from .metadata_parser import MetadataParser
18
+
19
+ __all__ = [
20
+ "GraphicsParser",
21
+ # Will be populated as parsers are added
22
+ ]