From c44bd9205d5df2907d3aa416a0af95acb8287b36 Mon Sep 17 00:00:00 2001 From: Eugene Mikhantyev Date: Sun, 29 Mar 2026 13:02:24 +0100 Subject: [PATCH] style: sort Python imports with isort Add isort configuration (profile=black, line_length=100) to pyproject.toml, add isort pre-commit hook, and auto-sort imports across all Python source files. Co-Authored-By: Claude Opus 4.6 --- .pre-commit-config.yaml | 6 ++ pyproject.toml | 4 + python/commands/__init__.py | 4 +- python/commands/board/__init__.py | 10 ++- python/commands/board/layers.py | 5 +- python/commands/board/outline.py | 5 +- python/commands/board/size.py | 5 +- python/commands/board/view.py | 13 ++-- python/commands/component.py | 9 ++- python/commands/component_schematic.py | 9 ++- python/commands/connection_schematic.py | 7 +- python/commands/datasheet_manager.py | 2 +- python/commands/design_rules.py | 11 +-- python/commands/dynamic_symbol_loader.py | 2 +- python/commands/export.py | 13 ++-- python/commands/footprint.py | 2 +- python/commands/freerouting.py | 10 +-- python/commands/jlcpcb.py | 17 +++-- python/commands/jlcpcb_parts.py | 8 +- python/commands/jlcsearch.py | 5 +- python/commands/library.py | 7 +- python/commands/library_schematic.py | 5 +- python/commands/library_symbol.py | 4 +- python/commands/pin_locator.py | 7 +- python/commands/project.py | 7 +- python/commands/routing.py | 7 +- python/commands/schematic.py | 5 +- python/commands/schematic_analysis.py | 5 +- python/commands/svg_import.py | 10 +-- python/commands/symbol_creator.py | 2 +- python/commands/wire_connectivity.py | 1 + python/commands/wire_manager.py | 10 +-- python/kicad_api/__init__.py | 2 +- python/kicad_api/base.py | 4 +- python/kicad_api/factory.py | 6 +- python/kicad_api/ipc_backend.py | 22 +++--- python/kicad_api/swig_backend.py | 4 +- python/kicad_interface.py | 75 +++++++++++-------- python/parsers/kicad_mod_parser.py | 2 +- python/resources/resource_definitions.py | 4 +- python/schemas/tool_schemas.py | 2 +- python/test_ipc_backend.py | 2 +- python/tests/test_freerouting.py | 5 +- python/tests/test_schematic_analysis.py | 26 +++---- .../tests/test_schematic_component_fields.py | 2 +- python/tests/test_schematic_tools.py | 5 +- python/tests/test_wire_junction_changes.py | 4 +- python/utils/kicad_process.py | 12 +-- python/utils/platform_helper.py | 2 +- tests/test_platform_helper.py | 9 ++- 50 files changed, 226 insertions(+), 179 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d66144d..e5ce50d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,3 +16,9 @@ repos: - id: black language_version: python3 args: [--config=pyproject.toml] + + - repo: https://github.com/pycqa/isort + rev: 8.0.1 + hooks: + - id: isort + args: [--settings-path=pyproject.toml] diff --git a/pyproject.toml b/pyproject.toml index 1bbf96e..9ab3555 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,3 +6,7 @@ requires-python = ">=3.10" [tool.black] line-length = 100 target-version = ["py310"] + +[tool.isort] +profile = "black" +line_length = 100 diff --git a/python/commands/__init__.py b/python/commands/__init__.py index 6d69317..158d4cc 100644 --- a/python/commands/__init__.py +++ b/python/commands/__init__.py @@ -2,12 +2,12 @@ KiCAD command implementations package """ -from .project import ProjectCommands from .board import BoardCommands from .component import ComponentCommands -from .routing import RoutingCommands from .design_rules import DesignRuleCommands from .export import ExportCommands +from .project import ProjectCommands +from .routing import RoutingCommands __all__ = [ "ProjectCommands", diff --git a/python/commands/board/__init__.py b/python/commands/board/__init__.py index 9c23957..96e511c 100644 --- a/python/commands/board/__init__.py +++ b/python/commands/board/__init__.py @@ -2,14 +2,16 @@ Board-related command implementations for KiCAD interface """ -import pcbnew import logging -from typing import Dict, Any, Optional +from typing import Any, Dict, Optional + +import pcbnew + +from .layers import BoardLayerCommands +from .outline import BoardOutlineCommands # Import specialized modules from .size import BoardSizeCommands -from .layers import BoardLayerCommands -from .outline import BoardOutlineCommands from .view import BoardViewCommands logger = logging.getLogger("kicad_interface") diff --git a/python/commands/board/layers.py b/python/commands/board/layers.py index 0219a5d..aa56cdf 100644 --- a/python/commands/board/layers.py +++ b/python/commands/board/layers.py @@ -2,9 +2,10 @@ Board layer command implementations for KiCAD interface """ -import pcbnew import logging -from typing import Dict, Any, Optional +from typing import Any, Dict, Optional + +import pcbnew logger = logging.getLogger("kicad_interface") diff --git a/python/commands/board/outline.py b/python/commands/board/outline.py index 05fe16d..57825fc 100644 --- a/python/commands/board/outline.py +++ b/python/commands/board/outline.py @@ -2,10 +2,11 @@ Board outline command implementations for KiCAD interface """ -import pcbnew import logging import math -from typing import Dict, Any, Optional +from typing import Any, Dict, Optional + +import pcbnew logger = logging.getLogger("kicad_interface") diff --git a/python/commands/board/size.py b/python/commands/board/size.py index 3d9fd4d..243b2ca 100644 --- a/python/commands/board/size.py +++ b/python/commands/board/size.py @@ -2,9 +2,10 @@ Board size command implementations for KiCAD interface """ -import pcbnew import logging -from typing import Dict, Any, Optional +from typing import Any, Dict, Optional + +import pcbnew logger = logging.getLogger("kicad_interface") diff --git a/python/commands/board/view.py b/python/commands/board/view.py index 9e35610..baee5cd 100644 --- a/python/commands/board/view.py +++ b/python/commands/board/view.py @@ -2,13 +2,14 @@ Board view command implementations for KiCAD interface """ -import os -import pcbnew -import logging -from typing import Dict, Any, Optional, List, Tuple -from PIL import Image -import io import base64 +import io +import logging +import os +from typing import Any, Dict, List, Optional, Tuple + +import pcbnew +from PIL import Image logger = logging.getLogger("kicad_interface") diff --git a/python/commands/component.py b/python/commands/component.py index e7eb029..4de1369 100644 --- a/python/commands/component.py +++ b/python/commands/component.py @@ -2,12 +2,13 @@ Component-related command implementations for KiCAD interface """ -import os -import pcbnew +import base64 import logging import math -from typing import Dict, Any, Optional, List, Tuple -import base64 +import os +from typing import Any, Dict, List, Optional, Tuple + +import pcbnew from commands.library import LibraryManager logger = logging.getLogger("kicad_interface") diff --git a/python/commands/component_schematic.py b/python/commands/component_schematic.py index 23fb4d6..2592820 100644 --- a/python/commands/component_schematic.py +++ b/python/commands/component_schematic.py @@ -1,10 +1,11 @@ -from skip import Schematic +import logging import os import uuid -import logging from pathlib import Path from typing import Optional +from skip import Schematic + logger = logging.getLogger(__name__) # Import dynamic symbol loader @@ -350,9 +351,9 @@ class ComponentManager: if __name__ == "__main__": # Example Usage (for testing) - from schematic import ( + from schematic import ( # Assuming schematic.py is in the same directory SchematicManager, - ) # Assuming schematic.py is in the same directory + ) # Create a new schematic test_sch = SchematicManager.create_schematic("ComponentTestSchematic") diff --git a/python/commands/connection_schematic.py b/python/commands/connection_schematic.py index 24f08fc..19dfb95 100644 --- a/python/commands/connection_schematic.py +++ b/python/commands/connection_schematic.py @@ -1,15 +1,16 @@ -from skip import Schematic -import os import logging +import os from pathlib import Path from typing import Optional +from skip import Schematic + logger = logging.getLogger(__name__) # Import new wire and pin managers try: - from commands.wire_manager import WireManager from commands.pin_locator import PinLocator + from commands.wire_manager import WireManager WIRE_MANAGER_AVAILABLE = True except ImportError: diff --git a/python/commands/datasheet_manager.py b/python/commands/datasheet_manager.py index 9e72429..fe11f14 100644 --- a/python/commands/datasheet_manager.py +++ b/python/commands/datasheet_manager.py @@ -9,8 +9,8 @@ URL schema: https://www.lcsc.com/datasheet/{LCSC#}.pdf No API key required. """ -import re import logging +import re from pathlib import Path from typing import Dict, List, Optional diff --git a/python/commands/design_rules.py b/python/commands/design_rules.py index 766a573..b8b69e5 100644 --- a/python/commands/design_rules.py +++ b/python/commands/design_rules.py @@ -2,10 +2,11 @@ Design rules command implementations for KiCAD interface """ -import os -import pcbnew import logging -from typing import Dict, Any, Optional, List, Tuple +import os +from typing import Any, Dict, List, Optional, Tuple + +import pcbnew logger = logging.getLogger("kicad_interface") @@ -171,11 +172,11 @@ class DesignRuleCommands: def run_drc(self, params: Dict[str, Any]) -> Dict[str, Any]: """Run Design Rule Check using kicad-cli""" - import subprocess import json - import tempfile import platform import shutil + import subprocess + import tempfile try: if not self.board: diff --git a/python/commands/dynamic_symbol_loader.py b/python/commands/dynamic_symbol_loader.py index c102394..4c9b778 100644 --- a/python/commands/dynamic_symbol_loader.py +++ b/python/commands/dynamic_symbol_loader.py @@ -7,10 +7,10 @@ on-the-fly using TEXT MANIPULATION (not sexpdata) to preserve file formatting. This enables access to all ~10,000+ KiCad symbols dynamically. """ +import logging import os import re import uuid -import logging from pathlib import Path from typing import Dict, List, Optional, Tuple diff --git a/python/commands/export.py b/python/commands/export.py index f7a2fd9..4311a48 100644 --- a/python/commands/export.py +++ b/python/commands/export.py @@ -2,13 +2,14 @@ Export command implementations for KiCAD interface """ -import os -import pcbnew -import logging -from typing import Dict, Any, Optional, List, Tuple import base64 +import logging +import os import shutil from datetime import datetime +from typing import Any, Dict, List, Optional, Tuple + +import pcbnew logger = logging.getLogger("kicad_interface") @@ -321,9 +322,9 @@ class ExportCommands: def export_3d(self, params: Dict[str, Any]) -> Dict[str, Any]: """Export 3D model files using kicad-cli (KiCAD 9.0 compatible)""" - import subprocess import platform import shutil + import subprocess try: if not self.board: @@ -608,8 +609,8 @@ class ExportCommands: Returns: Path to kicad-cli executable, or None if not found """ - import shutil import platform + import shutil # Try system PATH first cli_path = shutil.which("kicad-cli") diff --git a/python/commands/footprint.py b/python/commands/footprint.py index 5398672..55e54f7 100644 --- a/python/commands/footprint.py +++ b/python/commands/footprint.py @@ -8,9 +8,9 @@ KiCAD 9 .kicad_mod format reference: https://dev-docs.kicad.org/en/file-formats/sexpr-footprint/ """ +import logging import os import re -import logging from pathlib import Path from typing import Any, Dict, List, Optional diff --git a/python/commands/freerouting.py b/python/commands/freerouting.py index 39318d6..3da22b1 100644 --- a/python/commands/freerouting.py +++ b/python/commands/freerouting.py @@ -9,13 +9,13 @@ Supports two execution modes: - Docker: docker run eclipse-temurin:21-jre (requires Docker) """ -import os -import subprocess -import shutil -import time import logging +import os +import shutil +import subprocess +import time from pathlib import Path -from typing import Dict, Any, Optional, List +from typing import Any, Dict, List, Optional logger = logging.getLogger("kicad_interface") diff --git a/python/commands/jlcpcb.py b/python/commands/jlcpcb.py index 8856848..2cf9cad 100644 --- a/python/commands/jlcpcb.py +++ b/python/commands/jlcpcb.py @@ -5,18 +5,19 @@ Handles authentication and downloading the JLCPCB parts library for integration with KiCAD component selection. """ -import os -import logging -import requests -import time -import hmac +import base64 import hashlib +import hmac +import json +import logging +import os import secrets import string -import base64 -import json -from typing import Optional, Dict, List, Callable +import time from pathlib import Path +from typing import Callable, Dict, List, Optional + +import requests logger = logging.getLogger("kicad_interface") diff --git a/python/commands/jlcpcb_parts.py b/python/commands/jlcpcb_parts.py index ff36424..3edadd5 100644 --- a/python/commands/jlcpcb_parts.py +++ b/python/commands/jlcpcb_parts.py @@ -5,13 +5,13 @@ Manages local SQLite database of JLCPCB parts for fast searching and component selection. """ -import os -import sqlite3 import json import logging -from pathlib import Path -from typing import List, Dict, Optional +import os +import sqlite3 from datetime import datetime +from pathlib import Path +from typing import Dict, List, Optional logger = logging.getLogger("kicad_interface") diff --git a/python/commands/jlcsearch.py b/python/commands/jlcsearch.py index b51088a..9ae38c8 100644 --- a/python/commands/jlcsearch.py +++ b/python/commands/jlcsearch.py @@ -6,9 +6,10 @@ jlcsearch service at https://jlcsearch.tscircuit.com/ """ import logging -import requests -from typing import Optional, Dict, List, Callable import time +from typing import Callable, Dict, List, Optional + +import requests logger = logging.getLogger("kicad_interface") diff --git a/python/commands/library.py b/python/commands/library.py index 678a7c2..5e137e8 100644 --- a/python/commands/library.py +++ b/python/commands/library.py @@ -5,12 +5,12 @@ Handles parsing fp-lib-table files, discovering footprints, and providing search functionality for component placement. """ +import glob +import logging import os import re -import logging from pathlib import Path from typing import Dict, List, Optional, Tuple -import glob logger = logging.getLogger("kicad_interface") @@ -522,9 +522,10 @@ class LibraryCommands: # Attempt to enrich with parsed .kicad_mod data try: - from parsers.kicad_mod_parser import parse_kicad_mod from pathlib import Path as _Path + from parsers.kicad_mod_parser import parse_kicad_mod + mod_file = str(_Path(library_path) / f"{footprint_name}.kicad_mod") parsed = parse_kicad_mod(mod_file) if parsed: diff --git a/python/commands/library_schematic.py b/python/commands/library_schematic.py index b9c8fd4..6ddf7b4 100644 --- a/python/commands/library_schematic.py +++ b/python/commands/library_schematic.py @@ -1,8 +1,9 @@ -from skip import Schematic +import glob # Symbol class might not be directly importable in the current version import os -import glob + +from skip import Schematic class LibraryManager: diff --git a/python/commands/library_symbol.py b/python/commands/library_symbol.py index 434a622..95cd716 100644 --- a/python/commands/library_symbol.py +++ b/python/commands/library_symbol.py @@ -5,12 +5,12 @@ Handles parsing sym-lib-table files, discovering symbols, and providing search functionality for component selection. """ +import logging import os import re -import logging +from dataclasses import asdict, dataclass from pathlib import Path from typing import Dict, List, Optional -from dataclasses import dataclass, asdict logger = logging.getLogger("kicad_interface") diff --git a/python/commands/pin_locator.py b/python/commands/pin_locator.py index 3657c46..7122216 100644 --- a/python/commands/pin_locator.py +++ b/python/commands/pin_locator.py @@ -9,7 +9,8 @@ import logging import math import tempfile from pathlib import Path -from typing import List, Tuple, Optional, Dict +from typing import Dict, List, Optional, Tuple + import sexpdata from sexpdata import Symbol from skip import Schematic @@ -396,12 +397,12 @@ class PinLocator: if __name__ == "__main__": # Test pin location discovery + import shutil import sys - from pathlib import Path + from commands.component_schematic import ComponentManager from commands.schematic import SchematicManager - import shutil sys.path.insert(0, str(Path(__file__).parent.parent)) diff --git a/python/commands/project.py b/python/commands/project.py index 2a2e403..2b82697 100644 --- a/python/commands/project.py +++ b/python/commands/project.py @@ -2,11 +2,12 @@ Project-related command implementations for KiCAD interface """ -import os -import pcbnew # type: ignore import logging +import os import shutil -from typing import Dict, Any, Optional +from typing import Any, Dict, Optional + +import pcbnew # type: ignore logger = logging.getLogger("kicad_interface") diff --git a/python/commands/routing.py b/python/commands/routing.py index da66436..317de8b 100644 --- a/python/commands/routing.py +++ b/python/commands/routing.py @@ -2,11 +2,12 @@ Routing-related command implementations for KiCAD interface """ -import os -import pcbnew import logging import math -from typing import Dict, Any, Optional, List, Tuple +import os +from typing import Any, Dict, List, Optional, Tuple + +import pcbnew logger = logging.getLogger("kicad_interface") diff --git a/python/commands/schematic.py b/python/commands/schematic.py index 9365f25..19a801a 100644 --- a/python/commands/schematic.py +++ b/python/commands/schematic.py @@ -1,9 +1,10 @@ -from skip import Schematic +import logging import os import shutil -import logging import uuid +from skip import Schematic + logger = logging.getLogger("kicad_interface") diff --git a/python/commands/schematic_analysis.py b/python/commands/schematic_analysis.py index 4044972..d1a212a 100644 --- a/python/commands/schematic_analysis.py +++ b/python/commands/schematic_analysis.py @@ -8,12 +8,11 @@ and checking connectivity in KiCad schematic files. import logging import math from pathlib import Path -from typing import Dict, List, Tuple, Optional, Any, Set +from typing import Any, Dict, List, Optional, Set, Tuple import sexpdata -from sexpdata import Symbol - from commands.pin_locator import PinLocator +from sexpdata import Symbol logger = logging.getLogger("kicad_interface") diff --git a/python/commands/svg_import.py b/python/commands/svg_import.py index e337871..e6f2f33 100644 --- a/python/commands/svg_import.py +++ b/python/commands/svg_import.py @@ -15,13 +15,13 @@ Supported SVG elements: SVG coordinate system: Y increases downward (same as KiCAD mm), so no Y-flip needed. """ -import re -import math -import uuid -import os import logging -from typing import List, Tuple, Dict, Any, Optional +import math +import os +import re +import uuid import xml.etree.ElementTree as ET +from typing import Any, Dict, List, Optional, Tuple logger = logging.getLogger("kicad_interface") diff --git a/python/commands/symbol_creator.py b/python/commands/symbol_creator.py index e06a07f..6b99ffb 100644 --- a/python/commands/symbol_creator.py +++ b/python/commands/symbol_creator.py @@ -12,9 +12,9 @@ KiCAD 9 .kicad_sym format: - All coordinates in mm, 2.54mm grid typical for schematic symbols """ +import logging import os import re -import logging from pathlib import Path from typing import Any, Dict, List, Optional diff --git a/python/commands/wire_connectivity.py b/python/commands/wire_connectivity.py index eeb897b..acd1f3c 100644 --- a/python/commands/wire_connectivity.py +++ b/python/commands/wire_connectivity.py @@ -9,6 +9,7 @@ coordinate matching, mirroring KiCad's own connectivity algorithm. import logging from pathlib import Path from typing import Dict, List, Optional, Set, Tuple + from commands.pin_locator import PinLocator logger = logging.getLogger("kicad_interface") diff --git a/python/commands/wire_manager.py b/python/commands/wire_manager.py index d1046f5..3851f36 100644 --- a/python/commands/wire_manager.py +++ b/python/commands/wire_manager.py @@ -6,12 +6,13 @@ kicad-skip's wire API doesn't support creating wires with standard parameters, s manipulate the .kicad_sch file directly. """ -import uuid import logging import math import tempfile +import uuid from pathlib import Path -from typing import List, Tuple, Optional +from typing import List, Optional, Tuple + import sexpdata from sexpdata import Symbol @@ -671,10 +672,9 @@ class WireManager: if __name__ == "__main__": # Test wire creation - import sys - - from pathlib import Path import shutil + import sys + from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent)) diff --git a/python/kicad_api/__init__.py b/python/kicad_api/__init__.py index 223b009..ee997dc 100644 --- a/python/kicad_api/__init__.py +++ b/python/kicad_api/__init__.py @@ -20,8 +20,8 @@ Usage: board.set_size(100, 80) """ -from kicad_api.factory import create_backend from kicad_api.base import KiCADBackend +from kicad_api.factory import create_backend __all__ = ["create_backend", "KiCADBackend"] __version__ = "2.0.0-alpha.1" diff --git a/python/kicad_api/base.py b/python/kicad_api/base.py index 4a9157a..b64c922 100644 --- a/python/kicad_api/base.py +++ b/python/kicad_api/base.py @@ -4,10 +4,10 @@ Abstract base class for KiCAD API backends Defines the interface that all KiCAD backends must implement. """ +import logging from abc import ABC, abstractmethod from pathlib import Path -from typing import Optional, Dict, Any, List -import logging +from typing import Any, Dict, List, Optional logger = logging.getLogger(__name__) diff --git a/python/kicad_api/factory.py b/python/kicad_api/factory.py index 3ead056..34d4764 100644 --- a/python/kicad_api/factory.py +++ b/python/kicad_api/factory.py @@ -4,12 +4,12 @@ Backend factory for creating appropriate KiCAD API backend Auto-detects available backends and provides fallback mechanism. """ -import os import logging -from typing import Optional +import os from pathlib import Path +from typing import Optional -from kicad_api.base import KiCADBackend, APINotAvailableError +from kicad_api.base import APINotAvailableError, KiCADBackend logger = logging.getLogger(__name__) diff --git a/python/kicad_api/ipc_backend.py b/python/kicad_api/ipc_backend.py index cd4e0e8..65a3879 100644 --- a/python/kicad_api/ipc_backend.py +++ b/python/kicad_api/ipc_backend.py @@ -18,9 +18,9 @@ import logging import os import platform from pathlib import Path -from typing import Optional, Dict, Any, List, Callable +from typing import Any, Callable, Dict, List, Optional -from kicad_api.base import KiCADBackend, BoardAPI, ConnectionError, APINotAvailableError +from kicad_api.base import APINotAvailableError, BoardAPI, ConnectionError, KiCADBackend logger = logging.getLogger(__name__) @@ -317,8 +317,8 @@ class IPCBoardAPI(BoardAPI): try: from kipy.board_types import BoardRectangle from kipy.geometry import Vector2 - from kipy.util.units import from_mm from kipy.proto.board.board_types_pb2 import BoardLayer + from kipy.util.units import from_mm board = self._get_board() @@ -655,9 +655,9 @@ class IPCBoardAPI(BoardAPI): """ try: from kipy.board_types import Footprint - from kipy.geometry import Vector2, Angle - from kipy.util.units import from_mm + from kipy.geometry import Angle, Vector2 from kipy.proto.board.board_types_pb2 import BoardLayer + from kipy.util.units import from_mm board = self._get_board() @@ -708,7 +708,7 @@ class IPCBoardAPI(BoardAPI): ) -> bool: """Move a component to a new position (updates UI immediately).""" try: - from kipy.geometry import Vector2, Angle + from kipy.geometry import Angle, Vector2 from kipy.util.units import from_mm board = self._get_board() @@ -795,8 +795,8 @@ class IPCBoardAPI(BoardAPI): try: from kipy.board_types import Track from kipy.geometry import Vector2 - from kipy.util.units import from_mm from kipy.proto.board.board_types_pb2 import BoardLayer + from kipy.util.units import from_mm board = self._get_board() @@ -863,8 +863,8 @@ class IPCBoardAPI(BoardAPI): try: from kipy.board_types import Via from kipy.geometry import Vector2 - from kipy.util.units import from_mm from kipy.proto.board.board_types_pb2 import ViaType + from kipy.util.units import from_mm board = self._get_board() @@ -925,9 +925,9 @@ class IPCBoardAPI(BoardAPI): """Add text to the board.""" try: from kipy.board_types import BoardText - from kipy.geometry import Vector2, Angle - from kipy.util.units import from_mm + from kipy.geometry import Angle, Vector2 from kipy.proto.board.board_types_pb2 import BoardLayer + from kipy.util.units import from_mm board = self._get_board() @@ -1072,8 +1072,8 @@ class IPCBoardAPI(BoardAPI): try: from kipy.board_types import Zone, ZoneFillMode, ZoneType from kipy.geometry import PolyLine, PolyLineNode, Vector2 - from kipy.util.units import from_mm from kipy.proto.board.board_types_pb2 import BoardLayer + from kipy.util.units import from_mm board = self._get_board() diff --git a/python/kicad_api/swig_backend.py b/python/kicad_api/swig_backend.py index 312e0b6..7864c2b 100644 --- a/python/kicad_api/swig_backend.py +++ b/python/kicad_api/swig_backend.py @@ -11,9 +11,9 @@ WARNING: SWIG bindings are deprecated as of KiCAD 9.0 import logging from pathlib import Path -from typing import Optional, Dict, Any, List +from typing import Any, Dict, List, Optional -from kicad_api.base import KiCADBackend, BoardAPI, ConnectionError, APINotAvailableError +from kicad_api.base import APINotAvailableError, BoardAPI, ConnectionError, KiCADBackend logger = logging.getLogger(__name__) diff --git a/python/kicad_interface.py b/python/kicad_interface.py index 80fab5f..b3903ad 100644 --- a/python/kicad_interface.py +++ b/python/kicad_interface.py @@ -7,16 +7,17 @@ and KiCAD's Python API (pcbnew). It receives commands via stdin as JSON and returns responses via stdout also as JSON. """ -import sys import json -import traceback import logging import os -from typing import Dict, Any, Optional +import sys +import traceback +from typing import Any, Dict, Optional + +from resources.resource_definitions import RESOURCE_DEFINITIONS, handle_resource_read # Import tool schemas and resource definitions from schemas.tool_schemas import TOOL_SCHEMAS -from resources.resource_definitions import RESOURCE_DEFINITIONS, handle_resource_read # Configure logging log_dir = os.path.join(os.path.expanduser("~"), ".kicad-mcp", "logs") @@ -80,9 +81,10 @@ utils_dir = os.path.join(os.path.dirname(__file__)) if utils_dir not in sys.path: sys.path.insert(0, utils_dir) +from utils.kicad_process import KiCADProcessManager, check_and_launch_kicad + # Import platform helper and add KiCAD paths from utils.platform_helper import PlatformHelper -from utils.kicad_process import check_and_launch_kicad, KiCADProcessManager logger.info(f"Detecting KiCAD Python paths for {PlatformHelper.get_platform_name()}...") paths_added = PlatformHelper.add_kicad_to_python_path() @@ -202,27 +204,27 @@ elif KICAD_BACKEND == "ipc" and not USE_IPC_BACKEND: # Import command handlers try: logger.info("Importing command handlers...") - from commands.project import ProjectCommands from commands.board import BoardCommands from commands.component import ComponentCommands - from commands.routing import RoutingCommands - from commands.design_rules import DesignRuleCommands - from commands.export import ExportCommands - from commands.schematic import SchematicManager from commands.component_schematic import ComponentManager from commands.connection_schematic import ConnectionManager - from commands.library_schematic import LibraryManager as SchematicLibraryManager - from commands.library import ( - LibraryManager as FootprintLibraryManager, - LibraryCommands, - ) - from commands.library_symbol import SymbolLibraryManager, SymbolLibraryCommands + from commands.datasheet_manager import DatasheetManager + from commands.design_rules import DesignRuleCommands + from commands.export import ExportCommands + from commands.footprint import FootprintCreator + from commands.freerouting import FreeroutingCommands from commands.jlcpcb import JLCPCBClient, test_jlcpcb_connection from commands.jlcpcb_parts import JLCPCBPartsManager - from commands.datasheet_manager import DatasheetManager - from commands.footprint import FootprintCreator + from commands.library import ( + LibraryCommands, + ) + from commands.library import LibraryManager as FootprintLibraryManager + from commands.library_schematic import LibraryManager as SchematicLibraryManager + from commands.library_symbol import SymbolLibraryCommands, SymbolLibraryManager + from commands.project import ProjectCommands + from commands.routing import RoutingCommands + from commands.schematic import SchematicManager from commands.symbol_creator import SymbolCreator - from commands.freerouting import FreeroutingCommands logger.info("Successfully imported all command handlers") except ImportError as e: @@ -682,6 +684,7 @@ class KiCADInterface: logger.info("Adding component to schematic") try: from pathlib import Path + from commands.dynamic_symbol_loader import DynamicSymbolLoader schematic_path = params.get("schematicPath") @@ -733,8 +736,8 @@ class KiCADInterface: """Remove a placed symbol from a schematic using text-based manipulation (no skip writes)""" logger.info("Deleting schematic component") try: - from pathlib import Path import re + from pathlib import Path schematic_path = params.get("schematicPath") reference = params.get("reference") @@ -841,8 +844,8 @@ class KiCADInterface: """ logger.info("Editing schematic component") try: - from pathlib import Path import re + from pathlib import Path schematic_path = params.get("schematicPath") reference = params.get("reference") @@ -992,8 +995,8 @@ class KiCADInterface: """Return full component info: position and all field values with their (at x y angle) positions.""" logger.info("Getting schematic component info") try: - from pathlib import Path import re + from pathlib import Path schematic_path = params.get("schematicPath") reference = params.get("reference") @@ -1116,6 +1119,7 @@ class KiCADInterface: logger.info("Adding wire to schematic") try: from pathlib import Path + from commands.wire_manager import WireManager schematic_path = params.get("schematicPath") @@ -1239,6 +1243,7 @@ class KiCADInterface: logger.info("Adding junction to schematic") try: from pathlib import Path + from commands.wire_manager import WireManager schematic_path = params.get("schematicPath") @@ -1472,6 +1477,7 @@ class KiCADInterface: logger.info("Adding net label to schematic") try: from pathlib import Path + from commands.wire_manager import WireManager schematic_path = params.get("schematicPath") @@ -1591,6 +1597,7 @@ class KiCADInterface: logger.info("Getting schematic pin locations") try: from pathlib import Path + from commands.pin_locator import PinLocator schematic_path = params.get("schematicPath") @@ -1643,9 +1650,9 @@ class KiCADInterface: def _handle_get_schematic_view(self, params): """Get a rasterised image of the schematic (SVG export → optional PNG conversion)""" logger.info("Getting schematic view") + import base64 import subprocess import tempfile - import base64 try: schematic_path = params.get("schematicPath") @@ -1734,6 +1741,7 @@ class KiCADInterface: logger.info("Listing schematic components") try: from pathlib import Path + from commands.pin_locator import PinLocator schematic_path = params.get("schematicPath") @@ -2185,6 +2193,7 @@ class KiCADInterface: return {"success": False, "message": "schematicPath is required"} from pathlib import Path + from commands.wire_manager import WireManager start_point = [start.get("x", 0), start.get("y", 0)] @@ -2218,6 +2227,7 @@ class KiCADInterface: } from pathlib import Path + from commands.wire_manager import WireManager pos_list = None @@ -2240,9 +2250,9 @@ class KiCADInterface: def _handle_export_schematic_svg(self, params): """Export schematic to SVG using kicad-cli""" logger.info("Exporting schematic SVG") - import subprocess import glob import shutil + import subprocess try: schematic_path = params.get("schematicPath") @@ -2381,9 +2391,9 @@ class KiCADInterface: def _handle_run_erc(self, params): """Run Electrical Rules Check on a schematic via kicad-cli""" logger.info("Running ERC on schematic") + import os import subprocess import tempfile - import os try: schematic_path = params.get("schematicPath") @@ -2619,10 +2629,10 @@ class KiCADInterface: def _handle_get_schematic_view_region(self, params): """Export a cropped region of the schematic as an image""" logger.info("Exporting schematic view region") + import base64 + import os import subprocess import tempfile - import os - import base64 try: schematic_path = params.get("schematicPath") @@ -2736,6 +2746,7 @@ class KiCADInterface: logger.info("Finding overlapping elements in schematic") try: from pathlib import Path + from commands.schematic_analysis import find_overlapping_elements schematic_path = params.get("schematicPath") @@ -2761,6 +2772,7 @@ class KiCADInterface: logger.info("Getting elements in schematic region") try: from pathlib import Path + from commands.schematic_analysis import get_elements_in_region schematic_path = params.get("schematicPath") @@ -2790,6 +2802,7 @@ class KiCADInterface: logger.info("Finding wires crossing symbols in schematic") try: from pathlib import Path + from commands.schematic_analysis import find_wires_crossing_symbols schematic_path = params.get("schematicPath") @@ -3009,7 +3022,9 @@ class KiCADInterface: zone_count = self.board.GetAreaCount() if hasattr(self.board, "GetAreaCount") else 0 # Run pcbnew zone fill in an isolated subprocess to prevent crashes - import subprocess, sys, textwrap + import subprocess + import sys + import textwrap script = textwrap.dedent(f""" import pcbnew, sys @@ -3437,8 +3452,8 @@ print("ok") try: from kipy.board_types import BoardSegment from kipy.geometry import Vector2 - from kipy.util.units import from_mm from kipy.proto.board.board_types_pb2 import BoardLayer + from kipy.util.units import from_mm board = self.ipc_board_api._get_board() @@ -3488,8 +3503,8 @@ print("ok") try: from kipy.board_types import BoardCircle from kipy.geometry import Vector2 - from kipy.util.units import from_mm from kipy.proto.board.board_types_pb2 import BoardLayer + from kipy.util.units import from_mm board = self.ipc_board_api._get_board() diff --git a/python/parsers/kicad_mod_parser.py b/python/parsers/kicad_mod_parser.py index 82cbab7..a1f2ac0 100644 --- a/python/parsers/kicad_mod_parser.py +++ b/python/parsers/kicad_mod_parser.py @@ -15,8 +15,8 @@ KiCad S-expression file format reference: https://dev-docs.kicad.org/en/file-formats/sexpr-intro/index.html#_footprint """ -import re import logging +import re from pathlib import Path from typing import Any, Dict, List, Optional, Tuple diff --git a/python/resources/resource_definitions.py b/python/resources/resource_definitions.py index feed376..ba893db 100644 --- a/python/resources/resource_definitions.py +++ b/python/resources/resource_definitions.py @@ -5,10 +5,10 @@ Resources follow the MCP 2025-06-18 specification, providing read-only access to project data for LLM context. """ -import json import base64 -from typing import Dict, Any, List, Optional +import json import logging +from typing import Any, Dict, List, Optional logger = logging.getLogger("kicad_interface") diff --git a/python/schemas/tool_schemas.py b/python/schemas/tool_schemas.py index 5076a0e..94de080 100644 --- a/python/schemas/tool_schemas.py +++ b/python/schemas/tool_schemas.py @@ -10,7 +10,7 @@ Each tool includes: - outputSchema: Optional JSON Schema for return values (structured content) """ -from typing import Dict, Any +from typing import Any, Dict # ============================================================================= # PROJECT TOOLS diff --git a/python/test_ipc_backend.py b/python/test_ipc_backend.py index c8f6ee1..a6baf16 100644 --- a/python/test_ipc_backend.py +++ b/python/test_ipc_backend.py @@ -14,8 +14,8 @@ Usage: ./venv/bin/python python/test_ipc_backend.py """ -import sys import os +import sys # Add parent directory to path sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) diff --git a/python/tests/test_freerouting.py b/python/tests/test_freerouting.py index 287c560..c04f769 100644 --- a/python/tests/test_freerouting.py +++ b/python/tests/test_freerouting.py @@ -15,12 +15,11 @@ from pathlib import Path from unittest.mock import MagicMock, patch import pytest - from commands.freerouting import ( FreeroutingCommands, - _find_java, - _find_docker, _docker_available, + _find_docker, + _find_java, _java_version_ok, ) diff --git a/python/tests/test_schematic_analysis.py b/python/tests/test_schematic_analysis.py index a0cd46d..45158e3 100644 --- a/python/tests/test_schematic_analysis.py +++ b/python/tests/test_schematic_analysis.py @@ -6,11 +6,11 @@ Integration tests parse real .kicad_sch files via sexpdata. """ import os -import sys import shutil +import sys import tempfile from pathlib import Path -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch import pytest import sexpdata @@ -20,23 +20,23 @@ from sexpdata import Symbol sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from commands.schematic_analysis import ( - _parse_wires, - _parse_labels, - _parse_symbols, - _load_sexp, - _extract_lib_symbols, - _parse_lib_symbol_graphics, - _transform_local_point, - _line_segment_intersects_aabb, - _point_in_rect, - _distance, _aabb_overlap, _check_wire_overlap, _compute_symbol_bbox_direct, + _distance, + _extract_lib_symbols, + _line_segment_intersects_aabb, + _load_sexp, + _parse_labels, + _parse_lib_symbol_graphics, + _parse_symbols, + _parse_wires, + _point_in_rect, + _transform_local_point, compute_symbol_bbox, find_overlapping_elements, - get_elements_in_region, find_wires_crossing_symbols, + get_elements_in_region, ) # --------------------------------------------------------------------------- diff --git a/python/tests/test_schematic_component_fields.py b/python/tests/test_schematic_component_fields.py index 2173b10..2fb1c19 100644 --- a/python/tests/test_schematic_component_fields.py +++ b/python/tests/test_schematic_component_fields.py @@ -3,8 +3,8 @@ Tests for get_schematic_component and edit_schematic_component fieldPositions su """ import re -import sys import shutil +import sys import tempfile from pathlib import Path diff --git a/python/tests/test_schematic_tools.py b/python/tests/test_schematic_tools.py index 2c5f51c..d6814b5 100644 --- a/python/tests/test_schematic_tools.py +++ b/python/tests/test_schematic_tools.py @@ -268,7 +268,8 @@ def _make_handler_under_test(handler_name: str): This works because every _handle_* method starts with a params dict check before doing any file I/O or heavy imports. """ - import importlib.util, types + import importlib.util + import types # We monkey-patch sys.modules to avoid pcbnew/skip side effects stubs = {} @@ -301,8 +302,8 @@ class TestHandlerParamValidation: def _make_iface_stub(self): """Return a stub that exposes only the handler methods under test.""" - import types import importlib + import types # Build a minimal namespace that satisfies the imports inside each handler stub_mod = types.ModuleType("_handler_stubs") diff --git a/python/tests/test_wire_junction_changes.py b/python/tests/test_wire_junction_changes.py index bcf8b34..01bda02 100644 --- a/python/tests/test_wire_junction_changes.py +++ b/python/tests/test_wire_junction_changes.py @@ -119,7 +119,8 @@ class TestHandlerDispatch: @pytest.fixture(autouse=True) def load_handler_map(self): # Import only the dispatch table without initialising KiCAD connections - import importlib, types + import importlib + import types # Patch heavy imports before loading kicad_interface for mod in ["pcbnew", "skip"]: @@ -334,6 +335,7 @@ class TestPinSnapping: # Re-import so the patched skip.Schematic is used import importlib + import kicad_interface importlib.reload(kicad_interface) diff --git a/python/utils/kicad_process.py b/python/utils/kicad_process.py index 88ac20c..bb367bd 100644 --- a/python/utils/kicad_process.py +++ b/python/utils/kicad_process.py @@ -4,15 +4,15 @@ KiCAD Process Management Utilities Detects if KiCAD is running and provides auto-launch functionality. """ -import os -import subprocess -import logging -import platform -import time import ctypes +import logging +import os +import platform +import subprocess +import time from ctypes import wintypes from pathlib import Path -from typing import Optional, List +from typing import List, Optional logger = logging.getLogger(__name__) diff --git a/python/utils/platform_helper.py b/python/utils/platform_helper.py index 8393153..175db50 100644 --- a/python/utils/platform_helper.py +++ b/python/utils/platform_helper.py @@ -5,12 +5,12 @@ This module provides helpers for detecting the current platform and getting appropriate paths for KiCAD, configuration, logs, etc. """ +import logging import os import platform import sys from pathlib import Path from typing import List, Optional -import logging logger = logging.getLogger(__name__) diff --git a/tests/test_platform_helper.py b/tests/test_platform_helper.py index 0742e98..6a8033e 100644 --- a/tests/test_platform_helper.py +++ b/tests/test_platform_helper.py @@ -4,11 +4,12 @@ Tests for platform_helper utility These are unit tests that work on all platforms. """ -import pytest -import platform -from pathlib import Path -import sys import os +import platform +import sys +from pathlib import Path + +import pytest # Add parent directory to path to import utils sys.path.insert(0, str(Path(__file__).parent.parent / "python"))