mpflash 1.26.1__py3-none-any.whl → 1.26.3__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/__init__.py CHANGED
@@ -0,0 +1,5 @@
1
+ """MPFlash - MicroPython firmware flashing tool."""
2
+
3
+ from .logger import configure_safe_logging, setup_external_logger_safety
4
+
5
+ __all__ = ["configure_safe_logging", "setup_external_logger_safety"]
mpflash/db/core.py CHANGED
@@ -14,7 +14,8 @@ from .models import Base
14
14
 
15
15
  TRACE = False
16
16
  connect_str = f"sqlite:///{config.db_path.as_posix()}"
17
- log.debug(f"Connecting to database at {connect_str}")
17
+ if TRACE:
18
+ log.debug(f"Connecting to database at {connect_str}")
18
19
  engine = create_engine(connect_str, echo=TRACE)
19
20
  Session = sessionmaker(bind=engine)
20
21
 
@@ -4,18 +4,17 @@ Flash SAMD and RP2 via UF2
4
4
 
5
5
  import shutil
6
6
  import sys
7
- from pathlib import Path
8
7
  import time
8
+ from pathlib import Path
9
9
  from typing import Optional
10
10
 
11
11
  import tenacity
12
12
  from loguru import logger as log
13
-
14
13
  from tenacity import stop_after_attempt, wait_fixed
15
14
 
15
+ from mpflash.common import PORT_FWTYPES
16
16
  from mpflash.mpremoteboard import MPRemoteBoard
17
17
 
18
- from mpflash.common import PORT_FWTYPES
19
18
  from .boardid import get_board_id
20
19
  from .linux import dismount_uf2_linux, wait_for_UF2_linux
21
20
  from .macos import wait_for_UF2_macos
@@ -38,6 +37,10 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
38
37
  if ".uf2" not in PORT_FWTYPES[mcu.port]:
39
38
  log.error(f"UF2 not supported on {mcu.board} on {mcu.serialport}")
40
39
  return None
40
+
41
+ # For non-rp2 ports, remember if we need to erase filesystem after flashing
42
+ erase_filesystem_after_flash = erase and mcu.port != "rp2"
43
+
41
44
  if erase:
42
45
  if mcu.port == "rp2":
43
46
  rp2_erase =Path(__file__).parent.joinpath("../../vendor/pico-universal-flash-nuke/universal_flash_nuke.uf2").resolve()
@@ -52,8 +55,6 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
52
55
  dismount_uf2_linux()
53
56
  # allow for MCU restart after erase
54
57
  time.sleep(0.5)
55
- else:
56
- log.warning(f"Erase not (yet) supported on .UF2, for port {mcu.port}, skipping erase.")
57
58
 
58
59
  destination = waitfor_uf2(board_id=mcu.port.upper())
59
60
 
@@ -75,6 +76,16 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
75
76
  dismount_uf2_linux()
76
77
 
77
78
  mcu.wait_for_restart()
79
+
80
+ # For non-rp2 UF2 ports (like SAMD), erase filesystem after flash and restart
81
+ if erase_filesystem_after_flash:
82
+ # allow for MCU restart after erase
83
+ time.sleep(0.5)
84
+ log.info(f"Erasing {mcu.port} filesystem using mpremote rm -r :/")
85
+ try:
86
+ rc, result = mcu.run_command(["rm", "-r", ":/"], timeout=30, resume=True)
87
+ except Exception as e:
88
+ log.warning(f"Failed to erase filesystem on {mcu.port}: {e}")
78
89
  return mcu
79
90
 
80
91
 
mpflash/logger.py CHANGED
@@ -3,6 +3,14 @@ Logger setup for CLI tools with Unicode-safe output.
3
3
 
4
4
  Ensures log messages are compatible with the current console encoding.
5
5
  Removes or replaces Unicode icons if the encoding is not UTF-8.
6
+ Prevents Loguru colorization errors with angle bracket notation.
7
+
8
+ Usage for external packages:
9
+ from mpflash.logger import setup_external_logger_safety
10
+ setup_external_logger_safety()
11
+
12
+ This is particularly important when using packages like micropython-stubber
13
+ that may log messages containing angle bracket notation like <board_default>.
6
14
  """
7
15
 
8
16
  import functools
@@ -28,12 +36,23 @@ def _is_utf8_encoding() -> bool:
28
36
  return False
29
37
 
30
38
 
31
- def _log_formatter(record: dict) -> str:
39
+ def _sanitize_message(message: str) -> str:
40
+ """
41
+ Sanitize log messages to prevent Loguru colorization issues.
42
+
43
+ Escapes angle brackets that could be interpreted as color tags.
44
+ This prevents errors when logging documentation with placeholders like <board_default>.
45
+ """
46
+ # Escape angle brackets to prevent them from being interpreted as color tags
47
+ return message.replace("<", "\\<").replace(">", "\\>")
48
+
49
+
50
+ def _log_formatter(record) -> str:
32
51
  """
33
52
  Log message formatter for loguru and rich.
34
53
 
35
54
  Removes Unicode icons if console encoding is not UTF-8.
36
- Handles messages containing curly braces safely.
55
+ Handles messages containing curly braces and angle brackets safely.
37
56
  """
38
57
  color_map = {
39
58
  "TRACE": "cyan",
@@ -50,8 +69,12 @@ def _log_formatter(record: dict) -> str:
50
69
  icon = record["level"].icon
51
70
  else:
52
71
  icon = record["level"].name # fallback to text
53
- # Escape curly braces in the message to prevent format conflicts
54
- safe_message = record["message"].replace("{", "{{").replace("}", "}}")
72
+
73
+ # Sanitize the message to prevent format conflicts and colorization errors
74
+ safe_message = _sanitize_message(record["message"])
75
+ # Escape curly braces to prevent format conflicts
76
+ safe_message = safe_message.replace("{", "{{").replace("}", "}}")
77
+
55
78
  # Use string concatenation to avoid f-string format conflicts
56
79
  time_part = "[not bold green]{time:HH:mm:ss}[/not bold green]"
57
80
  message_part = f"[{lvl_color}]{safe_message}[/{lvl_color}]"
@@ -63,6 +86,7 @@ def set_loglevel(loglevel: str) -> None:
63
86
  Set the log level for the logger.
64
87
 
65
88
  Ensures Unicode safety for log output and handles format errors.
89
+ Disables colorization to prevent angle bracket interpretation issues.
66
90
  """
67
91
  try:
68
92
  log.remove()
@@ -77,7 +101,57 @@ def set_loglevel(loglevel: str) -> None:
77
101
  # Fallback to simple text output if formatting fails
78
102
  console.print(f"[LOG FORMAT ERROR] {message} (Error: {e})")
79
103
 
80
- log.add(safe_format_wrapper, level=loglevel.upper(), colorize=False, format=_log_formatter) # type: ignore
104
+ # Disable colorization completely to prevent angle bracket interpretation as color tags
105
+ log.add(
106
+ safe_format_wrapper,
107
+ level=loglevel.upper(),
108
+ colorize=False, # This prevents Loguru from parsing angle brackets as color tags
109
+ format=_log_formatter,
110
+ ) # type: ignore
111
+
112
+
113
+ def configure_safe_logging() -> None:
114
+ """
115
+ Configure logging to be safe from colorization errors.
116
+
117
+ This function helps prevent issues when external packages
118
+ (like micropython-stubber) log messages with angle brackets
119
+ that could be misinterpreted as color tags.
120
+ """
121
+ # Remove all existing handlers to start fresh
122
+ try:
123
+ log.remove()
124
+ except ValueError:
125
+ pass
126
+
127
+ # Add a completely safe handler with no colorization
128
+ log.add(
129
+ sys.stderr,
130
+ level="TRACE",
131
+ colorize=False, # Completely disable colorization
132
+ format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {name}:{function}:{line} - {message}",
133
+ )
134
+
135
+
136
+ def setup_external_logger_safety() -> None:
137
+ """
138
+ Setup safe logging configuration for external packages.
139
+
140
+ Call this function before running tools that might log messages
141
+ with angle bracket notation (like micropython-stubber) to prevent
142
+ Loguru colorization errors.
143
+ """
144
+ import logging
145
+
146
+ # Configure the root logger to be safe
147
+ logging.basicConfig(
148
+ level=logging.DEBUG,
149
+ format="%(asctime)s | %(levelname)s | %(name)s:%(funcName)s:%(lineno)d - %(message)s",
150
+ handlers=[logging.StreamHandler(sys.stderr)],
151
+ )
152
+
153
+ # Also configure loguru for safety
154
+ configure_safe_logging()
81
155
 
82
156
 
83
157
  def make_quiet() -> None:
@@ -6,6 +6,7 @@ import subprocess
6
6
  from dataclasses import dataclass
7
7
  from threading import Timer
8
8
  from typing import List, Optional, Tuple
9
+ from unittest.mock import DEFAULT
9
10
 
10
11
  from loguru import logger as log
11
12
 
@@ -31,6 +32,13 @@ DEFAULT_RESET_TAGS = [
31
32
  "rst:0x10 (RTCWDT_RTC_RESET)",
32
33
  ]
33
34
 
35
+ DEFAULT_ERROR_TAGS = ["Traceback ", "Error: ", "Exception: ", "ERROR :", "CRIT :"]
36
+ DEFAULT_WARNING_TAGS = ["WARN :", "TRACE :"]
37
+ DEFAULT_SUCCESS_TAGS = ["Done", "File saved", "File removed", "File renamed"]
38
+ DEFAULT_IGNORE_TAGS = [
39
+ ' File "<stdin>",',
40
+ "mpremote: rm -r: cannot remove :/ Operation not permitted",
41
+ ]
34
42
 
35
43
  def run(
36
44
  cmd: List[str],
@@ -70,13 +78,13 @@ def run(
70
78
  if not reset_tags:
71
79
  reset_tags = DEFAULT_RESET_TAGS
72
80
  if not error_tags:
73
- error_tags = ["Traceback ", "Error: ", "Exception: ", "ERROR :", "CRIT :"]
81
+ error_tags = DEFAULT_ERROR_TAGS
74
82
  if not warning_tags:
75
- warning_tags = ["WARN :", "TRACE :"]
83
+ warning_tags = DEFAULT_WARNING_TAGS
76
84
  if not success_tags:
77
- success_tags = []
85
+ success_tags = DEFAULT_SUCCESS_TAGS
78
86
  if not ignore_tags:
79
- ignore_tags = [' File "<stdin>",']
87
+ ignore_tags = DEFAULT_IGNORE_TAGS
80
88
 
81
89
  replace_tags = ["\x1b[1A"]
82
90
 
@@ -132,6 +140,8 @@ def run(
132
140
  log.info(line)
133
141
  if proc.stderr and log_errors:
134
142
  for line in proc.stderr:
143
+ if any(tag in line for tag in ignore_tags):
144
+ continue
135
145
  log.warning(line)
136
146
  except UnicodeDecodeError as e:
137
147
  log.error(f"Failed to decode output: {e}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mpflash
3
- Version: 1.26.1
3
+ Version: 1.26.3
4
4
  Summary: Flash and download tool for MicroPython firmwares
5
5
  Project-URL: Homepage, https://github.com/Josverl/mpflash
6
6
  Project-URL: Documentation, https://github.com/Josverl/mpflash/blob/main/README.md
@@ -29,7 +29,7 @@ Requires-Dist: bincopy>=20.0.0
29
29
  Requires-Dist: blkinfo>=0.2.0
30
30
  Requires-Dist: cache-to-disk>=2.0.0
31
31
  Requires-Dist: cachetools>=5.3.0
32
- Requires-Dist: esptool>=4.7.0
32
+ Requires-Dist: esptool<5.0.0,>=4.7.0
33
33
  Requires-Dist: inquirer>=3.2.4
34
34
  Requires-Dist: jsonlines>=4.0.0
35
35
  Requires-Dist: jsons>=1.6.3
@@ -52,8 +52,6 @@ Provides-Extra: dev
52
52
  Requires-Dist: ipykernel>=6.29.5; extra == 'dev'
53
53
  Requires-Dist: pandas>=2.2.3; extra == 'dev'
54
54
  Requires-Dist: tornado>=6.5; extra == 'dev'
55
- Provides-Extra: perf
56
- Requires-Dist: scalene>=1.5.51; extra == 'perf'
57
55
  Provides-Extra: test
58
56
  Requires-Dist: coverage<8.0.0,>=6.4.3; extra == 'test'
59
57
  Requires-Dist: distro>=1.8.0; extra == 'test'
@@ -1,4 +1,4 @@
1
- mpflash/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1
+ mpflash/__init__.py,sha256=1CcA1kUb3uUfa9N5O4T4ZQh-Ph0JMFAtBG5IiDIaNVg,201
2
2
  mpflash/ask_input.py,sha256=YUx65Xwj6dNPwWcbQiWG7U4wDW69zEdno2HcT1KwPBg,8886
3
3
  mpflash/basicgit.py,sha256=lpGQxL10Mq8D8S56h87aMrBH0vo18ji_hE9v0KJ9P-o,10245
4
4
  mpflash/cli_add.py,sha256=hI-o-9hAGD3U8cbpXvy9Nuv1KHNTZ6mS57LC4BTBtj8,3495
@@ -13,7 +13,7 @@ mpflash/connected.py,sha256=SZvqbnLztJH-DBByjGrWT24S5DGTSevWSwYncH6dFqk,3707
13
13
  mpflash/downloaded.py,sha256=xaeMYrTIGj_v4scUBojeJPL-U1kWJG-bdvkvJMbPh4Q,4218
14
14
  mpflash/errors.py,sha256=IAidY3qkZsXy6Pm1rdmVFmGyg81ywHhse3itaPctA2w,247
15
15
  mpflash/list.py,sha256=IrJa3UBjhHHfStbb9fPVYA8JJzoFTyXtbcKGNRSH8sE,4132
16
- mpflash/logger.py,sha256=XLu4VzMhMPlCdOo2HeS4GfyatE1swwwonV8c6a-GoD4,2651
16
+ mpflash/logger.py,sha256=h1Ra-uYCVqC2evuDoIoD7GGhTHK-ymkAuqNUsPdZHcI,5212
17
17
  mpflash/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
18
  mpflash/versions.py,sha256=HuujLNdMKY_mQXyEqwXVHcU8nbuXeBiWP2TMA5JQhr4,4884
19
19
  mpflash/bootloader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -25,7 +25,7 @@ mpflash/bootloader/touch1200.py,sha256=VND7_YniS9Vx6WEaAxjI72RZZ6WBOwmBTsKJkbuaA
25
25
  mpflash/custom/__init__.py,sha256=l9RU9hRm9j7IuRgacw-gHYjA2Op-5prvRO5yyODhFMQ,5269
26
26
  mpflash/custom/naming.py,sha256=uHQzFIDzuWQUNajeGSUcf_A-o7cxX37kfgXhzpFHNtk,3304
27
27
  mpflash/db/__init__.py,sha256=wnIlO4nOXsPGXMbn2OCqHRsR-hUmtJsko8VdqjH3ZUE,45
28
- mpflash/db/core.py,sha256=6Ftp-Br3fmVO5prsvR7oc-XfF3qLJrTOy2YKosQeQQk,5271
28
+ mpflash/db/core.py,sha256=PiHpk64pBP-myfacxfVgHo4BvSSvFrtx6LrLgIPMyYY,5286
29
29
  mpflash/db/gather_boards.py,sha256=fzCT1IGZ53FwkFrTY72ZeXH5klSlm5JFETsDU7lGYf4,4134
30
30
  mpflash/db/loader.py,sha256=CDlTj2T6w9Ch9s3RHi00E1TbUhjFsgXAsYSQr5kliB0,4889
31
31
  mpflash/db/meta.py,sha256=2pFTpFH-1zejGIDp2vs0hbX5rqUONt7B1WIvf8qBx5s,2248
@@ -41,7 +41,7 @@ mpflash/flash/esp.py,sha256=4977E1hDqJ4-EIkLzwrUtgZuc0ZTD7NvP1PQZgZ2DoU,3227
41
41
  mpflash/flash/stm32.py,sha256=jNgMpJaxUwtJ-v6VU1luD1t41AQprCUeNVCVEovxQe0,595
42
42
  mpflash/flash/stm32_dfu.py,sha256=W-3JsRQyf3DduoIRXDmGZ35RogqtjQgcJnk-GOtQoLE,3090
43
43
  mpflash/flash/worklist.py,sha256=wf-R9yPsmcE54dnoPA29pEEzNPZI3JwY85V_DB0hXNI,6584
44
- mpflash/flash/uf2/__init__.py,sha256=haL84hP2p1ZjKF6dXJJHAB_NTf7jT91MuZvmvg9SpIA,3617
44
+ mpflash/flash/uf2/__init__.py,sha256=fCTQLwI8jigzGY0zVWB1XmqyieNFDRHOWky2slZjZEM,4145
45
45
  mpflash/flash/uf2/boardid.py,sha256=U5wGM8VA3wEpUxQCMtuXpMZZomdVH8J_Zd5_GekUMuU,423
46
46
  mpflash/flash/uf2/linux.py,sha256=uTgqyS7C7xfQ25RrTcSUkt-m2u2Ks_o7bPLzIecPoC8,4355
47
47
  mpflash/flash/uf2/macos.py,sha256=JTaIpqnR_0k4oSEvzs9amhmK-PMxUJyZLnZ_wZwxa-0,1228
@@ -56,7 +56,7 @@ mpflash/mpboard_id/known.py,sha256=t-oREfW5P5Zue5zbte7WB9e7-mpZBF-NfHGTEUsOVLM,3
56
56
  mpflash/mpboard_id/resolve.py,sha256=5KCZ0Tcg3FYZ3HK_zux5EguwoSC2E03kCpW2fh4rN2A,779
57
57
  mpflash/mpremoteboard/__init__.py,sha256=4OIKAry-GeYUSgnEcs5TRb0xea0bstVQCOb28MjLDyk,14210
58
58
  mpflash/mpremoteboard/mpy_fw_info.py,sha256=ZDEPJN9XJnoG_oeWcLNiLJAD5bkVX2yI_j4K7msUxWM,5196
59
- mpflash/mpremoteboard/runner.py,sha256=H5f5JBcRnpF06Ui3xDY7IoDdO2Hex9lsI_eGMq5c2A4,4945
59
+ mpflash/mpremoteboard/runner.py,sha256=4YpL0OA3GRcCvXI47wrj6hJKJZ9uunkdDKDFic2ZM-8,5372
60
60
  mpflash/vendor/board_database.py,sha256=Cb8fEhJaZ2siMkLPW5rPwV9yzBsTtKGOqWUd9TxNgFM,8763
61
61
  mpflash/vendor/click_aliases.py,sha256=adLhqLxNpJEPjSCIRSTkR-QzSgavGFKT0cwRbjxpzRU,5395
62
62
  mpflash/vendor/dfu.py,sha256=6rqGCBS8mTxxaLtkdzJ8O6nc74kFk8jrkmKvxw-x-u8,5693
@@ -64,8 +64,8 @@ mpflash/vendor/pydfu.py,sha256=KD1RHHuhvhWi-l1UB6GyggkxouDKtZgkG4ivRbIfwC4,21264
64
64
  mpflash/vendor/readme.md,sha256=BQ7Uxf8joeYMjTUuSLLBG49ob6a9MgFPIEwuc72-Mfw,415
65
65
  mpflash/vendor/pico-universal-flash-nuke/LICENSE.txt,sha256=Zkc2iTNbib2NCMwtLjMEz0vFCPglgvaw6Mj7QiWldpQ,1484
66
66
  mpflash/vendor/pico-universal-flash-nuke/universal_flash_nuke.uf2,sha256=QuPMppqHMVOt3vDVU0bikHRLsTiDRQYNUcGQ_OLRFGI,28160
67
- mpflash-1.26.1.dist-info/METADATA,sha256=mB9GYaFlwmoK2qp0FNqxsjW4WHc14k2F8RRj848AisU,28095
68
- mpflash-1.26.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
69
- mpflash-1.26.1.dist-info/entry_points.txt,sha256=DZ24tsMKlCyTkjWet9vCoq5dcFeY43RKtTsLreQI_R8,53
70
- mpflash-1.26.1.dist-info/licenses/LICENSE,sha256=mWpNhsIxWzetYNnTpr4eb3HtgsxGIC8KcYWxXEcxQvE,1077
71
- mpflash-1.26.1.dist-info/RECORD,,
67
+ mpflash-1.26.3.dist-info/METADATA,sha256=T_PJNISbbh6QLopvV6CosjyXnO94zDJqjibo46havDM,28033
68
+ mpflash-1.26.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
69
+ mpflash-1.26.3.dist-info/entry_points.txt,sha256=DZ24tsMKlCyTkjWet9vCoq5dcFeY43RKtTsLreQI_R8,53
70
+ mpflash-1.26.3.dist-info/licenses/LICENSE,sha256=mWpNhsIxWzetYNnTpr4eb3HtgsxGIC8KcYWxXEcxQvE,1077
71
+ mpflash-1.26.3.dist-info/RECORD,,