77
88import re
99import subprocess
10- from typing import Optional , Dict , List , Tuple
11- from functools import lru_cache
10+ import sys
1211from dataclasses import dataclass
12+ from functools import lru_cache
1313from pathlib import Path
14+ from typing import Dict , List , Optional , Tuple
1415
15- from mpflash .logger import log
1616from mpflash .errors import MPFlashError
17+ from mpflash .logger import log
1718from mpflash .mpremoteboard import MPRemoteBoard
1819
19-
2020# =============================================================================
2121# Secure Subprocess Utilities
2222# =============================================================================
@@ -67,6 +67,33 @@ def _run_pyocd_command(args: List[str], timeout: int = 30) -> subprocess.Complet
6767 raise MPFlashError (f"Command execution failed: { e } " )
6868
6969
70+ def _check_libusb_windows () -> None :
71+ """Warn if pyusb cannot load a libusb backend on Windows.
72+
73+ libusb-package (pyocd's official solution) only ships pre-built wheels up
74+ to Python 3.13. On newer Python versions the source build silently produces
75+ an empty package, so pyocd finds no probes.
76+ """
77+ if sys .platform != "win32" :
78+ return
79+ try :
80+ import libusb_package # type: ignore
81+
82+ if libusb_package .get_libusb1_backend () is None :
83+ log .warning (
84+ "pyocd: no libusb backend available on Windows — debug probes will not be detected.\n "
85+ f" Current Python: { sys .version .split ()[0 ]} \n "
86+ " libusb-package only bundles a pre-built DLL for Python ≤ 3.13.\n "
87+ " Workaround: recreate your venv with Python 3.13 (e.g. `uv venv --python 3.13 --clear`).\n "
88+ " See: https://github.com/pyocd/libusb-package/issues/28"
89+ )
90+ except ImportError :
91+ log .warning (
92+ "pyocd: libusb-package is not installed — debug probes may not be detected on Windows.\n "
93+ " Fix: install it with `uv sync --extra pyocd`."
94+ )
95+
96+
7097# Lazy import for pyOCD to handle optional dependency
7198_pyocd_available = None
7299_pyocd_modules = {}
@@ -77,6 +104,7 @@ def _ensure_pyocd():
77104 global _pyocd_available , _pyocd_modules
78105
79106 if _pyocd_available is None :
107+ _check_libusb_windows ()
80108 try :
81109 import pyocd
82110 _pyocd_modules ['pyocd_version' ] = pyocd .__version__
0 commit comments