mpflash 1.24.7__py3-none-any.whl → 1.25.0__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 (49) hide show
  1. mpflash/ask_input.py +7 -7
  2. mpflash/basicgit.py +26 -59
  3. mpflash/bootloader/__init__.py +0 -2
  4. mpflash/bootloader/detect.py +1 -2
  5. mpflash/bootloader/manual.py +0 -1
  6. mpflash/bootloader/touch1200.py +2 -2
  7. mpflash/cli_flash.py +28 -5
  8. mpflash/cli_group.py +1 -0
  9. mpflash/cli_list.py +7 -8
  10. mpflash/cli_main.py +2 -2
  11. mpflash/common.py +6 -14
  12. mpflash/config.py +30 -6
  13. mpflash/connected.py +6 -14
  14. mpflash/db/boards.py +63 -0
  15. mpflash/db/downloads.py +87 -0
  16. mpflash/download/__init__.py +221 -0
  17. mpflash/download/from_web.py +204 -0
  18. mpflash/downloaded.py +9 -34
  19. mpflash/flash/__init__.py +33 -18
  20. mpflash/flash/esp.py +39 -8
  21. mpflash/flash/uf2/linux.py +4 -9
  22. mpflash/flash/uf2/macos.py +1 -1
  23. mpflash/flash/uf2/windows.py +1 -1
  24. mpflash/flash/worklist.py +10 -5
  25. mpflash/list.py +17 -6
  26. mpflash/logger.py +1 -3
  27. mpflash/mpboard_id/__init__.py +6 -87
  28. mpflash/mpboard_id/add_boards.py +3 -8
  29. mpflash/mpboard_id/board.py +5 -2
  30. mpflash/mpboard_id/board_id.py +67 -7
  31. mpflash/mpboard_id/board_info.json +30974 -0
  32. mpflash/mpboard_id/board_info.zip +0 -0
  33. mpflash/mpboard_id/known.py +108 -0
  34. mpflash/mpboard_id/store.py +2 -3
  35. mpflash/mpremoteboard/__init__.py +85 -17
  36. mpflash/mpremoteboard/mpy_fw_info.py +23 -22
  37. mpflash/py.typed +0 -0
  38. mpflash/vendor/board_database.py +86 -1
  39. mpflash/vendor/click_aliases.py +64 -0
  40. mpflash/vendor/dfu.py +2 -8
  41. mpflash/vendor/pydfu.py +3 -14
  42. mpflash/versions.py +16 -6
  43. {mpflash-1.24.7.dist-info → mpflash-1.25.0.dist-info}/METADATA +71 -13
  44. mpflash-1.25.0.dist-info/RECORD +62 -0
  45. {mpflash-1.24.7.dist-info → mpflash-1.25.0.dist-info}/WHEEL +1 -1
  46. mpflash/download.py +0 -364
  47. mpflash-1.24.7.dist-info/RECORD +0 -56
  48. {mpflash-1.24.7.dist-info → mpflash-1.25.0.dist-info}/LICENSE +0 -0
  49. {mpflash-1.24.7.dist-info → mpflash-1.25.0.dist-info}/entry_points.txt +0 -0
mpflash/flash/esp.py CHANGED
@@ -5,7 +5,7 @@
5
5
  """
6
6
 
7
7
  from pathlib import Path
8
- from typing import List, Optional
8
+ from typing import List, Literal, Optional
9
9
 
10
10
  import esptool
11
11
  from loguru import logger as log
@@ -13,8 +13,16 @@ from loguru import logger as log
13
13
  from mpflash.mpboard_id import find_known_board
14
14
  from mpflash.mpremoteboard import MPRemoteBoard
15
15
 
16
+ FlashMode = Literal["keep", "qio", "qout", "dio", "dout"]
16
17
 
17
- def flash_esp(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool = True) -> Optional[MPRemoteBoard]:
18
+ def flash_esp(
19
+ mcu: MPRemoteBoard,
20
+ fw_file: Path,
21
+ *,
22
+ erase: bool = True,
23
+ flash_mode: FlashMode = "keep", # keep, qio, qout, dio, dout
24
+ flash_size: str = "detect",
25
+ ) -> Optional[MPRemoteBoard]:
18
26
  if mcu.port not in ["esp32", "esp8266"] or mcu.board.startswith("ARDUINO_"):
19
27
  log.error(f"esptool not supported for {mcu.port} {mcu.board} on {mcu.serialport}")
20
28
  return None
@@ -25,26 +33,49 @@ def flash_esp(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool = True) -> Optio
25
33
  mcu.cpu = find_known_board(mcu.board).cpu
26
34
 
27
35
  cmds: List[List[str]] = []
28
- if erase:
29
- cmds.append(f"esptool --chip {mcu.cpu} --port {mcu.serialport} erase_flash".split())
30
36
 
37
+ chip = "auto"
38
+ start_addr = "0x0"
31
39
  if mcu.cpu.upper().startswith("ESP32"):
40
+ start_addr = "0x0"
41
+
32
42
  baud_rate = str(921_600)
33
- if mcu.cpu.upper() in ("ESP32", "ESP32S2"):
43
+ if mcu.cpu.upper() == "ESP32":
44
+ start_addr = "0x1000"
45
+ chip = "esp32"
46
+ elif "C2" in mcu.cpu.upper():
47
+ start_addr = "0x1000"
48
+ chip = "esp32c2"
49
+ elif "S2" in mcu.cpu.upper():
34
50
  start_addr = "0x1000"
35
- elif mcu.cpu.upper() in ("ESP32S3", "ESP32C3"):
51
+ chip = "esp32s2"
52
+ baud_rate = str(460_800)
53
+ elif "S3" in mcu.cpu.upper():
36
54
  start_addr = "0x0"
55
+ chip = "esp32s3"
56
+ elif "C3" in mcu.cpu.upper():
57
+ start_addr = "0x0"
58
+ chip = "esp32c3"
59
+ elif "C6" in mcu.cpu.upper():
60
+ start_addr = "0x0"
61
+ chip = "esp32c6"
62
+ baud_rate = str(460_800)
63
+
37
64
  cmds.append(
38
- f"esptool --chip {mcu.cpu} --port {mcu.serialport} -b {baud_rate} write_flash --compress {start_addr}".split()
65
+ f"esptool --chip {chip} --port {mcu.serialport} -b {baud_rate} write_flash --flash_mode {flash_mode} --flash_size {flash_size} --compress {start_addr}".split()
39
66
  + [str(fw_file)]
40
67
  )
41
68
  elif mcu.cpu.upper() == "ESP8266":
42
69
  baud_rate = str(460_800)
43
70
  start_addr = "0x0"
71
+ chip = "esp8266"
44
72
  cmds.append(
45
- f"esptool --chip {mcu.cpu} --port {mcu.serialport} -b {baud_rate} write_flash --flash_size=detect {start_addr}".split()
73
+ f"esptool --chip {chip} --port {mcu.serialport} -b {baud_rate} write_flash --flash_mode {flash_mode} --flash_size=detect {start_addr}".split()
46
74
  + [str(fw_file)]
47
75
  )
76
+ # now that we have the chip, we can do the erare properly
77
+ if erase:
78
+ cmds.insert(0, f"esptool --chip {chip} --port {mcu.serialport} erase_flash".split())
48
79
  try:
49
80
  for cmd in cmds:
50
81
  log.info(f"Running {' '.join(cmd)} ")
@@ -1,4 +1,4 @@
1
- """ Flashing UF2 based MCU on Linux"""
1
+ """Flashing UF2 based MCU on Linux"""
2
2
 
3
3
  # sourcery skip: snake-case-functions
4
4
  from __future__ import annotations
@@ -42,10 +42,7 @@ def get_uf2_drives():
42
42
  uf2.mountpoint = uf2_part["mountpoint"]
43
43
  yield uf2
44
44
  elif disk["type"] == "disk" and disk.get("children") and len(disk.get("children")) > 0:
45
- if (
46
- disk.get("children")[0]["type"] == "part"
47
- and disk.get("children")[0]["fstype"] == "vfat"
48
- ):
45
+ if disk.get("children")[0]["type"] == "part" and disk.get("children")[0]["fstype"] == "vfat":
49
46
  uf2_part = disk.get("children")[0]
50
47
  # print( json.dumps(uf2_part, indent=4))
51
48
  uf2 = UF2Disk()
@@ -69,9 +66,7 @@ def pmount(disk: UF2Disk):
69
66
  # drive is always vfat - so specify to have quicker results
70
67
  subprocess.run(["pmount", "-t", "vfat", disk.device_path, disk.mountpoint])
71
68
  except FileNotFoundError:
72
- log.error(
73
- "pmount not found, please install it using 'sudo apt install pmount', and add this user to the plugdev group."
74
- )
69
+ log.error("pmount not found, please install it using 'sudo apt install pmount', and add this user to the plugdev group.")
75
70
  return
76
71
  log.debug(f"Mounted {disk.label} at {disk.mountpoint}")
77
72
  glb_dismount_me.append(disk)
@@ -89,7 +84,7 @@ def pumount(disk: UF2Disk):
89
84
  if disk.mountpoint:
90
85
  subprocess.run(["pumount", disk.mountpoint]) # ), f"/media/{disk.label}"])
91
86
  log.info(f"Unmounted {disk.label} from {disk.mountpoint}")
92
- disk.mountpoint = f""
87
+ disk.mountpoint = ""
93
88
  else:
94
89
  log.warning(f"{disk.label} already dismounted")
95
90
 
@@ -1,4 +1,4 @@
1
- """ Flashing UF2 based MCU on macos"""
1
+ """Flashing UF2 based MCU on macos"""
2
2
 
3
3
  # sourcery skip: snake-case-functions
4
4
  from __future__ import annotations
@@ -12,7 +12,7 @@ import psutil
12
12
  from rich.progress import track
13
13
 
14
14
 
15
- def wait_for_UF2_windows(board_id: str, s_max: int = 10)-> Optional[Path]:
15
+ def wait_for_UF2_windows(board_id: str, s_max: int = 10) -> Optional[Path]:
16
16
  """Wait for the MCU to mount as a drive"""
17
17
  if s_max < 1:
18
18
  s_max = 10
mpflash/flash/worklist.py CHANGED
@@ -43,8 +43,8 @@ def auto_update(
43
43
  log.warning(f"Skipping flashing {mcu.family} {mcu.port} {mcu.board} on {mcu.serialport} as it is not a MicroPython firmware")
44
44
  continue
45
45
  board_firmwares = find_downloaded_firmware(
46
- fw_folder=fw_folder,
47
- board_id=mcu.board,
46
+ db_path=fw_folder,
47
+ board_id=mcu.board if not mcu.variant else f"{mcu.board}-{mcu.variant}",
48
48
  version=target_version,
49
49
  port=mcu.port,
50
50
  selector=selector,
@@ -91,11 +91,11 @@ def manual_worklist(
91
91
  # need the CPU type for the esptool
92
92
  mcu.cpu = info.cpu
93
93
  except (LookupError, MPFlashError) as e:
94
- log.error(f"Board {board_id} not found in board_info.zip")
94
+ log.error(f"Board {board_id} not found in board database")
95
95
  log.exception(e)
96
96
  return []
97
97
  mcu.board = board_id
98
- firmwares = find_downloaded_firmware(fw_folder=fw_folder, board_id=board_id, version=version, port=mcu.port)
98
+ firmwares = find_downloaded_firmware(db_path=fw_folder, board_id=board_id, version=version, port=mcu.port)
99
99
  if not firmwares:
100
100
  log.error(f"No firmware found for {mcu.port} {board_id} version {version}")
101
101
  return []
@@ -127,7 +127,12 @@ def single_auto_worklist(
127
127
 
128
128
 
129
129
  def full_auto_worklist(
130
- all_boards: List[MPRemoteBoard], *, include: List[str], ignore: List[str], version: str, fw_folder: Path
130
+ all_boards: List[MPRemoteBoard],
131
+ *,
132
+ include: List[str],
133
+ ignore: List[str],
134
+ version: str,
135
+ fw_folder: Path,
131
136
  ) -> WorkList:
132
137
  """
133
138
  Create a worklist for all connected micropython boards based on the information retrieved from the board.
mpflash/list.py CHANGED
@@ -14,13 +14,16 @@ def show_mcus(
14
14
  conn_mcus: List[MPRemoteBoard],
15
15
  title: str = "Connected boards",
16
16
  refresh: bool = True,
17
+ *,
18
+ title_style="magenta",
19
+ header_style="bold magenta",
17
20
  ):
18
- console.print(mcu_table(conn_mcus, title, refresh))
21
+ console.print(mcu_table(conn_mcus, title, refresh, title_style=title_style, header_style=header_style))
19
22
 
20
23
 
21
24
  def abbrv_family(family: str, is_wide: bool) -> str:
22
25
  if not is_wide:
23
- ABRV = {"micropython": "upy", "circuitpython": "cpy", "unknown": "?"}
26
+ ABRV = {"micropython": "mpy", "circuitpython": "cpy", "unknown": "?"}
24
27
  return ABRV.get(family, family[:4])
25
28
  return family
26
29
 
@@ -29,15 +32,19 @@ def mcu_table(
29
32
  conn_mcus: List[MPRemoteBoard],
30
33
  title: str = "Connected boards",
31
34
  refresh: bool = True,
35
+ *,
36
+ title_style="magenta",
37
+ header_style="bold magenta",
32
38
  ):
33
39
  """
34
40
  builds a rich table with the connected boards information
35
- The columns of the table are adjusted to the terminal width
41
+ The columns of the table are adjusted to the terminal width > 90
36
42
  the columns are :
37
43
  Narrow Wide
38
44
  - Serial Yes Yes
39
45
  - Family abbrv. Yes
40
46
  - Port - yes
47
+ - Variant Yes Yes only if any of the mcus have a variant
41
48
  - Board Yes Yes BOARD_ID and Description, and the description from board_info.toml
42
49
  - CPU - Yes
43
50
  - Version Yes Yes
@@ -59,8 +66,8 @@ def mcu_table(
59
66
  continue
60
67
  table = Table(
61
68
  title=title,
62
- title_style="magenta",
63
- header_style="bold magenta",
69
+ title_style=title_style,
70
+ header_style=header_style,
64
71
  collapse_padding=True,
65
72
  padding=(0, 0),
66
73
  )
@@ -68,13 +75,15 @@ def mcu_table(
68
75
  # check if the terminal is wide enough to show all columns or if we need to collapse some
69
76
  is_wide = console.width > 99
70
77
  needs_build = any(mcu.build for mcu in conn_mcus)
78
+ needs_variant = any(mcu.variant for mcu in conn_mcus)
71
79
 
72
80
  table.add_column("Serial" if is_wide else "Ser.", overflow="fold")
73
81
  table.add_column("Family" if is_wide else "Fam.", overflow="crop", max_width=None if is_wide else 4)
74
82
  if is_wide:
75
83
  table.add_column("Port")
76
84
  table.add_column("Board", overflow="fold")
77
- # table.add_column("Variant") # TODO: add variant
85
+ if needs_variant:
86
+ table.add_column("Variant")
78
87
  if is_wide:
79
88
  table.add_column("CPU")
80
89
  table.add_column("Version", overflow="fold", min_width=5, max_width=16)
@@ -94,6 +103,8 @@ def mcu_table(
94
103
  if is_wide:
95
104
  row.append(mcu.port)
96
105
  row.append(f"{mcu.board}\n{description}".strip())
106
+ if needs_variant:
107
+ row.append(mcu.variant)
97
108
  if is_wide:
98
109
  row.append(mcu.cpu)
99
110
  row.append(clean_version(mcu.version))
mpflash/logger.py CHANGED
@@ -20,9 +20,7 @@ def _log_formatter(record: dict) -> str:
20
20
  "CRITICAL": "bold white on red",
21
21
  }
22
22
  lvl_color = color_map.get(record["level"].name, "cyan")
23
- return (
24
- "[not bold green]{time:HH:mm:ss}[/not bold green] | {level.icon} " + f"[{lvl_color}]{{message}}[/{lvl_color}]"
25
- )
23
+ return "[not bold green]{time:HH:mm:ss}[/not bold green] | {level.icon} " + f"[{lvl_color}]{{message}}[/{lvl_color}]"
26
24
 
27
25
 
28
26
  def set_loglevel(loglevel: str):
@@ -6,93 +6,12 @@ that is included in the module.
6
6
 
7
7
  from functools import lru_cache
8
8
  from typing import List, Optional, Tuple
9
-
9
+ import importlib
10
10
  from mpflash.errors import MPFlashError
11
- from mpflash.mpboard_id.board import Board
12
- from mpflash.mpboard_id.store import read_known_boardinfo
13
- from mpflash.versions import clean_version
14
-
15
- # KNOWN ports and boards are sourced from the micropython repo,
16
- # this info is stored in the board_info.json file
17
-
18
-
19
- def get_known_ports() -> List[str]:
20
- # TODO: Filter for Version
21
- mp_boards = read_known_boardinfo()
22
- # select the unique ports from info
23
- ports = set({board.port for board in mp_boards if board.port})
24
- return sorted(list(ports))
25
-
26
-
27
- def get_known_boards_for_port(
28
- port: Optional[str] = "", versions: Optional[List[str]] = None
29
- ) -> List[Board]:
30
- """
31
- Returns a list of boards for the given port and version(s)
32
-
33
- port: The Micropython port to filter for
34
- versions: Optional, The Micropython versions to filter for (actual versions required)
35
- """
36
- mp_boards = read_known_boardinfo()
37
- if versions:
38
- preview_or_stable = "preview" in versions or "stable" in versions
39
- else:
40
- preview_or_stable = False
41
-
42
- # filter for 'preview' as they are not in the board_info.json
43
- # instead use stable version
44
- versions = versions or []
45
- if "preview" in versions:
46
- versions.remove("preview")
47
- versions.append("stable")
48
- if versions:
49
- # make sure of the v prefix
50
- versions = [clean_version(v) for v in versions]
51
- # filter for the version(s)
52
- mp_boards = [board for board in mp_boards if board.version in versions]
53
- if not mp_boards and preview_or_stable:
54
- # nothing found - perhaps there is a newer version for which we do not have the board info yet
55
- # use the latest known version from the board info
56
- mp_boards = read_known_boardinfo()
57
- last_known_version = sorted({b.version for b in mp_boards})[-1]
58
- mp_boards = [board for board in mp_boards if board.version == last_known_version]
59
-
60
- # filter for the port
61
- if port:
62
- mp_boards = [board for board in mp_boards if board.port == port]
63
- return mp_boards
64
-
65
-
66
- def known_stored_boards(port: str, versions: Optional[List[str]] = None) -> List[Tuple[str, str]]:
67
- """
68
- Returns a list of tuples with the description and board name for the given port and version
69
-
70
- port : str : The Micropython port to filter for
71
- versions : List[str] : The Micropython versions to filter for (actual versions required)
72
- """
73
- mp_boards = get_known_boards_for_port(port, versions)
74
-
75
- boards = set({(f"{board.version} {board.description}", board.board_id) for board in mp_boards})
76
- return sorted(list(boards))
11
+ from .board import Board
77
12
 
13
+ from mpflash.versions import clean_version
14
+ from .store import read_known_boardinfo
15
+ from .known import get_known_ports, get_known_boards_for_port
16
+ from .known import known_stored_boards, find_known_board
78
17
 
79
- @lru_cache(maxsize=20)
80
- def find_known_board(board_id: str) -> Board:
81
- """Find the board for the given BOARD_ID or 'board description' and return the board info as a Board object"""
82
- # Some functional overlap with:
83
- # mpboard_id\board_id.py _find_board_id_by_description
84
- info = read_known_boardinfo()
85
- for board_info in info:
86
- if board_id in (
87
- board_info.board_id,
88
- board_info.description,
89
- ) or board_info.description.startswith(board_id):
90
- if not board_info.cpu:
91
- # failsafe for older board_info.json files
92
- print(f"Board {board_id} has no CPU info, using port as CPU")
93
- if " with " in board_info.description:
94
- board_info.cpu = board_info.description.split(" with ")[-1]
95
- else:
96
- board_info.cpu = board_info.port
97
- return board_info
98
- raise MPFlashError(f"Board {board_id} not found")
@@ -16,8 +16,7 @@ import mpflash.basicgit as git
16
16
  from mpflash.logger import log
17
17
  from mpflash.mpboard_id import Board
18
18
  from mpflash.mpboard_id.store import write_boardinfo_json
19
- from mpflash.versions import (get_preview_mp_version, get_stable_mp_version,
20
- micropython_versions)
19
+ from mpflash.versions import get_preview_mp_version, micropython_versions
21
20
 
22
21
  # look for all mpconfigboard.h files and extract the board name
23
22
  # from the #define MICROPY_HW_BOARD_NAME "PYBD_SF6"
@@ -25,9 +24,7 @@ from mpflash.versions import (get_preview_mp_version, get_stable_mp_version,
25
24
  RE_H_MICROPY_HW_BOARD_NAME = re.compile(r"#define\s+MICROPY_HW_BOARD_NAME\s+\"(.+)\"")
26
25
  RE_H_MICROPY_HW_MCU_NAME = re.compile(r"#define\s+MICROPY_HW_MCU_NAME\s+\"(.+)\"")
27
26
  # find boards and variants in the mpconfigboard*.cmake files
28
- RE_CMAKE_MICROPY_HW_BOARD_NAME = re.compile(
29
- r"MICROPY_HW_BOARD_NAME\s?=\s?\"(?P<variant>[\w\s\S]*)\""
30
- )
27
+ RE_CMAKE_MICROPY_HW_BOARD_NAME = re.compile(r"MICROPY_HW_BOARD_NAME\s?=\s?\"(?P<variant>[\w\s\S]*)\"")
31
28
  RE_CMAKE_MICROPY_HW_MCU_NAME = re.compile(r"MICROPY_HW_MCU_NAME\s?=\s?\"(?P<variant>[\w\s\S]*)\"")
32
29
  # TODO: normal make files
33
30
 
@@ -121,9 +118,7 @@ def boards_from_headers(mpy_path: Path, version: str, family: str):
121
118
  mcu_name = match[1]
122
119
  found += 1
123
120
  if found == 2:
124
- description = (
125
- f"{board_name} with {mcu_name}" if mcu_name != "-" else board_name
126
- )
121
+ description = f"{board_name} with {mcu_name}" if mcu_name != "-" else board_name
127
122
  board_list.append(
128
123
  Board(
129
124
  board_id=board,
@@ -21,17 +21,20 @@ class Board:
21
21
  mcu_name: str = field(default="")
22
22
  cpu: str = field(default="")
23
23
  variant: str = field(default="")
24
+ mcu: str = field(default="")
24
25
 
25
26
  def __post_init__(self):
26
27
  if not self.cpu:
27
28
  if " with " in self.description:
28
- self.cpu = self.description.split(" with ")[-1]
29
+ self.cpu = self.description.rsplit(" with ")[-1]
29
30
  else:
30
31
  self.cpu = self.port
31
32
 
32
33
  @staticmethod
33
34
  def from_dict(data: dict) -> "Board":
34
- return Board(**data)
35
+ valid_keys = {field.name for field in Board.__dataclass_fields__.values()}
36
+ filtered_data = {k: v for k, v in data.items() if k in valid_keys}
37
+ return Board(**filtered_data)
35
38
 
36
39
  def to_dict(self) -> dict:
37
40
  return self.__dict__
@@ -3,11 +3,15 @@ Translate board description to board designator
3
3
  """
4
4
 
5
5
  import functools
6
+ import re
7
+ import sqlite3
6
8
  from pathlib import Path
7
- from typing import Optional
9
+ from typing import List, Optional
8
10
 
11
+ from mpflash.config import config
9
12
  from mpflash.errors import MPFlashError
10
13
  from mpflash.logger import log
14
+ from mpflash.mpboard_id.board import Board
11
15
  from mpflash.mpboard_id.store import read_known_boardinfo
12
16
  from mpflash.versions import clean_version, get_preview_mp_version, get_stable_mp_version
13
17
 
@@ -20,21 +24,78 @@ def find_board_id_by_description(
20
24
  board_info: Optional[Path] = None,
21
25
  ) -> Optional[str]:
22
26
  """Find the MicroPython BOARD_ID based on the description in the firmware"""
23
-
27
+ version = clean_version(version) if version else ""
24
28
  try:
25
29
  boards = _find_board_id_by_description(
26
30
  descr=descr,
27
31
  short_descr=short_descr,
28
- board_info=board_info,
29
- version=clean_version(version) if version else None,
32
+ db_path=board_info,
33
+ version=version,
30
34
  )
31
- return boards[-1].board_id
35
+ if not boards:
36
+ log.debug(f"Version {version} not found in board info, using any version")
37
+ boards = _find_board_id_by_description(
38
+ descr=descr,
39
+ short_descr=short_descr,
40
+ db_path=board_info,
41
+ version="%", # any version
42
+ )
43
+ return boards[0].board_id if boards else None
32
44
  except MPFlashError:
33
45
  return "UNKNOWN_BOARD"
34
46
 
35
47
 
36
- @functools.lru_cache(maxsize=20)
37
48
  def _find_board_id_by_description(
49
+ *,
50
+ descr: str,
51
+ short_descr: str,
52
+ version: Optional[str] = None,
53
+ variant: str = "",
54
+ db_path: Optional[Path] = None,
55
+ ):
56
+ short_descr = short_descr or ""
57
+ boards: List[Board] = []
58
+ version = clean_version(version) if version else "%"
59
+ if "-preview" in version:
60
+ version = version.replace("-preview", "%")
61
+ descriptions = [descr, short_descr]
62
+ if descr.startswith("Generic"):
63
+ descriptions.append(descr[8:])
64
+ descriptions.append(short_descr[8:])
65
+
66
+ try:
67
+ with sqlite3.connect(db_path or config.db_path) as conn:
68
+ conn.row_factory = sqlite3.Row
69
+ cursor = conn.cursor()
70
+
71
+ qry = f"""
72
+ SELECT
73
+ *
74
+ FROM board_downloaded
75
+ WHERE
76
+ board_id IN (
77
+ SELECT DISTINCT board_id
78
+ FROM board_downloaded
79
+ WHERE description IN {str(tuple(descriptions))}
80
+ )
81
+ AND version like '{version}'
82
+ AND variant like '{variant}'
83
+ """
84
+ cursor.execute(qry)
85
+ rows = cursor.fetchall()
86
+ for row in rows:
87
+ r = dict(row)
88
+
89
+ boards.append(Board.from_dict(dict(row)))
90
+ except sqlite3.OperationalError as e:
91
+ raise MPFlashError("Database error") from e
92
+ if not boards:
93
+ raise MPFlashError(f"No board info found for description '{descr}' or '{short_descr}'")
94
+ return boards
95
+
96
+
97
+ @functools.lru_cache(maxsize=20)
98
+ def _find_board_id_by_description_xx(
38
99
  *,
39
100
  descr: str,
40
101
  short_descr: str,
@@ -89,4 +150,3 @@ def _find_board_id_by_description(
89
150
  if not matches:
90
151
  raise MPFlashError(f"No board info found for description '{descr}' or '{short_descr}'")
91
152
  return sorted(matches, key=lambda x: x.version)
92
-