micropython-stubber 1.20.1__py3-none-any.whl → 1.20.4__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 (60) hide show
  1. {micropython_stubber-1.20.1.dist-info → micropython_stubber-1.20.4.dist-info}/METADATA +4 -3
  2. {micropython_stubber-1.20.1.dist-info → micropython_stubber-1.20.4.dist-info}/RECORD +58 -51
  3. {micropython_stubber-1.20.1.dist-info → micropython_stubber-1.20.4.dist-info}/WHEEL +1 -1
  4. mpflash/README.md +16 -5
  5. mpflash/mpflash/add_firmware.py +98 -0
  6. mpflash/mpflash/ask_input.py +97 -120
  7. mpflash/mpflash/cli_download.py +42 -25
  8. mpflash/mpflash/cli_flash.py +70 -32
  9. mpflash/mpflash/cli_group.py +17 -14
  10. mpflash/mpflash/cli_list.py +39 -3
  11. mpflash/mpflash/cli_main.py +17 -6
  12. mpflash/mpflash/common.py +125 -12
  13. mpflash/mpflash/config.py +12 -0
  14. mpflash/mpflash/connected.py +74 -0
  15. mpflash/mpflash/download.py +132 -51
  16. mpflash/mpflash/downloaded.py +36 -15
  17. mpflash/mpflash/flash.py +2 -2
  18. mpflash/mpflash/flash_esp.py +2 -2
  19. mpflash/mpflash/flash_uf2.py +14 -8
  20. mpflash/mpflash/flash_uf2_boardid.py +2 -1
  21. mpflash/mpflash/flash_uf2_linux.py +5 -16
  22. mpflash/mpflash/flash_uf2_macos.py +37 -0
  23. mpflash/mpflash/flash_uf2_windows.py +5 -5
  24. mpflash/mpflash/list.py +57 -57
  25. mpflash/mpflash/mpboard_id/__init__.py +41 -44
  26. mpflash/mpflash/mpboard_id/add_boards.py +255 -0
  27. mpflash/mpflash/mpboard_id/board.py +37 -0
  28. mpflash/mpflash/mpboard_id/board_id.py +54 -34
  29. mpflash/mpflash/mpboard_id/board_info.zip +0 -0
  30. mpflash/mpflash/mpboard_id/store.py +43 -0
  31. mpflash/mpflash/mpremoteboard/__init__.py +18 -6
  32. mpflash/mpflash/uf2disk.py +12 -0
  33. mpflash/mpflash/vendor/basicgit.py +288 -0
  34. mpflash/mpflash/vendor/dfu.py +1 -0
  35. mpflash/mpflash/vendor/versions.py +7 -3
  36. mpflash/mpflash/worklist.py +71 -48
  37. mpflash/poetry.lock +164 -138
  38. mpflash/pyproject.toml +18 -15
  39. stubber/__init__.py +1 -1
  40. stubber/board/createstubs.py +13 -3
  41. stubber/board/createstubs_db.py +5 -7
  42. stubber/board/createstubs_db_min.py +329 -825
  43. stubber/board/createstubs_db_mpy.mpy +0 -0
  44. stubber/board/createstubs_mem.py +6 -7
  45. stubber/board/createstubs_mem_min.py +304 -765
  46. stubber/board/createstubs_mem_mpy.mpy +0 -0
  47. stubber/board/createstubs_min.py +293 -975
  48. stubber/board/createstubs_mpy.mpy +0 -0
  49. stubber/board/modulelist.txt +10 -0
  50. stubber/commands/get_core_cmd.py +7 -6
  51. stubber/commands/get_docstubs_cmd.py +8 -3
  52. stubber/commands/get_frozen_cmd.py +5 -2
  53. stubber/publish/publish.py +18 -7
  54. stubber/update_module_list.py +2 -24
  55. stubber/utils/makeversionhdr.py +3 -2
  56. stubber/utils/versions.py +2 -1
  57. mpflash/mpflash/mpboard_id/board_info.csv +0 -2213
  58. mpflash/mpflash/mpboard_id/board_info.json +0 -19910
  59. {micropython_stubber-1.20.1.dist-info → micropython_stubber-1.20.4.dist-info}/LICENSE +0 -0
  60. {micropython_stubber-1.20.1.dist-info → micropython_stubber-1.20.4.dist-info}/entry_points.txt +0 -0
@@ -5,97 +5,85 @@ Note: The prompts can use "{version}" and "{action}" to insert the version and a
5
5
  The values are provided from the answers dictionary.
6
6
  """
7
7
 
8
- from dataclasses import dataclass, field
9
- from pathlib import Path
10
- from typing import Dict, List, Sequence, Tuple, Union
8
+ from typing import List, Sequence, Tuple, Union
11
9
 
12
10
  from loguru import logger as log
13
11
 
14
- from mpflash.config import config
15
- from mpflash.mpboard_id import get_stored_boards_for_port, known_stored_boards, local_mp_ports
16
- from mpflash.mpremoteboard import MPRemoteBoard
17
- from mpflash.vendor.versions import micropython_versions
18
-
19
-
20
- @dataclass
21
- class Params:
22
- ports: List[str] = field(default_factory=list)
23
- boards: List[str] = field(default_factory=list)
24
- versions: List[str] = field(default_factory=list)
25
- fw_folder: Path = Path()
26
-
27
-
28
- @dataclass
29
- class DownloadParams(Params):
30
- clean: bool = False
31
- force: bool = False
32
-
33
-
34
- @dataclass
35
- class FlashParams(Params):
36
- # TODO: Should Serial port be a list?
37
- serial: str = ""
38
- erase: bool = True
39
- bootloader: bool = True
40
- cpu: str = ""
41
-
42
-
43
- ParamType = Union[DownloadParams, FlashParams]
12
+ from .common import DownloadParams, FlashParams, ParamType
13
+ from .config import config
14
+ from .mpboard_id import get_known_boards_for_port, get_known_ports, known_stored_boards
15
+ from .mpremoteboard import MPRemoteBoard
16
+ from .vendor.versions import micropython_versions
44
17
 
45
18
 
46
19
  def ask_missing_params(
47
20
  params: ParamType,
48
- action: str = "download",
49
21
  ) -> ParamType:
50
22
  """
51
23
  Asks the user for parameters that have not been supplied on the commandline and returns the updated params.
52
24
 
53
25
  Args:
54
26
  params (ParamType): The parameters to be updated.
55
- action (str, optional): The action to be performed. Defaults to "download".
56
27
 
57
28
  Returns:
58
29
  ParamType: The updated parameters.
59
30
  """
31
+ import inquirer
32
+
33
+ log.trace(f"ask_missing_params: {params}")
34
+
35
+ # if action flash, single input
36
+ # if action download, multiple input
37
+ multi_select = isinstance(params, DownloadParams)
38
+ action = "download" if isinstance(params, DownloadParams) else "flash"
60
39
  if not config.interactive:
61
40
  # no interactivity allowed
62
41
  return params
63
- # import only when needed to reduce load time
64
- import inquirer
65
42
 
66
43
  questions = []
67
- answers = {"action": action}
68
- if isinstance(params, FlashParams):
44
+ answers: dict[str, Union[str, List]] = {"action": action}
45
+ if not multi_select:
69
46
  if not params.serial or "?" in params.serial:
70
- ask_serialport(questions, action=action)
47
+ questions.append(ask_serialport(multi_select=False, bluetooth=False))
71
48
  else:
72
49
  answers["serial"] = params.serial
73
50
 
74
- if not params.versions or "?" in params.versions:
75
- ask_versions(questions, action=action)
51
+ if params.versions == [] or "?" in params.versions:
52
+ questions.append(ask_mp_version(multi_select=multi_select, action=action))
76
53
  else:
77
54
  # versions is used to show only the boards for the selected versions
78
55
  answers["versions"] = params.versions # type: ignore
79
56
 
80
57
  if not params.boards or "?" in params.boards:
81
- ask_port_board(questions, action=action)
82
-
83
- answers = inquirer.prompt(questions, answers=answers)
58
+ questions.extend(ask_port_board(multi_select=multi_select, action=action))
59
+ if questions:
60
+ answers = inquirer.prompt(questions, answers=answers) # type: ignore
84
61
  if not answers:
85
62
  # input cancelled by user
86
63
  return [] # type: ignore
87
- # print(repr(answers))
64
+ log.trace(f"answers: {answers}")
88
65
  if isinstance(params, FlashParams) and "serial" in answers:
89
- params.serial = answers["serial"]
66
+ if isinstance(answers["serial"], str):
67
+ answers["serial"] = [answers["serial"]]
68
+ params.serial = [s.split()[0] for s in answers["serial"]] # split to remove the description
90
69
  if "port" in answers:
91
- params.ports = [answers["port"]]
70
+ params.ports = [p for p in params.ports if p != "?"] # remove the "?" if present
71
+ params.ports.extend(answers["port"])
92
72
  if "boards" in answers:
93
- params.boards = answers["boards"] if isinstance(answers["boards"], list) else [answers["boards"]]
73
+ params.boards = [b for b in params.boards if b != "?"] # remove the "?" if present
74
+ params.boards.extend(answers["boards"] if isinstance(answers["boards"], list) else [answers["boards"]])
94
75
  if "versions" in answers:
76
+ params.versions = [v for v in params.versions if v != "?"] # remove the "?" if present
95
77
  # make sure it is a list
96
- params.versions = answers["versions"] if isinstance(answers["versions"], list) else [answers["versions"]]
97
-
98
- log.debug(repr(params))
78
+ if isinstance(answers["versions"], (list, tuple)):
79
+ params.versions.extend(answers["versions"])
80
+ else:
81
+ params.versions.append(answers["versions"])
82
+ # remove duplicates
83
+ params.ports = list(set(params.ports))
84
+ params.boards = list(set(params.boards))
85
+ params.versions = list(set(params.versions))
86
+ log.trace(f"ask_missing_params returns: {params}")
99
87
 
100
88
  return params
101
89
 
@@ -110,19 +98,18 @@ def filter_matching_boards(answers: dict) -> Sequence[Tuple[str, str]]:
110
98
  Returns:
111
99
  Sequence[Tuple[str, str]]: The filtered boards.
112
100
  """
101
+ versions = None
113
102
  # if version is not asked ; then need to get the version from the inputs
114
103
  if "versions" in answers:
115
- _versions = list(answers["versions"])
116
- if "stable" in _versions:
117
- _versions.remove("stable")
118
- _versions.append(micropython_versions()[-2]) # latest stable
119
- if "preview" in _versions:
120
- _versions.remove("preview")
121
- _versions.extend((micropython_versions()[-1], micropython_versions()[-2])) # latest preview and stable
122
-
123
- some_boards = known_stored_boards(answers["port"], _versions) # or known_mp_boards(answers["port"])
124
- else:
125
- some_boards = known_stored_boards(answers["port"])
104
+ versions = list(answers["versions"])
105
+ if "stable" in versions:
106
+ versions.remove("stable")
107
+ versions.append(micropython_versions()[-2]) # latest stable
108
+ elif "preview" in versions:
109
+ versions.remove("preview")
110
+ versions.extend((micropython_versions()[-1], micropython_versions()[-2])) # latest preview and stable
111
+
112
+ some_boards = known_stored_boards(answers["port"], versions) # or known_mp_boards(answers["port"])
126
113
 
127
114
  if some_boards:
128
115
  # Create a dictionary where the keys are the second elements of the tuples
@@ -131,11 +118,11 @@ def filter_matching_boards(answers: dict) -> Sequence[Tuple[str, str]]:
131
118
  # Get the values of the dictionary, which are the unique items from the original list
132
119
  some_boards = list(unique_dict.values())
133
120
  else:
134
- some_boards = [(f"No {answers['port']} boards found for version(s) {_versions}", "")]
121
+ some_boards = [(f"No {answers['port']} boards found for version(s) {versions}", "")]
135
122
  return some_boards
136
123
 
137
124
 
138
- def ask_port_board(questions: list, *, action: str):
125
+ def ask_port_board(*, multi_select: bool, action: str):
139
126
  """
140
127
  Asks the user for the port and board selection.
141
128
 
@@ -149,31 +136,28 @@ def ask_port_board(questions: list, *, action: str):
149
136
  # import only when needed to reduce load time
150
137
  import inquirer
151
138
 
152
- # TODO: if action = flash, Use Inquirer.List for boards
153
- inquirer_ux = inquirer.Checkbox if action == "download" else inquirer.List
154
- questions.extend(
155
- (
156
- inquirer.List(
157
- "port",
158
- message="Which port do you want to {action} " + "to {serial} ?" if action == "flash" else "?",
159
- choices=local_mp_ports(),
160
- autocomplete=True,
161
- ),
162
- inquirer_ux(
163
- "boards",
164
- message=(
165
- "Which {port} board firmware do you want to {action} " + "to {serial} ?"
166
- if action == "flash"
167
- else "?"
168
- ),
169
- choices=filter_matching_boards,
170
- validate=lambda _, x: True if x else "Please select at least one board", # type: ignore
139
+ # if action flash, single input
140
+ # if action download, multiple input
141
+ inquirer_ux = inquirer.Checkbox if multi_select else inquirer.List
142
+ return [
143
+ inquirer.List(
144
+ "port",
145
+ message="Which port do you want to {action} " + "to {serial} ?" if action == "flash" else "?",
146
+ choices=get_known_ports(),
147
+ # autocomplete=True,
148
+ ),
149
+ inquirer_ux(
150
+ "boards",
151
+ message=(
152
+ "Which {port} board firmware do you want to {action} " + "to {serial} ?" if action == "flash" else "?"
171
153
  ),
172
- )
173
- )
154
+ choices=filter_matching_boards,
155
+ validate=lambda _, x: True if x else "Please select at least one board", # type: ignore
156
+ ),
157
+ ]
174
158
 
175
159
 
176
- def ask_versions(questions: list, *, action: str):
160
+ def ask_mp_version(multi_select: bool, action: str):
177
161
  """
178
162
  Asks the user for the version selection.
179
163
 
@@ -182,46 +166,43 @@ def ask_versions(questions: list, *, action: str):
182
166
  action (str): The action to be performed.
183
167
 
184
168
  Returns:
185
- None
169
+
186
170
  """
187
171
  # import only when needed to reduce load time
188
172
  import inquirer
189
173
  import inquirer.errors
190
174
 
191
- input_ux = inquirer.Checkbox if action == "download" else inquirer.List
175
+ input_ux = inquirer.Checkbox if multi_select else inquirer.List
176
+
192
177
  mp_versions: List[str] = micropython_versions()
193
- mp_versions = [v for v in mp_versions if "preview" not in v]
178
+ mp_versions.reverse() # newest first
194
179
 
195
180
  # remove the versions for which there are no known boards in the board_info.json
196
181
  # todo: this may be a little slow
197
- mp_versions = [v for v in mp_versions if get_stored_boards_for_port("stm32", [v])]
198
-
199
- mp_versions.append("preview")
200
- mp_versions.reverse() # newest first
182
+ mp_versions = [v for v in mp_versions if "preview" in v or get_known_boards_for_port("stm32", [v])]
201
183
 
202
184
  def at_least_one_validation(answers, current) -> bool:
203
185
  if not current:
204
186
  raise inquirer.errors.ValidationError("", reason="Please select at least one version")
205
- if isinstance(current, list):
206
- if not any(current):
207
- raise inquirer.errors.ValidationError("", reason="Please select at least one version")
187
+ if isinstance(current, list) and not any(current):
188
+ raise inquirer.errors.ValidationError("", reason="Please select at least one version")
208
189
  return True
209
190
 
210
- questions.append(
211
- input_ux(
212
- # inquirer.List(
213
- "versions",
214
- message="Which version(s) do you want to {action} " + ("to {serial} ?" if action == "flash" else "?"),
215
- # Hints would be nice , but needs a hint for each and every option
216
- # hints=["Use space to select multiple options"],
217
- choices=mp_versions,
218
- autocomplete=True,
219
- validate=at_least_one_validation,
220
- )
191
+ message = "Which version(s) do you want to {action} " + ("to {serial} ?" if action == "flash" else "?")
192
+ q = input_ux(
193
+ # inquirer.List(
194
+ "versions",
195
+ message=message,
196
+ # Hints would be nice , but needs a hint for each and every option
197
+ # hints=["Use space to select multiple options"],
198
+ choices=mp_versions,
199
+ autocomplete=True,
200
+ validate=at_least_one_validation, # type: ignore
221
201
  )
202
+ return q
222
203
 
223
204
 
224
- def ask_serialport(questions: list, *, action: str):
205
+ def ask_serialport(*, multi_select: bool = False, bluetooth: bool = False):
225
206
  """
226
207
  Asks the user for the serial port selection.
227
208
 
@@ -235,15 +216,11 @@ def ask_serialport(questions: list, *, action: str):
235
216
  # import only when needed to reduce load time
236
217
  import inquirer
237
218
 
238
- serialports = MPRemoteBoard.connected_boards()
239
- questions.append(
240
- inquirer.List(
241
- "serial",
242
- message="Which serial port do you want to {action} ?",
243
- choices=serialports,
244
- other=True,
245
- validate=lambda _, x: True if x else "Please select or enter a serial port", # type: ignore
246
- )
219
+ comports = MPRemoteBoard.connected_boards(bluetooth=bluetooth, description=True)
220
+ return inquirer.List(
221
+ "serial",
222
+ message="Which serial port do you want to {action} ?",
223
+ choices=comports,
224
+ other=True,
225
+ validate=lambda _, x: True if x else "Please select or enter a serial port", # type: ignore
247
226
  )
248
-
249
- return questions
@@ -1,18 +1,18 @@
1
1
  """CLI to Download MicroPython firmware for specific ports, boards and versions."""
2
2
 
3
3
  from pathlib import Path
4
- from typing import List, Tuple
5
4
 
6
5
  import rich_click as click
7
6
  from loguru import logger as log
8
7
 
8
+ from mpflash.connected import connected_ports_boards
9
9
  from mpflash.errors import MPFlashError
10
- from mpflash.mpboard_id import find_stored_board
10
+ from mpflash.mpboard_id import find_known_board
11
11
  from mpflash.vendor.versions import clean_version
12
12
 
13
- from .ask_input import DownloadParams, ask_missing_params
13
+ from .ask_input import ask_missing_params
14
14
  from .cli_group import cli
15
- from .cli_list import list_mcus
15
+ from .common import DownloadParams
16
16
  from .config import config
17
17
  from .download import download
18
18
 
@@ -50,6 +50,28 @@ from .download import download
50
50
  help="The board(s) to download the firmware for.",
51
51
  metavar="BOARD_ID or ?",
52
52
  )
53
+ @click.option(
54
+ "--serial",
55
+ "--serial-port",
56
+ "-s",
57
+ "serial",
58
+ default=["*"],
59
+ show_default=True,
60
+ multiple=True,
61
+ help="Which serial port(s) to flash",
62
+ metavar="SERIALPORT",
63
+ )
64
+ @click.option(
65
+ "--ignore",
66
+ "-i",
67
+ is_eager=True,
68
+ help="Serial port(s) to ignore. Defaults to MPFLASH_IGNORE.",
69
+ multiple=True,
70
+ default=[],
71
+ envvar="MPFLASH_IGNORE",
72
+ show_default=True,
73
+ metavar="SERIALPORT",
74
+ )
53
75
  @click.option(
54
76
  "--clean/--no-clean",
55
77
  default=True,
@@ -67,14 +89,25 @@ def cli_download(**kwargs) -> int:
67
89
  params = DownloadParams(**kwargs)
68
90
  params.versions = list(params.versions)
69
91
  params.boards = list(params.boards)
92
+ params.serial = list(params.serial)
93
+ params.ignore = list(params.ignore)
94
+
95
+ # all_boards: List[MPRemoteBoard] = []
70
96
  if params.boards:
71
- pass
72
- # TODO Clean board - same as in cli_flash.py
97
+ if not params.ports:
98
+ # no ports specified - resolve ports from specified boards by resolving board IDs
99
+ for board in params.boards:
100
+ if board != "?":
101
+ try:
102
+ board_ = find_known_board(board)
103
+ params.ports.append(board_.port)
104
+ except MPFlashError as e:
105
+ log.error(f"{e}")
73
106
  else:
74
- # no boards specified - detect connected boards
75
- params.ports, params.boards = connected_ports_boards()
107
+ # no boards specified - detect connected ports and boards
108
+ params.ports, params.boards, _ = connected_ports_boards(include=params.serial, ignore=params.ignore)
76
109
 
77
- params = ask_missing_params(params, action="download")
110
+ params = ask_missing_params(params)
78
111
  if not params: # Cancelled by user
79
112
  return 2
80
113
  params.versions = [clean_version(v, drop_v=True) for v in params.versions]
@@ -93,19 +126,3 @@ def cli_download(**kwargs) -> int:
93
126
  except MPFlashError as e:
94
127
  log.error(f"{e}")
95
128
  return 1
96
-
97
-
98
- def connected_ports_boards() -> Tuple[List[str], List[str]]:
99
- """
100
- Returns a tuple containing lists of unique ports and boards from the connected MCUs.
101
- Boards that are physically connected, but give no tangible response are ignored.
102
-
103
- Returns:
104
- A tuple containing two lists:
105
- - A list of unique ports where MCUs are connected.
106
- - A list of unique board names of the connected MCUs.
107
- """
108
- mpr_boards = [b for b in list_mcus() if b.connected]
109
- ports = list({b.port for b in mpr_boards})
110
- boards = list({b.board for b in mpr_boards})
111
- return ports, boards
@@ -1,19 +1,23 @@
1
1
  from pathlib import Path
2
+ from typing import List
2
3
 
3
4
  import rich_click as click
4
5
  from loguru import logger as log
5
6
 
7
+ # from mpflash.common import filtered_comports
6
8
  from mpflash.errors import MPFlashError
7
- from mpflash.mpboard_id import find_stored_board
9
+ from mpflash.mpboard_id import find_known_board
10
+ from mpflash.mpremoteboard import MPRemoteBoard
8
11
  from mpflash.vendor.versions import clean_version
9
12
 
10
- from .ask_input import FlashParams, ask_missing_params
13
+ from .ask_input import ask_missing_params
11
14
  from .cli_download import connected_ports_boards
12
15
  from .cli_group import cli
13
16
  from .cli_list import show_mcus
17
+ from .common import FlashParams
14
18
  from .config import config
15
19
  from .flash import flash_list
16
- from .worklist import MPRemoteBoard, WorkList, full_auto_worklist, manual_worklist, single_auto_worklist
20
+ from .worklist import WorkList, full_auto_worklist, manual_worklist, single_auto_worklist
17
21
 
18
22
  # #########################################################################################################
19
23
  # CLI
@@ -48,10 +52,22 @@ from .worklist import MPRemoteBoard, WorkList, full_auto_worklist, manual_workli
48
52
  "--serial-port",
49
53
  "-s",
50
54
  "serial",
51
- default="auto",
55
+ default=["*"],
56
+ multiple=True,
52
57
  show_default=True,
53
58
  help="Which serial port(s) to flash",
54
- metavar="SERIAL_PORT",
59
+ metavar="SERIALPORT",
60
+ )
61
+ @click.option(
62
+ "--ignore",
63
+ "-i",
64
+ is_eager=True,
65
+ help="Serial port(s) to ignore. Defaults to MPFLASH_IGNORE.",
66
+ multiple=True,
67
+ default=[],
68
+ envvar="MPFLASH_IGNORE",
69
+ show_default=True,
70
+ metavar="SERIALPORT",
55
71
  )
56
72
  @click.option(
57
73
  "--port",
@@ -101,31 +117,31 @@ def cli_flash_board(**kwargs) -> int:
101
117
  kwargs["boards"] = [kwargs.pop("board")]
102
118
 
103
119
  params = FlashParams(**kwargs)
120
+ params.versions = list(params.versions)
121
+ params.boards = list(params.boards)
122
+ params.serial = list(params.serial)
123
+ params.ignore = list(params.ignore)
124
+
125
+ # make it simple for the user to flash one board by asking for the serial port if not specified
126
+ if params.boards == ["?"] and params.serial == "*":
127
+ params.serial = ["?"]
128
+
129
+ # Detect connected boards if not specified,
130
+ # and ask for input if boards cannot be detected
131
+ all_boards: List[MPRemoteBoard] = []
104
132
  if not params.boards or params.boards == []:
105
133
  # nothing specified - detect connected boards
106
- params.ports, params.boards = connected_ports_boards()
134
+ params.ports, params.boards, all_boards = connected_ports_boards(include=params.ports, ignore=params.ignore)
107
135
  if params.boards == []:
108
136
  # No MicroPython boards detected, but it could be unflashed or not in bootloader mode
109
137
  # Ask for serial port and board_id to flash
110
- params.serial = "?"
138
+ params.serial = ["?"]
111
139
  params.boards = ["?"]
112
140
  else:
113
- for board_id in params.boards:
114
- if board_id == "":
115
- params.boards.remove(board_id)
116
- continue
117
- if " " in board_id:
118
- try:
119
- info = find_stored_board(board_id)
120
- if info:
121
- log.info(f"Resolved board description: {info['board']}")
122
- params.boards.remove(board_id)
123
- params.boards.append(info["board"])
124
- except Exception as e:
125
- log.warning(f"unable to resolve board description: {e}")
141
+ resolve_board_ids(params)
126
142
 
127
143
  # Ask for missing input if needed
128
- params = ask_missing_params(params, action="flash")
144
+ params = ask_missing_params(params)
129
145
  if not params: # Cancelled by user
130
146
  return 2
131
147
  # TODO: Just in time Download of firmware
@@ -135,27 +151,33 @@ def cli_flash_board(**kwargs) -> int:
135
151
  if len(params.versions) > 1:
136
152
  log.error(f"Only one version can be flashed at a time, not {params.versions}")
137
153
  raise MPFlashError("Only one version can be flashed at a time")
138
- # if len(params.boards) > 1:
139
- # log.error(f"Only one board can be flashed at a time, not {params.boards}")
140
- # raise MPFlashError("Only one board can be flashed at a time")
141
154
 
142
155
  params.versions = [clean_version(v) for v in params.versions]
143
156
  worklist: WorkList = []
144
157
  # if serial port == auto and there are one or more specified/detected boards
145
- if params.serial == "auto" and params.boards:
146
- worklist = full_auto_worklist(version=params.versions[0], fw_folder=params.fw_folder)
158
+ if params.serial == ["*"] and params.boards:
159
+ if not all_boards:
160
+ log.trace("No boards detected yet, scanning for connected boards")
161
+ _, _, all_boards = connected_ports_boards(include=params.ports, ignore=params.ignore)
162
+ worklist = full_auto_worklist(
163
+ all_boards=all_boards,
164
+ version=params.versions[0],
165
+ fw_folder=params.fw_folder,
166
+ include=params.serial,
167
+ ignore=params.ignore,
168
+ )
147
169
  elif params.versions[0] and params.boards[0] and params.serial:
148
- # A single serial port including the board / variant
170
+ # A one or more serial port including the board / variant
149
171
  worklist = manual_worklist(
150
- params.versions[0],
151
- params.fw_folder,
152
- params.serial,
153
- params.boards[0],
172
+ params.serial[0],
173
+ board_id=params.boards[0],
174
+ version=params.versions[0],
175
+ fw_folder=params.fw_folder,
154
176
  )
155
177
  else:
156
178
  # just this serial port on auto
157
179
  worklist = single_auto_worklist(
158
- serial_port=params.serial,
180
+ serial=params.serial[0],
159
181
  version=params.versions[0],
160
182
  fw_folder=params.fw_folder,
161
183
  )
@@ -172,3 +194,19 @@ def cli_flash_board(**kwargs) -> int:
172
194
  else:
173
195
  log.error("No boards were flashed")
174
196
  return 1
197
+
198
+
199
+ def resolve_board_ids(params):
200
+ """Resolve board descriptions to board_id, and remove empty strings from list of boards"""
201
+ for board_id in params.boards:
202
+ if board_id == "":
203
+ params.boards.remove(board_id)
204
+ continue
205
+ if " " in board_id:
206
+ try:
207
+ if info := find_known_board(board_id):
208
+ log.info(f"Resolved board description: {info.board_id}")
209
+ params.boards.remove(board_id)
210
+ params.boards.append(info.board_id)
211
+ except Exception as e:
212
+ log.warning(f"Unable to resolve board description: {e}")
@@ -5,30 +5,34 @@ Additional comands are added in the submodules.
5
5
 
6
6
  import rich_click as click
7
7
 
8
- from .config import config
9
- from .logger import make_quiet, set_loglevel
8
+ from .config import __version__, config
9
+ from .logger import log, make_quiet, set_loglevel
10
10
 
11
11
 
12
12
  def cb_verbose(ctx, param, value):
13
13
  """Callback to set the log level to DEBUG if verbose is set"""
14
14
  if value and not config.quiet:
15
- set_loglevel("DEBUG")
16
15
  config.verbose = True
16
+ if value > 1:
17
+ set_loglevel("TRACE")
18
+ else:
19
+ set_loglevel("DEBUG")
20
+ log.debug(f"version: {__version__}")
17
21
  else:
18
22
  set_loglevel("INFO")
19
23
  config.verbose = False
20
24
  return value
21
25
 
22
26
 
23
- def cb_ignore(ctx, param, value):
27
+ def cb_interactive(ctx, param, value):
24
28
  if value:
25
- config.ignore_ports = list(value)
29
+ config.interactive = value
26
30
  return value
27
31
 
28
32
 
29
- def cb_interactive(ctx, param, value):
33
+ def cb_test(ctx, param, value):
30
34
  if value:
31
- config.interactive = value
35
+ config.tests = value
32
36
  return value
33
37
 
34
38
 
@@ -64,21 +68,20 @@ def cb_quiet(ctx, param, value):
64
68
  "-V",
65
69
  "--verbose",
66
70
  is_eager=True,
67
- is_flag=True,
71
+ count=True,
68
72
  help="Enables verbose mode.",
69
73
  callback=cb_verbose,
70
74
  )
71
75
  @click.option(
72
- "--ignore",
73
- "-i",
76
+ "--test",
74
77
  is_eager=True,
75
- help="Serial port(s) to ignore. Defaults to MPFLASH_IGNORE.",
76
- callback=cb_ignore,
78
+ help="test a specific feature",
79
+ callback=cb_test,
77
80
  multiple=True,
78
81
  default=[],
79
- envvar="MPFLASH_IGNORE",
82
+ envvar="MPFLASH_TEST",
80
83
  show_default=True,
81
- metavar="SERIALPORT",
84
+ metavar="TEST",
82
85
  )
83
86
  def cli(**kwargs):
84
87
  """mpflash - MicroPython Tool.