micropython-stubber 1.23.0__py3-none-any.whl → 1.23.1.post1__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 (110) hide show
  1. {micropython_stubber-1.23.0.dist-info → micropython_stubber-1.23.1.post1.dist-info}/METADATA +32 -14
  2. micropython_stubber-1.23.1.post1.dist-info/RECORD +159 -0
  3. micropython_stubber-1.23.1.post1.dist-info/entry_points.txt +5 -0
  4. mpflash/README.md +40 -4
  5. mpflash/mpflash/add_firmware.py +1 -1
  6. mpflash/mpflash/ask_input.py +1 -1
  7. {stubber → mpflash/mpflash}/basicgit.py +3 -13
  8. mpflash/mpflash/bootloader/__init__.py +2 -37
  9. mpflash/mpflash/bootloader/activate.py +60 -0
  10. mpflash/mpflash/bootloader/detect.py +82 -0
  11. mpflash/mpflash/bootloader/manual.py +10 -11
  12. mpflash/mpflash/bootloader/micropython.py +2 -0
  13. mpflash/mpflash/bootloader/touch1200.py +13 -22
  14. mpflash/mpflash/cli_download.py +2 -2
  15. mpflash/mpflash/cli_flash.py +13 -16
  16. mpflash/mpflash/cli_group.py +18 -5
  17. mpflash/mpflash/cli_list.py +8 -2
  18. mpflash/mpflash/cli_main.py +3 -5
  19. mpflash/mpflash/common.py +3 -1
  20. mpflash/mpflash/config.py +2 -1
  21. mpflash/mpflash/connected.py +11 -8
  22. mpflash/mpflash/download.py +9 -5
  23. mpflash/mpflash/downloaded.py +1 -1
  24. mpflash/mpflash/{flash.py → flash/__init__.py} +3 -3
  25. mpflash/mpflash/{flash_esp.py → flash/esp.py} +1 -1
  26. mpflash/mpflash/{flash_stm32.py → flash/stm32.py} +4 -3
  27. mpflash/mpflash/{flash_stm32_dfu.py → flash/stm32_dfu.py} +1 -1
  28. mpflash/mpflash/{flash_uf2.py → flash/uf2/__init__.py} +19 -20
  29. mpflash/mpflash/{flash_uf2_linux.py → flash/uf2/linux.py} +12 -11
  30. mpflash/mpflash/{flash_uf2_macos.py → flash/uf2/macos.py} +11 -6
  31. mpflash/mpflash/{flash_uf2_windows.py → flash/uf2/windows.py} +11 -6
  32. mpflash/mpflash/{worklist.py → flash/worklist.py} +8 -9
  33. mpflash/mpflash/list.py +26 -9
  34. mpflash/mpflash/mpboard_id/__init__.py +1 -1
  35. mpflash/mpflash/mpboard_id/add_boards.py +3 -7
  36. mpflash/mpflash/mpboard_id/board_id.py +1 -1
  37. mpflash/mpflash/mpremoteboard/__init__.py +57 -17
  38. {stubber/utils → mpflash/mpflash}/versions.py +31 -24
  39. mpflash/poetry.lock +16 -5
  40. mpflash/pyproject.toml +4 -3
  41. stubber/__init__.py +1 -1
  42. stubber/board/createstubs.py +4 -4
  43. stubber/board/createstubs_db.py +5 -5
  44. stubber/board/createstubs_db_min.py +1 -1
  45. stubber/board/createstubs_db_mpy.mpy +0 -0
  46. stubber/board/createstubs_mem.py +5 -5
  47. stubber/board/createstubs_mem_min.py +1 -1
  48. stubber/board/createstubs_mem_mpy.mpy +0 -0
  49. stubber/board/createstubs_min.py +1 -1
  50. stubber/board/createstubs_mpy.mpy +0 -0
  51. stubber/bulk/mcu_stubber.py +28 -45
  52. stubber/codemod/enrich.py +1 -1
  53. stubber/codemod/merge_docstub.py +1 -1
  54. stubber/codemod/utils.py +2 -3
  55. stubber/commands/build_cmd.py +1 -1
  56. stubber/commands/cli.py +6 -12
  57. stubber/commands/clone_cmd.py +3 -2
  58. stubber/commands/config_cmd.py +1 -1
  59. stubber/commands/enrich_folder_cmd.py +1 -1
  60. stubber/commands/get_core_cmd.py +1 -1
  61. stubber/commands/get_docstubs_cmd.py +6 -3
  62. stubber/commands/get_frozen_cmd.py +6 -3
  63. stubber/commands/get_mcu_cmd.py +53 -12
  64. stubber/commands/merge_cmd.py +2 -3
  65. stubber/commands/publish_cmd.py +2 -3
  66. stubber/commands/stub_cmd.py +1 -1
  67. stubber/commands/switch_cmd.py +2 -2
  68. stubber/commands/variants_cmd.py +2 -3
  69. stubber/downloader.py +2 -1
  70. stubber/freeze/common.py +7 -3
  71. stubber/freeze/freeze_folder.py +2 -2
  72. stubber/freeze/freeze_manifest_2.py +19 -6
  73. stubber/freeze/get_frozen.py +8 -4
  74. stubber/get_cpython.py +15 -4
  75. stubber/minify.py +11 -6
  76. stubber/publish/candidates.py +20 -7
  77. stubber/publish/defaults.py +4 -9
  78. stubber/publish/merge_docstubs.py +4 -2
  79. stubber/publish/missing_class_methods.py +5 -3
  80. stubber/publish/package.py +8 -4
  81. stubber/publish/pathnames.py +2 -2
  82. stubber/publish/publish.py +2 -2
  83. stubber/publish/pypi.py +6 -2
  84. stubber/publish/stubpackage.py +39 -17
  85. stubber/rst/classsort.py +2 -1
  86. stubber/rst/lookup.py +1 -0
  87. stubber/rst/reader.py +12 -20
  88. stubber/rst/report_return.py +12 -4
  89. stubber/rst/rst_utils.py +2 -1
  90. stubber/stubs_from_docs.py +1 -1
  91. stubber/tools/manifestfile.py +1 -2
  92. stubber/update_fallback.py +2 -2
  93. stubber/update_module_list.py +1 -1
  94. stubber/utils/__init__.py +2 -1
  95. stubber/utils/config.py +18 -8
  96. stubber/utils/manifest.py +2 -4
  97. stubber/utils/post.py +2 -1
  98. stubber/utils/repos.py +4 -5
  99. stubber/utils/stubmaker.py +1 -1
  100. stubber/utils/typed_config_toml.py +5 -2
  101. stubber/variants.py +1 -1
  102. micropython_stubber-1.23.0.dist-info/RECORD +0 -159
  103. micropython_stubber-1.23.0.dist-info/entry_points.txt +0 -3
  104. mpflash/mpflash/flash_stm32_cube.py +0 -111
  105. mpflash/mpflash/vendor/versions.py +0 -119
  106. {micropython_stubber-1.23.0.dist-info → micropython_stubber-1.23.1.post1.dist-info}/LICENSE +0 -0
  107. {micropython_stubber-1.23.0.dist-info → micropython_stubber-1.23.1.post1.dist-info}/WHEEL +0 -0
  108. /mpflash/{mpflash/vendor/basicgit.py → basicgit.py} +0 -0
  109. /mpflash/mpflash/{flash_uf2_boardid.py → flash/uf2/boardid.py} +0 -0
  110. /mpflash/mpflash/{uf2disk.py → flash/uf2/uf2disk.py} +0 -0
@@ -12,7 +12,7 @@ from typing import List
12
12
  from loguru import logger as log
13
13
  from rich.progress import track
14
14
 
15
- from .flash_uf2_boardid import get_board_id
15
+ from .boardid import get_board_id
16
16
  from .uf2disk import UF2Disk
17
17
 
18
18
  glb_dismount_me: List[UF2Disk] = []
@@ -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()
@@ -98,16 +95,17 @@ def dismount_uf2_linux():
98
95
  glb_dismount_me = []
99
96
 
100
97
 
101
- def wait_for_UF2_linux(s_max: int = 10):
98
+ def wait_for_UF2_linux(board_id: str, s_max: int = 10):
102
99
  destination = ""
103
100
  wait = 10
104
101
  uf2_drives = []
105
102
  # while not destination and wait > 0:
106
103
  for _ in track(
107
104
  range(s_max),
108
- description="Waiting for mcu to mount as a drive",
105
+ description=f"Waiting for mcu to mount as a drive ({s_max}s)",
109
106
  transient=True,
110
- refresh_per_second=2,
107
+ show_speed=False,
108
+ refresh_per_second=1,
111
109
  total=s_max,
112
110
  ):
113
111
  uf2_drives += list(get_uf2_drives())
@@ -116,9 +114,12 @@ def wait_for_UF2_linux(s_max: int = 10):
116
114
  time.sleep(1)
117
115
  try:
118
116
  if Path(drive.mountpoint, "INFO_UF2.TXT").exists():
119
- board_id = get_board_id(Path(drive.mountpoint)) # type: ignore
120
- destination = Path(drive.mountpoint)
121
- break
117
+ this_board_id = get_board_id(Path(drive.mountpoint))
118
+ if not board_id or board_id.upper() in this_board_id.upper():
119
+ # is it the correct board?
120
+ destination = Path(drive.mountpoint)
121
+ break
122
+ continue
122
123
  except PermissionError:
123
124
  log.debug(f"Permission error on {drive.mountpoint}")
124
125
  continue
@@ -9,26 +9,31 @@ from typing import Optional
9
9
 
10
10
  from rich.progress import track
11
11
 
12
+ from .boardid import get_board_id
12
13
 
13
- def wait_for_UF2_macos(s_max: int = 10) -> Optional[Path]:
14
+
15
+ def wait_for_UF2_macos(board_id: str, s_max: int = 10) -> Optional[Path]:
14
16
  """Wait for the MCU to mount as a drive"""
15
17
  if s_max < 1:
16
18
  s_max = 10
17
19
  destination = None
18
20
  for _ in track(
19
21
  range(s_max),
20
- description="Waiting for mcu to mount as a drive",
22
+ description=f"Waiting for mcu to mount as a drive ({s_max}s)",
21
23
  transient=True,
22
- refresh_per_second=2,
24
+ show_speed=False,
25
+ refresh_per_second=1,
23
26
  total=s_max,
24
27
  ):
25
- # log.info(f"Waiting for mcu to mount as a drive : {n} seconds left")
26
28
  vol_mounts = Path("/Volumes").iterdir()
27
29
  for vol in vol_mounts:
28
30
  try:
29
31
  if Path(vol, "INFO_UF2.TXT").exists():
30
- destination = Path(vol)
31
- break
32
+ this_board_id = get_board_id(Path(vol))
33
+ if not board_id or board_id.upper() in this_board_id.upper():
34
+ destination = Path(vol)
35
+ break
36
+ continue
32
37
  except OSError:
33
38
  pass
34
39
  if destination:
@@ -7,29 +7,34 @@ import time
7
7
  from pathlib import Path
8
8
  from typing import Optional
9
9
 
10
+ from .boardid import get_board_id
10
11
  import psutil
11
12
  from rich.progress import track
12
13
 
13
14
 
14
- def wait_for_UF2_windows(s_max: int = 10) -> Optional[Path]:
15
+ def wait_for_UF2_windows(board_id: str, s_max: int = 10)-> Optional[Path]:
15
16
  """Wait for the MCU to mount as a drive"""
16
17
  if s_max < 1:
17
18
  s_max = 10
18
19
  destination = None
19
20
  for _ in track(
20
21
  range(s_max),
21
- description="Waiting for mcu to mount as a drive",
22
+ description=f"Waiting for mcu to mount as a drive ({s_max}s)",
22
23
  transient=True,
23
- refresh_per_second=2,
24
+ show_speed=False,
25
+ refresh_per_second=1,
24
26
  total=s_max,
25
27
  ):
26
- # log.info(f"Waiting for mcu to mount as a drive : {n} seconds left")
27
28
  drives = [drive.device for drive in psutil.disk_partitions()]
28
29
  for drive in drives:
29
30
  try:
30
31
  if Path(drive, "INFO_UF2.TXT").exists():
31
- destination = Path(drive)
32
- break
32
+ this_board_id = get_board_id(Path(drive))
33
+ if not board_id or board_id.upper() in this_board_id.upper():
34
+ # is it the correct board?
35
+ destination = Path(drive)
36
+ break
37
+ continue
33
38
  except OSError:
34
39
  pass
35
40
  if destination:
@@ -1,15 +1,16 @@
1
+ """Worklist for updating boards"""
2
+
1
3
  from pathlib import Path
2
4
  from typing import Dict, List, Optional, Tuple
3
5
 
4
6
  from loguru import logger as log
5
7
 
6
- from mpflash.common import FWInfo, filtered_comports, PORT_FWTYPES
8
+ from mpflash.common import FWInfo, filtered_comports
9
+ from mpflash.downloaded import find_downloaded_firmware
7
10
  from mpflash.errors import MPFlashError
8
-
9
- from .downloaded import find_downloaded_firmware
10
- from .list import show_mcus
11
- from .mpboard_id import find_known_board
12
- from .mpremoteboard import MPRemoteBoard
11
+ from mpflash.list import show_mcus
12
+ from mpflash.mpboard_id import find_known_board
13
+ from mpflash.mpremoteboard import MPRemoteBoard
13
14
 
14
15
  # #########################################################################################################
15
16
  WorkList = List[Tuple[MPRemoteBoard, FWInfo]]
@@ -39,9 +40,7 @@ def auto_update(
39
40
  wl: WorkList = []
40
41
  for mcu in conn_boards:
41
42
  if mcu.family not in ("micropython", "unknown"):
42
- log.warning(
43
- f"Skipping flashing {mcu.family} {mcu.port} {mcu.board} on {mcu.serialport} as it is not a MicroPython firmware"
44
- )
43
+ log.warning(f"Skipping flashing {mcu.family} {mcu.port} {mcu.board} on {mcu.serialport} as it is not a MicroPython firmware")
45
44
  continue
46
45
  board_firmwares = find_downloaded_firmware(
47
46
  fw_folder=fw_folder,
mpflash/mpflash/list.py CHANGED
@@ -3,8 +3,9 @@ from typing import List
3
3
  from rich.progress import track
4
4
  from rich.table import Table
5
5
 
6
+ from mpflash.config import config
6
7
  from mpflash.mpremoteboard import MPRemoteBoard
7
- from mpflash.vendor.versions import clean_version
8
+ from mpflash.versions import clean_version
8
9
 
9
10
  from .logger import console
10
11
 
@@ -37,11 +38,25 @@ def mcu_table(
37
38
  - Serial Yes Yes
38
39
  - Family abbrv. Yes
39
40
  - Port - yes
40
- - Board Yes Yes BOARD_ID and Description
41
+ - Board Yes Yes BOARD_ID and Description, and the description from board_info.toml
41
42
  - CPU - Yes
42
43
  - Version Yes Yes
43
44
  - Build * * only if any of the mcus have a build
45
+ - Location - - only if --usb is given
44
46
  """
47
+ # refresh if requested
48
+ if refresh:
49
+ for mcu in track(
50
+ conn_mcus,
51
+ description="Updating board info",
52
+ transient=True,
53
+ show_speed=False,
54
+ refresh_per_second=1,
55
+ ):
56
+ try:
57
+ mcu.get_mcu_info()
58
+ except ConnectionError:
59
+ continue
45
60
  table = Table(
46
61
  title=title,
47
62
  title_style="magenta",
@@ -49,6 +64,7 @@ def mcu_table(
49
64
  collapse_padding=True,
50
65
  padding=(0, 0),
51
66
  )
67
+ # Build the table
52
68
  # check if the terminal is wide enough to show all columns or if we need to collapse some
53
69
  is_wide = console.width > 99
54
70
  needs_build = any(mcu.build for mcu in conn_mcus)
@@ -64,14 +80,13 @@ def mcu_table(
64
80
  table.add_column("Version", overflow="fold", min_width=5, max_width=16)
65
81
  if needs_build:
66
82
  table.add_column("Build" if is_wide else "Bld", justify="right")
67
-
68
- for mcu in track(conn_mcus, description="Updating board info", transient=True, refresh_per_second=2):
69
- if refresh:
70
- try:
71
- mcu.get_mcu_info()
72
- except ConnectionError:
73
- continue
83
+ if config.usb:
84
+ table.add_column("Location", overflow="fold", max_width=40)
85
+ # fill the table with the data
86
+ for mcu in conn_mcus:
74
87
  description = f"[italic bright_cyan]{mcu.description}" if mcu.description else ""
88
+ if "description" in mcu.toml:
89
+ description += f"\n[italic bright_green]{mcu.toml['description']}"
75
90
  row = [
76
91
  mcu.serialport.replace("/dev/", ""),
77
92
  abbrv_family(mcu.family, is_wide),
@@ -84,6 +99,8 @@ def mcu_table(
84
99
  row.append(clean_version(mcu.version))
85
100
  if needs_build:
86
101
  row.append(mcu.build)
102
+ if config.usb:
103
+ row.append(mcu.location)
87
104
 
88
105
  table.add_row(*row)
89
106
  return table
@@ -10,7 +10,7 @@ from typing import List, Optional, Tuple
10
10
  from mpflash.errors import MPFlashError
11
11
  from mpflash.mpboard_id.board import Board
12
12
  from mpflash.mpboard_id.store import read_known_boardinfo
13
- from mpflash.vendor.versions import clean_version
13
+ from mpflash.versions import clean_version
14
14
 
15
15
  # KNOWN ports and boards are sourced from the micropython repo,
16
16
  # this info is stored in the board_info.json file
@@ -12,11 +12,11 @@ import rich.table
12
12
  from rich.console import Console
13
13
  from rich.progress import track
14
14
 
15
- import mpflash.vendor.basicgit as git
15
+ 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.vendor.versions import micropython_versions
19
+ from mpflash.versions import micropython_versions
20
20
 
21
21
  # look for all mpconfigboard.h files and extract the board name
22
22
  # from the #define MICROPY_HW_BOARD_NAME "PYBD_SF6"
@@ -222,11 +222,7 @@ def make_table(board_list: List[Board]) -> rich.table.Table:
222
222
 
223
223
  def ask_mpy_path():
224
224
  """Ask the user for the path to the MicroPython repository."""
225
- questions = [
226
- inquirer.Text(
227
- "mpy_path", message="Enter the path to the MicroPython repository", default=".\\repos\\micropython"
228
- )
229
- ]
225
+ questions = [inquirer.Text("mpy_path", message="Enter the path to the MicroPython repository", default=".\\repos\\micropython")]
230
226
  if answers := inquirer.prompt(questions):
231
227
  return Path(answers["mpy_path"])
232
228
  else:
@@ -9,7 +9,7 @@ from typing import Optional
9
9
  from mpflash.errors import MPFlashError
10
10
  from mpflash.logger import log
11
11
  from mpflash.mpboard_id.store import read_known_boardinfo
12
- from mpflash.vendor.versions import clean_version, get_stable_mp_version
12
+ from mpflash.versions import clean_version, get_stable_mp_version
13
13
 
14
14
 
15
15
  def find_board_id_by_description(
@@ -8,16 +8,20 @@ from pathlib import Path
8
8
  from typing import List, Optional, Union
9
9
 
10
10
  import serial.tools.list_ports
11
- from loguru import logger as log
12
11
  from rich.progress import track
13
12
  from tenacity import retry, stop_after_attempt, wait_fixed
14
13
 
15
14
  from mpflash.errors import MPFlashError
15
+ from mpflash.logger import log
16
16
  from mpflash.mpboard_id.board_id import find_board_id_by_description
17
17
  from mpflash.mpremoteboard.runner import run
18
18
 
19
+ if sys.version_info >= (3, 11):
20
+ import tomllib # type: ignore
21
+ else:
22
+ import tomli as tomllib # type: ignore
23
+
19
24
  ###############################################################################################
20
- # TODO : make this a bit nicer
21
25
  HERE = Path(__file__).parent
22
26
 
23
27
  OK = 0
@@ -29,7 +33,7 @@ RETRIES = 3
29
33
  class MPRemoteBoard:
30
34
  """Class to run mpremote commands"""
31
35
 
32
- def __init__(self, serialport: str = "", update: bool = False):
36
+ def __init__(self, serialport: str = "", update: bool = False, *, location: str = ""):
33
37
  """
34
38
  Initialize MPRemoteBoard object.
35
39
 
@@ -37,7 +41,7 @@ class MPRemoteBoard:
37
41
  - serialport (str): The serial port to connect to. Default is an empty string.
38
42
  - update (bool): Whether to update the MCU information. Default is False.
39
43
  """
40
- self.serialport = serialport
44
+ self.serialport: str = serialport
41
45
  self.firmware = {}
42
46
 
43
47
  self.connected = False
@@ -51,6 +55,8 @@ class MPRemoteBoard:
51
55
  self.arch = ""
52
56
  self.mpy = ""
53
57
  self.build = ""
58
+ self.location = location
59
+ self.toml = {}
54
60
  if update:
55
61
  self.get_mcu_info()
56
62
 
@@ -91,9 +97,7 @@ class MPRemoteBoard:
91
97
 
92
98
  if sys.platform == "win32":
93
99
  # Windows sort of comports by number - but fallback to device name
94
- return sorted(
95
- output, key=lambda x: int(x.split()[0][3:]) if x.split()[0][3:].isdigit() else x
96
- )
100
+ return sorted(output, key=lambda x: int(x.split()[0][3:]) if x.split()[0][3:].isdigit() else x)
97
101
  # sort by device name
98
102
  return sorted(output)
99
103
 
@@ -112,13 +116,14 @@ class MPRemoteBoard:
112
116
  ["run", str(HERE / "mpy_fw_info.py")],
113
117
  no_info=True,
114
118
  timeout=timeout,
119
+ resume=True, # Avoid restarts
115
120
  )
116
121
  if rc != OK:
117
122
  raise ConnectionError(f"Failed to get mcu_info for {self.serialport}")
118
123
  # Ok we have the info, now parse it
119
- s = result[0].strip()
120
- if s.startswith("{") and s.endswith("}"):
121
- info = eval(s)
124
+ raw_info = result[0].strip()
125
+ if raw_info.startswith("{") and raw_info.endswith("}"):
126
+ info = eval(raw_info)
122
127
  self.family = info["family"]
123
128
  self.version = info["version"]
124
129
  self.build = info["build"]
@@ -129,12 +134,47 @@ class MPRemoteBoard:
129
134
  self.description = descr = info["board"]
130
135
  pos = descr.rfind(" with")
131
136
  short_descr = descr[:pos].strip() if pos != -1 else ""
132
- if board_name := find_board_id_by_description(
133
- descr, short_descr, version=self.version
134
- ):
137
+ if board_name := find_board_id_by_description(descr, short_descr, version=self.version):
135
138
  self.board = board_name
136
139
  else:
137
140
  self.board = "UNKNOWN_BOARD"
141
+ # get the board_info.toml
142
+ self.get_board_info_toml()
143
+ # now we know the board is connected
144
+ self.connected = True
145
+
146
+ @retry(stop=stop_after_attempt(RETRIES), wait=wait_fixed(0.2), reraise=True) # type: ignore ## retry_error_cls=ConnectionError,
147
+ def get_board_info_toml(self, timeout: int = 1):
148
+ """
149
+ Reads the content of the board_info.toml file from the connected board,
150
+ and adds that to the board object.
151
+
152
+ Parameters:
153
+ - timeout (int): The timeout value in seconds.
154
+
155
+ Raises:
156
+ - ConnectionError: If failed to communicate with the serial port.
157
+ """
158
+ try:
159
+ rc, result = self.run_command(
160
+ ["cat", ":board_info.toml"],
161
+ no_info=True,
162
+ timeout=timeout,
163
+ log_errors=False,
164
+ )
165
+ except Exception as e:
166
+ raise ConnectionError(f"Failed to get board_info.toml for {self.serialport}: {e}")
167
+ # this is optional - so only parse if we got the file
168
+ self.toml = {}
169
+ if rc in [OK]: # sometimes we get an -9 ???
170
+ try:
171
+ # Ok we have the info, now parse it
172
+ self.toml = tomllib.loads("".join(result))
173
+ log.debug(f"board_info.toml: {self.toml}")
174
+ except Exception as e:
175
+ log.error(f"Failed to parse board_info.toml: {e}")
176
+ else:
177
+ log.trace(f"Failed to read board_info.toml: {result}")
138
178
 
139
179
  def disconnect(self) -> bool:
140
180
  """
@@ -162,6 +202,7 @@ class MPRemoteBoard:
162
202
  log_errors: bool = True,
163
203
  no_info: bool = False,
164
204
  timeout: int = 60,
205
+ resume: bool = False,
165
206
  **kwargs,
166
207
  ):
167
208
  """
@@ -182,7 +223,7 @@ class MPRemoteBoard:
182
223
  if self.serialport:
183
224
  prefix += ["connect", self.serialport]
184
225
  # if connected add resume to keep state between commands
185
- if self.connected:
226
+ if self.connected or resume:
186
227
  prefix += ["resume"]
187
228
  cmd = prefix + cmd
188
229
  log.debug(" ".join(cmd))
@@ -211,11 +252,10 @@ class MPRemoteBoard:
211
252
  """wait for the board to restart"""
212
253
  for _ in track(
213
254
  range(timeout),
214
- description="Waiting for the board to restart",
255
+ description=f"Waiting for the board to restart ({timeout}s)",
215
256
  transient=True,
216
- get_time=lambda: time.time(),
217
257
  show_speed=False,
218
- refresh_per_second=2,
258
+ refresh_per_second=1,
219
259
  total=timeout,
220
260
  ):
221
261
  time.sleep(1)
@@ -1,14 +1,17 @@
1
- """Handle versions of micropython based on the git tags in the repo """
1
+ """
2
+ #############################################################
3
+ # Version handling copied from stubber/utils/versions.py
4
+ #############################################################
5
+ """
2
6
 
3
- from functools import lru_cache
4
7
  from pathlib import Path
5
8
 
6
- from github import Github
9
+ from cache_to_disk import NoCacheCondition, cache_to_disk
7
10
  from loguru import logger as log
8
11
  from packaging.version import parse
9
12
 
10
- import stubber.basicgit as git
11
- import stubber.utils as utils
13
+ import mpflash.basicgit as git
14
+ from mpflash.common import GH_CLIENT
12
15
 
13
16
  OLDEST_VERSION = "1.16"
14
17
  "This is the oldest MicroPython version to build the stubs on"
@@ -27,7 +30,7 @@ def clean_version(
27
30
  commit: bool = False,
28
31
  drop_v: bool = False,
29
32
  flat: bool = False,
30
- ):
33
+ ): # sourcery skip: assign-if-exp
31
34
  "Clean up and transform the many flavours of versions"
32
35
  # 'v1.13.0-103-gb137d064e' --> 'v1.13-103'
33
36
  if version in {"", "-"}:
@@ -48,14 +51,12 @@ def clean_version(
48
51
  if len(nibbles) == 1:
49
52
  version = nibbles[0]
50
53
  elif build and not is_preview:
54
+ # HACK: this is not always right, but good enough most of the time
51
55
  version = "-".join(nibbles) if commit else "-".join(nibbles[:-1])
56
+ elif is_preview:
57
+ version = "-".join((nibbles[0], V_PREVIEW))
52
58
  else:
53
- # version = "-".join((nibbles[0], LATEST))
54
- # HACK: this is not always right, but good enough most of the time
55
- if is_preview:
56
- version = "-".join((nibbles[0], V_PREVIEW))
57
- else:
58
- version = V_PREVIEW
59
+ version = V_PREVIEW
59
60
  if flat:
60
61
  version = version.strip().replace(".", "_").replace("-", "_")
61
62
  else:
@@ -70,20 +71,12 @@ def clean_version(
70
71
  return version
71
72
 
72
73
 
73
- @lru_cache(maxsize=10)
74
- def checkedout_version(path: Path, flat: bool = False) -> str:
75
- """Get the checked-out version of the repo"""
76
- version = git.get_local_tag(path.as_posix())
77
- if not version:
78
- raise ValueError("No valid Tag found")
79
- version = utils.clean_version(version, flat=flat, drop_v=False)
80
- return version
81
-
82
-
74
+ @cache_to_disk(n_days_to_cache=1)
83
75
  def micropython_versions(minver: str = "v1.20", reverse: bool = False):
84
76
  """Get the list of micropython versions from github tags"""
77
+ cache_it = True
85
78
  try:
86
- gh_client = Github()
79
+ gh_client = GH_CLIENT
87
80
  repo = gh_client.get_repo("micropython/micropython")
88
81
  versions = [tag.name for tag in repo.get_tags() if parse(tag.name) >= parse(minver)]
89
82
  # Only keep the last preview
@@ -109,10 +102,15 @@ def micropython_versions(minver: str = "v1.20", reverse: bool = False):
109
102
  "v1.11",
110
103
  "v1.10",
111
104
  ]
105
+ cache_it = False
112
106
  versions = [v for v in versions if parse(v) >= parse(minver)]
113
107
  # remove all but the most recent (preview) version
114
108
  versions = versions[:1] + [v for v in versions if "preview" not in v]
115
- return sorted(versions, reverse=reverse)
109
+ versions = sorted(versions, reverse=reverse)
110
+ if cache_it:
111
+ return versions
112
+ # returns - but does not cache
113
+ raise NoCacheCondition(function_value=versions)
116
114
 
117
115
 
118
116
  def get_stable_mp_version() -> str:
@@ -126,3 +124,12 @@ def get_preview_mp_version() -> str:
126
124
  all_versions = micropython_versions(minver=OLDEST_VERSION)
127
125
  return [v for v in all_versions if v.endswith(V_PREVIEW)][-1]
128
126
 
127
+
128
+ # Do not cache , same path will have different versions checked out
129
+ def checkedout_version(path: Path, flat: bool = False) -> str:
130
+ """Get the checked-out version of the repo"""
131
+ version = git.get_local_tag(path.as_posix())
132
+ if not version:
133
+ raise ValueError("No valid Tag found")
134
+ version = clean_version(version, flat=flat, drop_v=False)
135
+ return version
mpflash/poetry.lock CHANGED
@@ -1,4 +1,4 @@
1
- # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
1
+ # This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
2
2
 
3
3
  [[package]]
4
4
  name = "ansicon"
@@ -249,6 +249,17 @@ files = [
249
249
  {file = "blkinfo-0.2.0.tar.gz", hash = "sha256:322a906595f78832d6725ac74a0b9fd2794df3388584d9f05c1a2f8e19324851"},
250
250
  ]
251
251
 
252
+ [[package]]
253
+ name = "cache-to-disk"
254
+ version = "2.0.0"
255
+ description = "Local disk caching decorator for python function."
256
+ optional = false
257
+ python-versions = "*"
258
+ files = [
259
+ {file = "cache_to_disk-2.0.0-py3-none-any.whl", hash = "sha256:ea5afe13d4284cb4a06169b0807fbc60547cbe19c54563bf90e1d44f24029481"},
260
+ {file = "cache_to_disk-2.0.0.tar.gz", hash = "sha256:79e19ea9b72eedc5cec83bb8aa55374afc671493e7d13d541f3b63eb3a13fb32"},
261
+ ]
262
+
252
263
  [[package]]
253
264
  name = "cachetools"
254
265
  version = "5.3.3"
@@ -1452,13 +1463,13 @@ test = ["codecov", "coverage", "mypy", "nptyping (>=1.3.0)", "numpy", "pycodesty
1452
1463
 
1453
1464
  [[package]]
1454
1465
  name = "urllib3"
1455
- version = "2.2.1"
1466
+ version = "2.2.2"
1456
1467
  description = "HTTP library with thread-safe connection pooling, file post, and more."
1457
1468
  optional = false
1458
1469
  python-versions = ">=3.8"
1459
1470
  files = [
1460
- {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
1461
- {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
1471
+ {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
1472
+ {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
1462
1473
  ]
1463
1474
 
1464
1475
  [package.extras]
@@ -1585,4 +1596,4 @@ files = [
1585
1596
  [metadata]
1586
1597
  lock-version = "2.0"
1587
1598
  python-versions = ">=3.8.1,<4.0"
1588
- content-hash = "99cc10bd15b033a47635e31676205b38443b15697fb189b01a8eb7f2f175acf8"
1599
+ content-hash = "e0f5697e08c8c691e538c586c7a478cdc93de23d56435373b56abdd2147bda87"
mpflash/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "mpflash"
3
- version = "0.9.0"
3
+ version = "1.0.0"
4
4
  description = "Flash and download tool for MicroPython firmwares"
5
5
  authors = ["Jos Verlinde <jos_verlinde@hotmail.com>"]
6
6
  license = "MIT"
@@ -26,18 +26,19 @@ esptool = "^4.7.0"
26
26
  inquirer = "^3.2.4"
27
27
  jsonlines = "^4.0.0"
28
28
  jsons = "^1.6.3"
29
- libusb = {version = "^1.0.27", platform = "win32"}
29
+ libusb = { version = "^1.0.27", platform = "win32" }
30
30
  loguru = "^0.7.2"
31
31
  mpremote = "^1.22.0"
32
32
  packaging = "23.2"
33
33
  platformdirs = "^4.2.0"
34
34
  psutil = "^5.9.8"
35
35
  pygithub = "^2.1.1"
36
- python = ">=3.8.1,<4.0"
36
+ python = ">=3.8.1,<4.0"
37
37
  pyusb = "^1.2.1"
38
38
  requests = "^2.31.0"
39
39
  rich-click = "^1.8.1"
40
40
  tenacity = "8.2.3"
41
+ cache-to-disk = "^2.0.0"
41
42
 
42
43
 
43
44
  [tool.poetry.group.dev]
stubber/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """get the version"""
2
2
 
3
- __version__ = "1.23.0"
3
+ __version__ = "1.23.1"
@@ -24,7 +24,7 @@ try:
24
24
  except ImportError:
25
25
  from ucollections import OrderedDict # type: ignore
26
26
 
27
- __version__ = "v1.23.0"
27
+ __version__ = "v1.23.1"
28
28
  ENOENT = 2
29
29
  _MAX_CLASS_LEVEL = 2 # Max class nesting
30
30
  LIBS = ["lib", "/lib", "/sd/lib", "/flash/lib", "."]
@@ -488,7 +488,7 @@ def ensure_folder(path: str):
488
488
 
489
489
  def _build(s):
490
490
  # extract build from sys.version or os.uname().version if available
491
- # sys.version: 'MicroPython v1.23.0-preview.6.g3d0b6276f'
491
+ # sys.version: 'MicroPython v1.23.1-preview.6.g3d0b6276f'
492
492
  # sys.implementation.version: 'v1.13-103-gb137d064e'
493
493
  if not s:
494
494
  return ""
@@ -595,10 +595,10 @@ def _info(): # type:() -> dict[str, str]
595
595
  if (
596
596
  info["version"]
597
597
  and info["version"].endswith(".0")
598
- and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.23.0 do not have a micro .0
598
+ and info["version"] >= "1.10.0" # versions from 1.10.0 to 1.23.1 do not have a micro .0
599
599
  and info["version"] <= "1.19.9"
600
600
  ):
601
- # versions from 1.10.0 to 1.23.0 do not have a micro .0
601
+ # versions from 1.10.0 to 1.23.1 do not have a micro .0
602
602
  info["version"] = info["version"][:-2]
603
603
 
604
604
  # spell-checker: disable