mpflash 1.25.1__py3-none-any.whl → 1.25.2__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.
mpflash/db/core.py CHANGED
@@ -2,7 +2,8 @@ from pathlib import Path
2
2
  from sqlite3 import DatabaseError, OperationalError
3
3
 
4
4
  from loguru import logger as log
5
- from sqlalchemy import create_engine
5
+ from sqlalchemy import create_engine, text
6
+ from sqlalchemy.exc import SQLAlchemyError
6
7
  from sqlalchemy.orm import sessionmaker
7
8
 
8
9
  from mpflash.config import config
@@ -13,18 +14,90 @@ from .models import Base
13
14
 
14
15
  TRACE = False
15
16
  connect_str = f"sqlite:///{config.db_path.as_posix()}"
17
+ log.debug(f"Connecting to database at {connect_str}")
16
18
  engine = create_engine(connect_str, echo=TRACE)
17
19
  Session = sessionmaker(bind=engine)
18
20
 
19
21
 
22
+ def get_schema_version() -> int:
23
+ """Get current database schema version."""
24
+ from .meta import get_metadata_value
25
+
26
+ version = get_metadata_value("schema_version")
27
+ return int(version) if version else 0
28
+
29
+
30
+ def set_schema_version(version: int) -> None:
31
+ """Set database schema version."""
32
+ from .meta import set_metadata_value
33
+
34
+ set_metadata_value("schema_version", str(version))
35
+
36
+
37
+ def migration_001_add_custom_id() -> None:
38
+ """Add custom_id column to firmwares table."""
39
+ log.info("Running migration 001: Add custom_id column to firmwares table")
40
+
41
+ with Session() as session:
42
+ # Check if column already exists
43
+ result = session.execute(text("PRAGMA table_info(firmwares)")).fetchall()
44
+
45
+ columns = [row[1] for row in result]
46
+ if "custom_id" not in columns:
47
+ # Add column without UNIQUE constraint
48
+ session.execute(text("ALTER TABLE firmwares ADD COLUMN custom_id VARCHAR(40)"))
49
+
50
+ # Create regular index for performance
51
+ session.execute(text("CREATE INDEX IF NOT EXISTS idx_firmwares_custom_id ON firmwares(custom_id)"))
52
+
53
+ session.commit()
54
+ log.info("Added custom_id column and index to firmwares table")
55
+ else:
56
+ log.info("custom_id column already exists in firmwares table")
57
+
58
+ # Check if index exists and create if needed
59
+ result = session.execute(text("PRAGMA index_list(firmwares)")).fetchall()
60
+
61
+ index_names = [row[1] for row in result]
62
+ if "idx_firmwares_custom_id" not in index_names:
63
+ session.execute(text("CREATE INDEX IF NOT EXISTS idx_firmwares_custom_id ON firmwares(custom_id)"))
64
+ session.commit()
65
+ log.info("Added index to existing custom_id column")
66
+
67
+
68
+ def run_schema_migrations() -> None:
69
+ """Run all pending database schema migrations."""
70
+ current_version = get_schema_version()
71
+ target_version = 1 # Current latest version
72
+
73
+ if current_version >= target_version:
74
+ log.debug(f"Database schema is up to date (version {current_version})")
75
+ return
76
+
77
+ log.info(f"Upgrading database schema from version {current_version} to {target_version}")
78
+
79
+ try:
80
+ # Run migrations in order
81
+ if current_version < 1:
82
+ migration_001_add_custom_id()
83
+
84
+ # Update schema version
85
+ set_schema_version(target_version)
86
+ log.info(f"Database schema upgraded to version {target_version}")
87
+
88
+ except SQLAlchemyError as e:
89
+ log.error(f"Migration failed: {e}")
90
+ raise MPFlashError(f"Failed to migrate database schema: {e}")
91
+
92
+
20
93
  def migrate_database(boards: bool = True, firmwares: bool = True):
21
- """Migrate from 1.24.x to 1.25.x"""
94
+ """Migrate from 1.24.x to 1.25.x and run schema migrations."""
22
95
  # Move import here to avoid circular import
23
96
  from .loader import load_jsonl_to_db, update_boards
24
97
 
25
98
  # get the location of the database from the session
26
99
  with Session() as session:
27
- db_location = session.get_bind().url.database # type: ignore
100
+ db_location = session.get_bind().url.database # type: ignore
28
101
  log.debug(f"Database location: {Path(db_location)}") # type: ignore
29
102
 
30
103
  try:
@@ -33,6 +106,10 @@ def migrate_database(boards: bool = True, firmwares: bool = True):
33
106
  log.error(f"Error creating database: {e}")
34
107
  log.error("Database might already exist, trying to migrate.")
35
108
  raise MPFlashError("Database migration failed. Please check the logs for more details.") from e
109
+
110
+ # Run schema migrations after creating database
111
+ run_schema_migrations()
112
+
36
113
  if boards:
37
114
  update_boards()
38
115
  if firmwares:
@@ -54,8 +131,9 @@ def migrate_database(boards: bool = True, firmwares: bool = True):
54
131
 
55
132
 
56
133
  def create_database():
57
- """
58
- Create the SQLite database and tables if they don't exist.
59
- """
134
+ """Create the SQLite database and tables if they don't exist."""
60
135
  # Create the database and tables if they don't exist
61
136
  Base.metadata.create_all(engine)
137
+
138
+ # Run schema migrations after table creation
139
+ run_schema_migrations()
@@ -1,11 +1,11 @@
1
1
  from os import path
2
2
  from pathlib import Path
3
- from mpflash.mpremoteboard import HERE
4
- from mpflash.vendor.board_database import Database
5
- from mpflash.logger import log
6
-
7
3
  from typing import List
4
+
8
5
  import mpflash.basicgit as git
6
+ from mpflash.logger import log
7
+ from mpflash.mpremoteboard import HERE
8
+ from mpflash.vendor.board_database import Database
9
9
  from mpflash.versions import micropython_versions
10
10
 
11
11
  HERE = Path(__file__).parent.resolve()
@@ -79,6 +79,7 @@ def create_zip_file(longlist, zip_file: Path):
79
79
  """Create a ZIP file containing the CSV data."""
80
80
  # lazy import
81
81
  import zipfile
82
+
82
83
  import pandas as pd
83
84
 
84
85
  csv_filename = "micropython_boards.csv"
@@ -96,6 +97,7 @@ def create_zip_file(longlist, zip_file: Path):
96
97
 
97
98
  def package_repo(mpy_path: Path):
98
99
  mpy_path = mpy_path or Path("../repos/micropython")
100
+ log.info(f"Packaging Micropython boards from {mpy_path}")
99
101
  mp_versions = micropython_versions(minver="1.18")
100
102
  longlist = boardlist_from_repo(
101
103
  versions=mp_versions,
mpflash/db/loader.py CHANGED
@@ -24,7 +24,7 @@ def load_data_from_zip(zip_file: Path) -> int:
24
24
  if not zip_file.exists() or not zip_file.is_file():
25
25
  log.error(f"Zip file {zip_file} not found.")
26
26
  return 0
27
- count = 0
27
+ count = 0
28
28
  # Load data directly from the zip file
29
29
  with zipfile.ZipFile(zip_file, "r") as zipf:
30
30
  # Read the CSV file from the zip
@@ -109,14 +109,15 @@ def load_jsonl_to_db(jsonl_path: Path):
109
109
 
110
110
 
111
111
  def update_boards():
112
+ boards_version = "v1.25.2"
112
113
  try:
113
114
  meta = get_metadata()
114
115
  log.debug(f"Metadata: {meta}")
115
- if meta.get("boards_version", "") < "v1.25.0":
116
+ if meta.get("boards_version", "") < boards_version:
116
117
  log.info("Update boards from CSV to SQLite database.")
117
118
  # Load data from the zip file into the database
118
119
  load_data_from_zip(HERE / "micropython_boards.zip")
119
- set_metadata_value("boards_version", "v1.25.0")
120
+ set_metadata_value("boards_version", boards_version)
120
121
  meta = get_metadata()
121
122
  except Exception as e:
122
123
  raise MPFlashError(f"Error updating boards table: {e}") from e
mpflash/db/meta.py CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  from typing import Optional
3
2
 
4
3
  from loguru import logger as log
@@ -18,10 +17,11 @@ def get_metadata() -> dict:
18
17
  with Session() as session:
19
18
  metadata = session.query(Metadata).all()
20
19
  return {m.name: m.value for m in metadata}
21
- except (DatabaseError, OperationalError) as e:
20
+ except (DatabaseError, OperationalError) as e:
22
21
  log.error(f"Error retrieving metadata: {e}")
23
22
  return {}
24
-
23
+
24
+
25
25
  def set_metadata(metadata: dict):
26
26
  """
27
27
  Set metadata in the database.
@@ -56,6 +56,7 @@ def get_metadata_value(name: str) -> Optional[str]:
56
56
  metadata = session.query(Metadata).filter(Metadata.name == name).first()
57
57
  return metadata.value if metadata else None
58
58
 
59
+
59
60
  def set_metadata_value(name: str, value: str):
60
61
  """
61
62
  Set metadata value by name.
Binary file
mpflash/db/models.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from pathlib import Path
2
+ from typing import Union
2
3
 
3
4
  import sqlalchemy as sa
4
5
  from sqlalchemy import Index, String
@@ -78,6 +79,7 @@ class Firmware(Base):
78
79
  source: Mapped[str] = mapped_column()
79
80
  build: Mapped[int] = mapped_column(default=0, comment="Build number")
80
81
  custom: Mapped[bool] = mapped_column(default=False, comment="True if this is a custom firmware")
82
+ custom_id: Mapped[Union[str, None]] = mapped_column(String(40), nullable=True, default=None)
81
83
 
82
84
  @property
83
85
  def preview(self) -> bool:
@@ -4,6 +4,7 @@ Uses the micropython.org website to get the available versions and locations to
4
4
  """
5
5
 
6
6
  import itertools
7
+ import re
7
8
  from pathlib import Path
8
9
  from typing import Dict, List, Optional
9
10
 
@@ -16,7 +17,7 @@ from rich.progress import track
16
17
  from mpflash.common import PORT_FWTYPES
17
18
  from mpflash.config import config
18
19
  from mpflash.db.core import Session
19
- from mpflash.db.models import Firmware
20
+ from mpflash.db.models import Firmware, Board
20
21
  from mpflash.downloaded import clean_downloaded_firmwares
21
22
  from mpflash.errors import MPFlashError
22
23
  from mpflash.mpboard_id.alternate import add_renamed_boards
@@ -36,6 +37,56 @@ def key_fw_boardid_preview_ext(fw: Firmware):
36
37
  return fw.board_id, fw.preview, fw.ext
37
38
 
38
39
 
40
+
41
+
42
+ # Cache for variant suffixes - will be populated on first use
43
+ _PATTERN = ""
44
+
45
+ def _get_variant_pattern():
46
+ """
47
+ Query the database for all known variant suffixes.
48
+ This is done only once and the result is cached.
49
+
50
+ Returns:
51
+ List of known variant suffixes.
52
+ """
53
+ global _PATTERN
54
+
55
+ if _PATTERN:
56
+ # If the pattern is already set, return it
57
+ return _PATTERN
58
+
59
+ with Session() as session:
60
+ # Query distinct variants
61
+ variants = session.query(Board.variant).distinct().all()
62
+
63
+
64
+ _VARIANT_SUFFIXES = [f"_{v[0]}" for v in variants if v[0] and v[0].strip()]
65
+ # workaround for the fact that the variant names do not always match the suffixes
66
+ # e.g. 'PIMORONI_PICOLIPO' has the variant 'FLASH_16MB' but the suffix is '_16MB'
67
+ _VARIANT_SUFFIXES.extend( ["_2MB", "_4MB", "_8MB", "_16MB"] ) # add common SPIRAM size suffixes
68
+
69
+ # return _VARIANT_SUFFIXES
70
+ _PATTERN = f"({'|'.join(re.escape(v) for v in _VARIANT_SUFFIXES)})$"
71
+ return _PATTERN
72
+
73
+
74
+
75
+ def strip_variant(board: str) -> str:
76
+ """
77
+ Strips the variant suffix from the board name based on variants in the database.
78
+ For example, 'RPI_PICO_W_SPIRAM' becomes 'RPI_PICO_W'.
79
+
80
+ Args:
81
+ board: The board name to process.
82
+
83
+ Returns:
84
+ The board name without the variant suffix.
85
+ """
86
+ pattern = _get_variant_pattern()
87
+ return re.sub(pattern, "", board)
88
+
89
+
39
90
  def download_firmwares(
40
91
  firmware_folder: Path,
41
92
  ports: List[str],
@@ -60,9 +111,15 @@ def download_firmwares(
60
111
 
61
112
  downloaded = 0
62
113
  versions = [] if versions is None else [clean_version(v) for v in versions]
114
+
115
+ # remove the known variant suffixes from the boards
116
+ # TODO: IS THIS REALLY NEEDED ?
117
+ # boards = [strip_variant(b) for b in boards]
118
+
63
119
  # handle downloading firmware for renamed boards
64
120
  boards = add_renamed_boards(boards)
65
121
 
122
+
66
123
  available_firmwares = get_firmware_list(ports, boards, versions, clean)
67
124
 
68
125
  for b in available_firmwares:
@@ -38,4 +38,4 @@ class FWInfo:
38
38
  data["ext"] = Path(data["filename"]).suffix
39
39
  if "family" not in data:
40
40
  data["family"] = "micropython"
41
- return FWInfo(**filtered_data)
41
+ return FWInfo(**filtered_data)
mpflash/downloaded.py CHANGED
@@ -59,17 +59,22 @@ def find_downloaded_firmware(
59
59
  version: str = "",
60
60
  port: str = "",
61
61
  variants: bool = False,
62
+ custom: bool = False,
62
63
  ) -> List[Firmware]:
63
64
  version = clean_version(version)
64
65
  log.debug(f"Looking for firmware for {board_id} {version} ")
65
66
  # Special handling for preview versions
66
67
  with Session() as session:
67
- if version == "preview" or "preview" in version:
68
+ if "preview" in version:
68
69
  # Find all preview firmwares for this board/port, return the latest (highest build)
69
- query = session.query(Firmware).filter(Firmware.board_id == board_id)
70
+ if custom:
71
+ query = session.query(Firmware).filter(Firmware.custom_id == board_id)
72
+ else:
73
+ query = session.query(Firmware).filter(Firmware.board_id == board_id)
70
74
  if port:
71
75
  query = query.filter(Firmware.port == port)
72
76
  query = query.filter(Firmware.firmware_file.contains("preview")).order_by(Firmware.build.desc())
77
+ log.trace(f"Querying for preview firmware: {query}")
73
78
  fw_list = query.all()
74
79
  if fw_list:
75
80
  return [fw_list[0]] # Return the latest preview only
@@ -82,7 +87,7 @@ def find_downloaded_firmware(
82
87
  #
83
88
  log.debug(f"2nd search with renamed board_id :{board_id}")
84
89
  with Session() as session:
85
- if version == "preview" or "preview" in version:
90
+ if "preview" in version:
86
91
  query = session.query(Firmware).filter(Firmware.board_id.in_(more_board_ids))
87
92
  if port:
88
93
  query = query.filter(Firmware.port == port)
@@ -96,4 +101,3 @@ def find_downloaded_firmware(
96
101
  return fw_list
97
102
  log.warning(f"No firmware files found for board {board_id} version {version}")
98
103
  return []
99
-
mpflash/flash/__init__.py CHANGED
@@ -39,6 +39,15 @@ def flash_list(
39
39
  log.error(f"Failed to flash {mcu.board} on {mcu.serialport}: {e}")
40
40
  continue
41
41
  if updated:
42
+ if fw_info.custom:
43
+ # Add / Update board_info.toml with the custom_id and Description
44
+ mcu.get_board_info_toml()
45
+ if fw_info.description:
46
+ mcu.toml["description"] = fw_info.description
47
+ mcu.toml["mpflash"]["board_id"] = fw_info.board_id
48
+ mcu.toml["mpflash"]["custom_id"] = fw_info.custom_id
49
+ mcu.set_board_info_toml()
50
+
42
51
  flashed.append(updated)
43
52
  else:
44
53
  log.error(f"Failed to flash {mcu.board} on {mcu.serialport}")
@@ -51,7 +60,7 @@ def flash_mcu(
51
60
  fw_file: Path,
52
61
  erase: bool = False,
53
62
  bootloader: BootloaderMethod = BootloaderMethod.AUTO,
54
- **kwargs
63
+ **kwargs
55
64
  ):
56
65
  """Flash a single MCU with the specified firmware."""
57
66
  updated = None
mpflash/flash/worklist.py CHANGED
@@ -34,6 +34,7 @@ def auto_update_worklist(
34
34
  Returns:
35
35
  WorkList: List of boards and firmware information to update
36
36
  """
37
+ log.debug(f"auto_update_worklist: {len(conn_boards)} boards, target version: {target_version}")
37
38
  wl: WorkList = []
38
39
  for mcu in conn_boards:
39
40
  if mcu.family not in ("micropython", "unknown"):
@@ -65,12 +66,14 @@ def manual_worklist(
65
66
  *,
66
67
  board_id: str,
67
68
  version: str,
69
+ custom: bool = False,
68
70
  ) -> WorkList:
69
71
  """Create a worklist for manually specified boards."""
72
+ log.debug(f"manual_worklist: {len(serial)} serial ports, board_id: {board_id}, version: {version}")
70
73
  wl: WorkList = []
71
74
  for comport in serial:
72
75
  log.trace(f"Manual updating {comport} to {board_id} {version}")
73
- wl.append(manual_board(comport, board_id=board_id, version=version))
76
+ wl.append(manual_board(comport, board_id=board_id, version=version, custom=custom))
74
77
  return wl
75
78
 
76
79
 
@@ -79,6 +82,7 @@ def manual_board(
79
82
  *,
80
83
  board_id: str,
81
84
  version: str,
85
+ custom: bool = False,
82
86
  ) -> FlashItem:
83
87
  """Create a Flash work item for a single board specified manually.
84
88
 
@@ -90,7 +94,7 @@ def manual_board(
90
94
  Returns:
91
95
  FlashItem: Board and firmware information to update
92
96
  """
93
- log.trace(f"Manual updating {serial} to {board_id} {version}")
97
+ log.debug(f"manual_board: {serial} {board_id} {version}")
94
98
  mcu = MPRemoteBoard(serial)
95
99
  # Lookup the matching port and cpu in board_info based in the board name
96
100
  try:
@@ -103,7 +107,7 @@ def manual_board(
103
107
  log.exception(e)
104
108
  return (mcu, None)
105
109
  mcu.board = board_id
106
- firmwares = find_downloaded_firmware(board_id=board_id, version=version, port=mcu.port)
110
+ firmwares = find_downloaded_firmware(board_id=board_id, version=version, port=mcu.port, custom=custom)
107
111
  if not firmwares:
108
112
  log.trace(f"No firmware found for {mcu.port} {board_id} version {version}")
109
113
  return (mcu, None)
@@ -125,6 +129,7 @@ def single_auto_worklist(
125
129
  Returns:
126
130
  WorkList: List of boards and firmware information to update
127
131
  """
132
+ log.debug(f"single_auto_worklist: {serial} version: {version}")
128
133
  log.trace(f"Auto updating {serial} to {version}")
129
134
  conn_boards = [MPRemoteBoard(serial)]
130
135
  todo = auto_update_worklist(conn_boards, version) # type: ignore # List / list
@@ -149,7 +154,7 @@ def full_auto_worklist(
149
154
  Returns:
150
155
  WorkList: List of boards and firmware information to update
151
156
  """
152
- log.trace(f"Auto updating all boards to {version}")
157
+ log.debug(f"full_auto_worklist: {len(all_boards)} boards, include: {include}, ignore: {ignore}, version: {version}")
153
158
  if selected_boards := filter_boards(all_boards, include=include, ignore=ignore):
154
159
  return auto_update_worklist(selected_boards, version)
155
160
  else:
mpflash/list.py CHANGED
@@ -90,6 +90,7 @@ def mcu_table(
90
90
  if needs_build:
91
91
  table.add_column("Build" if is_wide else "Bld", justify="right")
92
92
  if config.usb:
93
+ table.add_column("vid:pid", overflow="fold", max_width=14)
93
94
  table.add_column("Location", overflow="fold", max_width=60)
94
95
  # fill the table with the data
95
96
  for mcu in conn_mcus:
@@ -111,6 +112,7 @@ def mcu_table(
111
112
  if needs_build:
112
113
  row.append(mcu.build)
113
114
  if config.usb:
115
+ row.append(f"{mcu.vid:04x}:{mcu.pid:04x}")
114
116
  row.append(mcu.location)
115
117
 
116
118
  table.add_row(*row)
mpflash/logger.py CHANGED
@@ -15,6 +15,7 @@ from .config import config
15
15
 
16
16
  console = Console()
17
17
 
18
+
18
19
  # Detect if the output encoding supports Unicode (UTF-8)
19
20
  @functools.lru_cache(maxsize=1)
20
21
  def _is_utf8_encoding() -> bool:
@@ -26,11 +27,13 @@ def _is_utf8_encoding() -> bool:
26
27
  except BaseException:
27
28
  return False
28
29
 
30
+
29
31
  def _log_formatter(record: dict) -> str:
30
32
  """
31
33
  Log message formatter for loguru and rich.
32
34
 
33
35
  Removes Unicode icons if console encoding is not UTF-8.
36
+ Handles messages containing curly braces safely.
34
37
  """
35
38
  color_map = {
36
39
  "TRACE": "cyan",
@@ -47,21 +50,34 @@ def _log_formatter(record: dict) -> str:
47
50
  icon = record["level"].icon
48
51
  else:
49
52
  icon = record["level"].name # fallback to text
50
- # Insert color directly using f-string
51
- return f"[not bold green]{{time:HH:mm:ss}}[/not bold green] | {icon} [{lvl_color}]{record['message']}[/{lvl_color}]"
53
+ # Escape curly braces in the message to prevent format conflicts
54
+ safe_message = record["message"].replace("{", "{{").replace("}", "}}")
55
+ # Use string concatenation to avoid f-string format conflicts
56
+ time_part = "[not bold green]{time:HH:mm:ss}[/not bold green]"
57
+ message_part = f"[{lvl_color}]{safe_message}[/{lvl_color}]"
58
+ return f"{time_part} | {icon} {message_part}"
52
59
 
53
60
 
54
61
  def set_loglevel(loglevel: str) -> None:
55
62
  """
56
63
  Set the log level for the logger.
57
64
 
58
- Ensures Unicode safety for log output.
65
+ Ensures Unicode safety for log output and handles format errors.
59
66
  """
60
67
  try:
61
68
  log.remove()
62
69
  except ValueError:
63
70
  pass
64
- log.add(console.print, level=loglevel.upper(), colorize=False, format=_log_formatter) # type: ignore
71
+
72
+ # Add error handling for format issues
73
+ def safe_format_wrapper(message):
74
+ try:
75
+ console.print(message)
76
+ except (KeyError, ValueError) as e:
77
+ # Fallback to simple text output if formatting fails
78
+ console.print(f"[LOG FORMAT ERROR] {message} (Error: {e})")
79
+
80
+ log.add(safe_format_wrapper, level=loglevel.upper(), colorize=False, format=_log_formatter) # type: ignore
65
81
 
66
82
 
67
83
  def make_quiet() -> None:
@@ -2,7 +2,6 @@
2
2
  Translate board description to board designator
3
3
  """
4
4
 
5
-
6
5
  from typing import List, Optional
7
6
 
8
7
  from mpflash.db.core import Session
@@ -17,25 +16,24 @@ def find_board_id_by_description(
17
16
  short_descr: str,
18
17
  *,
19
18
  version: str,
20
- ) -> Optional[str]:
19
+ ) -> str:
21
20
  """Find the MicroPython BOARD_ID based on the description in the firmware"""
22
21
  version = clean_version(version) if version else ""
23
- try:
22
+ boards = _find_board_id_by_description(
23
+ descr=descr,
24
+ short_descr=short_descr,
25
+ version=version,
26
+ )
27
+ if not boards:
28
+ log.debug(f"Version {version} not found in board info, using any version")
24
29
  boards = _find_board_id_by_description(
25
30
  descr=descr,
26
31
  short_descr=short_descr,
27
- version=version,
32
+ version="%", # any version
28
33
  )
29
- if not boards:
30
- log.debug(f"Version {version} not found in board info, using any version")
31
- boards = _find_board_id_by_description(
32
- descr=descr,
33
- short_descr=short_descr,
34
- version="%", # any version
35
- )
36
- return boards[0].board_id if boards else None
37
- except MPFlashError:
38
- return "UNKNOWN_BOARD"
34
+ if not boards:
35
+ raise MPFlashError(f"No board info found for description '{descr}' or '{short_descr}'")
36
+ return boards[0].board_id
39
37
 
40
38
 
41
39
  def _find_board_id_by_description(
@@ -63,7 +61,5 @@ def _find_board_id_by_description(
63
61
  )
64
62
  boards = qry.all()
65
63
 
66
- if not boards:
67
- raise MPFlashError(f"No board info found for description '{descr}' or '{short_descr}'")
68
- return boards
69
64
 
65
+ return boards
@@ -64,9 +64,15 @@ def known_stored_boards(port: str, versions: List[str] = []) -> List[Tuple[str,
64
64
 
65
65
 
66
66
  def find_known_board(board_id: str, version="") -> Board:
67
- """Find the board for the given BOARD_ID or 'board description' and return the board info as a Board object"""
67
+ """
68
+ Find the board for the given BOARD_ID or 'board description'
69
+ if the board_id is not found, it will try to find it by description.
70
+
71
+ if the board_id contains an @, it will split it and use the first part as the board_id
72
+ Returns the board info as a Board object
73
+ """
68
74
  with Session() as session:
69
- qry = session.query(Board).filter(Board.board_id == board_id)
75
+ qry = session.query(Board).filter(Board.board_id == board_id.split("@")[0])
70
76
  if version:
71
77
  qry = qry.filter(Board.version == version)
72
78
  board = qry.first()