micropython-stubber 1.23.1__py3-none-any.whl → 1.23.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.
Files changed (153) hide show
  1. {micropython_stubber-1.23.1.dist-info → micropython_stubber-1.23.2.dist-info}/LICENSE +30 -30
  2. {micropython_stubber-1.23.1.dist-info → micropython_stubber-1.23.2.dist-info}/METADATA +32 -15
  3. micropython_stubber-1.23.2.dist-info/RECORD +158 -0
  4. micropython_stubber-1.23.2.dist-info/entry_points.txt +5 -0
  5. mpflash/README.md +220 -194
  6. mpflash/libusb_flash.ipynb +203 -203
  7. mpflash/mpflash/add_firmware.py +98 -98
  8. mpflash/mpflash/ask_input.py +236 -236
  9. mpflash/mpflash/basicgit.py +284 -284
  10. mpflash/mpflash/bootloader/__init__.py +2 -2
  11. mpflash/mpflash/bootloader/activate.py +60 -60
  12. mpflash/mpflash/bootloader/detect.py +82 -82
  13. mpflash/mpflash/bootloader/manual.py +101 -101
  14. mpflash/mpflash/bootloader/micropython.py +12 -12
  15. mpflash/mpflash/bootloader/touch1200.py +36 -36
  16. mpflash/mpflash/cli_download.py +129 -129
  17. mpflash/mpflash/cli_flash.py +224 -219
  18. mpflash/mpflash/cli_group.py +111 -111
  19. mpflash/mpflash/cli_list.py +87 -81
  20. mpflash/mpflash/cli_main.py +39 -39
  21. mpflash/mpflash/common.py +210 -165
  22. mpflash/mpflash/config.py +44 -44
  23. mpflash/mpflash/connected.py +96 -78
  24. mpflash/mpflash/download.py +364 -364
  25. mpflash/mpflash/downloaded.py +130 -130
  26. mpflash/mpflash/errors.py +9 -9
  27. mpflash/mpflash/flash/__init__.py +55 -55
  28. mpflash/mpflash/flash/esp.py +59 -59
  29. mpflash/mpflash/flash/stm32.py +19 -19
  30. mpflash/mpflash/flash/stm32_dfu.py +104 -104
  31. mpflash/mpflash/flash/uf2/__init__.py +88 -88
  32. mpflash/mpflash/flash/uf2/boardid.py +15 -15
  33. mpflash/mpflash/flash/uf2/linux.py +136 -130
  34. mpflash/mpflash/flash/uf2/macos.py +42 -42
  35. mpflash/mpflash/flash/uf2/uf2disk.py +12 -12
  36. mpflash/mpflash/flash/uf2/windows.py +43 -43
  37. mpflash/mpflash/flash/worklist.py +170 -170
  38. mpflash/mpflash/list.py +106 -99
  39. mpflash/mpflash/logger.py +41 -41
  40. mpflash/mpflash/mpboard_id/__init__.py +93 -93
  41. mpflash/mpflash/mpboard_id/add_boards.py +251 -251
  42. mpflash/mpflash/mpboard_id/board.py +37 -37
  43. mpflash/mpflash/mpboard_id/board_id.py +86 -86
  44. mpflash/mpflash/mpboard_id/store.py +43 -43
  45. mpflash/mpflash/mpremoteboard/__init__.py +266 -222
  46. mpflash/mpflash/mpremoteboard/mpy_fw_info.py +141 -141
  47. mpflash/mpflash/mpremoteboard/runner.py +140 -140
  48. mpflash/mpflash/vendor/click_aliases.py +91 -91
  49. mpflash/mpflash/vendor/dfu.py +165 -165
  50. mpflash/mpflash/vendor/pydfu.py +605 -605
  51. mpflash/mpflash/vendor/readme.md +2 -2
  52. mpflash/mpflash/versions.py +135 -135
  53. mpflash/poetry.lock +1599 -1599
  54. mpflash/pyproject.toml +65 -65
  55. mpflash/stm32_udev_rules.md +62 -62
  56. stubber/__init__.py +3 -3
  57. stubber/board/board_info.csv +193 -193
  58. stubber/board/boot.py +34 -34
  59. stubber/board/createstubs.py +1004 -986
  60. stubber/board/createstubs_db.py +826 -825
  61. stubber/board/createstubs_db_min.py +332 -331
  62. stubber/board/createstubs_db_mpy.mpy +0 -0
  63. stubber/board/createstubs_lvgl.py +741 -741
  64. stubber/board/createstubs_lvgl_min.py +741 -741
  65. stubber/board/createstubs_mem.py +767 -766
  66. stubber/board/createstubs_mem_min.py +307 -306
  67. stubber/board/createstubs_mem_mpy.mpy +0 -0
  68. stubber/board/createstubs_min.py +295 -294
  69. stubber/board/createstubs_mpy.mpy +0 -0
  70. stubber/board/fw_info.py +141 -141
  71. stubber/board/info.py +183 -183
  72. stubber/board/main.py +19 -19
  73. stubber/board/modulelist.txt +247 -247
  74. stubber/board/pyrightconfig.json +34 -34
  75. stubber/bulk/mcu_stubber.py +437 -454
  76. stubber/codemod/_partials/__init__.py +48 -48
  77. stubber/codemod/_partials/db_main.py +147 -147
  78. stubber/codemod/_partials/lvgl_main.py +77 -77
  79. stubber/codemod/_partials/modules_reader.py +80 -80
  80. stubber/codemod/add_comment.py +53 -53
  81. stubber/codemod/add_method.py +65 -65
  82. stubber/codemod/board.py +317 -317
  83. stubber/codemod/enrich.py +151 -145
  84. stubber/codemod/merge_docstub.py +284 -284
  85. stubber/codemod/modify_list.py +54 -54
  86. stubber/codemod/utils.py +56 -56
  87. stubber/commands/build_cmd.py +94 -94
  88. stubber/commands/cli.py +49 -55
  89. stubber/commands/clone_cmd.py +78 -78
  90. stubber/commands/config_cmd.py +29 -29
  91. stubber/commands/enrich_folder_cmd.py +71 -71
  92. stubber/commands/get_core_cmd.py +71 -71
  93. stubber/commands/get_docstubs_cmd.py +92 -89
  94. stubber/commands/get_frozen_cmd.py +117 -114
  95. stubber/commands/get_mcu_cmd.py +102 -61
  96. stubber/commands/merge_cmd.py +66 -66
  97. stubber/commands/publish_cmd.py +118 -118
  98. stubber/commands/stub_cmd.py +31 -31
  99. stubber/commands/switch_cmd.py +62 -62
  100. stubber/commands/variants_cmd.py +48 -48
  101. stubber/cst_transformer.py +178 -178
  102. stubber/data/board_info.csv +193 -193
  103. stubber/data/board_info.json +1729 -1729
  104. stubber/data/micropython_tags.csv +15 -15
  105. stubber/data/requirements-core-micropython.txt +38 -38
  106. stubber/data/requirements-core-pycopy.txt +39 -39
  107. stubber/downloader.py +37 -36
  108. stubber/freeze/common.py +72 -68
  109. stubber/freeze/freeze_folder.py +69 -69
  110. stubber/freeze/freeze_manifest_2.py +126 -113
  111. stubber/freeze/get_frozen.py +131 -127
  112. stubber/get_cpython.py +112 -101
  113. stubber/get_lobo.py +59 -59
  114. stubber/minify.py +423 -419
  115. stubber/publish/bump.py +86 -86
  116. stubber/publish/candidates.py +275 -256
  117. stubber/publish/database.py +18 -18
  118. stubber/publish/defaults.py +40 -40
  119. stubber/publish/enums.py +24 -24
  120. stubber/publish/helpers.py +29 -29
  121. stubber/publish/merge_docstubs.py +136 -130
  122. stubber/publish/missing_class_methods.py +51 -49
  123. stubber/publish/package.py +150 -146
  124. stubber/publish/pathnames.py +51 -51
  125. stubber/publish/publish.py +120 -120
  126. stubber/publish/pypi.py +42 -38
  127. stubber/publish/stubpackage.py +1055 -1027
  128. stubber/rst/__init__.py +9 -9
  129. stubber/rst/classsort.py +78 -77
  130. stubber/rst/lookup.py +533 -530
  131. stubber/rst/output_dict.py +401 -401
  132. stubber/rst/reader.py +814 -814
  133. stubber/rst/report_return.py +77 -69
  134. stubber/rst/rst_utils.py +541 -540
  135. stubber/stubber.py +38 -38
  136. stubber/stubs_from_docs.py +90 -90
  137. stubber/tools/manifestfile.py +654 -654
  138. stubber/tools/readme.md +6 -6
  139. stubber/update_fallback.py +117 -117
  140. stubber/update_module_list.py +123 -123
  141. stubber/utils/__init__.py +6 -6
  142. stubber/utils/config.py +137 -125
  143. stubber/utils/makeversionhdr.py +54 -54
  144. stubber/utils/manifest.py +90 -90
  145. stubber/utils/post.py +80 -79
  146. stubber/utils/repos.py +156 -150
  147. stubber/utils/stubmaker.py +139 -139
  148. stubber/utils/typed_config_toml.py +80 -77
  149. stubber/variants.py +106 -106
  150. micropython_stubber-1.23.1.dist-info/RECORD +0 -159
  151. micropython_stubber-1.23.1.dist-info/entry_points.txt +0 -3
  152. mpflash/basicgit.py +0 -288
  153. {micropython_stubber-1.23.1.dist-info → micropython_stubber-1.23.2.dist-info}/WHEEL +0 -0
mpflash/mpflash/common.py CHANGED
@@ -1,165 +1,210 @@
1
- import fnmatch
2
- import os
3
- import sys
4
- from dataclasses import dataclass, field
5
- from enum import Enum
6
- from pathlib import Path
7
- from typing import List, Optional, Union
8
-
9
- from github import Auth, Github
10
- from serial.tools import list_ports
11
- from serial.tools.list_ports_common import ListPortInfo
12
-
13
- from .logger import log
14
-
15
- # from mpflash.mpremoteboard import MPRemoteBoard
16
-
17
- PORT_FWTYPES = {
18
- "stm32": [".dfu"], # need .dfu for pydfu.py - .hex for cube cli/GUI
19
- "esp32": [".bin"],
20
- "esp8266": [".bin"],
21
- "rp2": [".uf2"],
22
- "samd": [".uf2"],
23
- # below this not yet implemented / tested
24
- "mimxrt": [".hex"],
25
- "nrf": [".uf2"],
26
- "renesas-ra": [".hex"],
27
- }
28
-
29
- # Token with no permissions to avoid throttling
30
- # https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#getting-a-higher-rate-limit
31
- PAT_NO_ACCESS = (
32
- "github_pat" + "_11AAHPVFQ0qAkDnSUaMKSp" + "_ZkDl5NRRwBsUN6EYg9ahp1Dvj4FDDONnXVgimxC2EtpY7Q7BUKBoQ0Jq72X"
33
- )
34
- PAT = os.environ.get("GITHUB_TOKEN") or PAT_NO_ACCESS
35
- GH_CLIENT = Github(auth=Auth.Token(PAT))
36
-
37
-
38
- @dataclass
39
- class FWInfo:
40
- """
41
- Downloaded Firmware information
42
- is somewhat related to the BOARD class in the mpboard_id module
43
- """
44
-
45
- port: str # MicroPython port
46
- board: str # MicroPython board
47
- filename: str = field(default="") # relative filename of the firmware image
48
- firmware: str = field(default="") # url or path to original firmware image
49
- variant: str = field(default="") # MicroPython variant
50
- preview: bool = field(default=False) # True if the firmware is a preview version
51
- version: str = field(default="") # MicroPython version (NO v prefix)
52
- url: str = field(default="") # url to the firmware image download folder
53
- build: str = field(default="0") # The build = number of commits since the last release
54
- ext: str = field(default="") # the file extension of the firmware
55
- family: str = field(default="micropython") # The family of the firmware
56
- custom: bool = field(default=False) # True if the firmware is a custom build
57
- description: str = field(default="") # Description used by this firmware (custom only)
58
-
59
- def to_dict(self) -> dict:
60
- """Convert the object to a dictionary"""
61
- return self.__dict__
62
-
63
- @classmethod
64
- def from_dict(cls, data: dict) -> "FWInfo":
65
- """Create a FWInfo object from a dictionary"""
66
- # add missing keys
67
- if "ext" not in data:
68
- data["ext"] = Path(data["firmware"]).suffix
69
- if "family" not in data:
70
- data["family"] = "micropython"
71
- return cls(**data)
72
-
73
-
74
- @dataclass
75
- class Params:
76
- """Common parameters for downloading and flashing firmware"""
77
-
78
- ports: List[str] = field(default_factory=list)
79
- boards: List[str] = field(default_factory=list)
80
- versions: List[str] = field(default_factory=list)
81
- fw_folder: Path = Path()
82
- serial: List[str] = field(default_factory=list)
83
- ignore: List[str] = field(default_factory=list)
84
-
85
-
86
- @dataclass
87
- class DownloadParams(Params):
88
- """Parameters for downloading firmware"""
89
-
90
- clean: bool = False
91
- force: bool = False
92
-
93
-
94
- class BootloaderMethod(Enum):
95
- AUTO = "auto"
96
- MANUAL = "manual"
97
- MPY = "mpy"
98
- TOUCH_1200 = "touch1200"
99
- NONE = "none"
100
-
101
-
102
-
103
- @dataclass
104
- class FlashParams(Params):
105
- """Parameters for flashing a board"""
106
-
107
- erase: bool = True
108
- bootloader: BootloaderMethod = BootloaderMethod.NONE
109
- cpu: str = ""
110
-
111
- def __post_init__(self):
112
- if isinstance(self.bootloader, str):
113
- self.bootloader = BootloaderMethod(self.bootloader)
114
-
115
-
116
- ParamType = Union[DownloadParams, FlashParams]
117
-
118
-
119
- def filtered_comports(
120
- ignore: Optional[List[str]] = None,
121
- include: Optional[List[str]] = None,
122
- bluetooth: bool = False,
123
- ) -> List[ListPortInfo]: # sourcery skip: assign-if-exp
124
- """
125
- Get a list of filtered comports.
126
- """
127
- if not ignore:
128
- ignore = []
129
- elif not isinstance(ignore, list): # type: ignore
130
- ignore = list(ignore)
131
- if not include:
132
- include = ["*"]
133
- elif not isinstance(include, list): # type: ignore
134
- include = list(include)
135
-
136
- # remove ports that are to be ignored
137
- log.trace(f"{include=}, {ignore=}, {bluetooth=}")
138
- comports = [p for p in list_ports.comports() if not any(fnmatch.fnmatch(p.device, i) for i in ignore)]
139
- log.trace(f"comports: {[p.device for p in comports]}")
140
- # remove bluetooth ports
141
-
142
- if include != ["*"]:
143
- # if there are explicit ports to include, add them to the list
144
- explicit = [p for p in list_ports.comports() if any(fnmatch.fnmatch(p.device, i) for i in include)]
145
- log.trace(f"explicit: {[p.device for p in explicit]}")
146
- if ignore == []:
147
- # if nothing to ignore, just use the explicit list as a sinple sane default
148
- comports = explicit
149
- else:
150
- # if there are ports to ignore, add the explicit list to the filtered list
151
- comports = list(set(explicit) | set(comports))
152
- if not bluetooth:
153
- # filter out bluetooth ports
154
- comports = [p for p in comports if "bluetooth" not in p.description.lower()]
155
- comports = [p for p in comports if "BTHENUM" not in p.hwid]
156
- if sys.platform == "darwin":
157
- comports = [p for p in comports if ".Bluetooth" not in p.device]
158
- log.trace(f"no Bluetooth: {[p.device for p in comports]}")
159
- log.debug(f"filtered_comports: {[p.device for p in comports]}")
160
- # sort
161
- if sys.platform == "win32":
162
- # Windows sort of comports by number - but fallback to device name
163
- return sorted(comports, key=lambda x: int(x.device.split()[0][3:]) if x.device.split()[0][3:].isdigit() else x)
164
- # sort by device name
165
- return sorted(comports, key=lambda x: x.device)
1
+ import fnmatch
2
+ import glob
3
+ import os
4
+ import platform
5
+ import sys
6
+ from dataclasses import dataclass, field
7
+ from enum import Enum
8
+ from pathlib import Path
9
+ from typing import List, Optional, Union
10
+
11
+ from github import Auth, Github
12
+ from serial.tools import list_ports
13
+ from serial.tools.list_ports_common import ListPortInfo
14
+
15
+ from .logger import log
16
+
17
+ # from mpflash.mpremoteboard import MPRemoteBoard
18
+
19
+ PORT_FWTYPES = {
20
+ "stm32": [".dfu"], # need .dfu for pydfu.py - .hex for cube cli/GUI
21
+ "esp32": [".bin"],
22
+ "esp8266": [".bin"],
23
+ "rp2": [".uf2"],
24
+ "samd": [".uf2"],
25
+ # below this not yet implemented / tested
26
+ "mimxrt": [".hex"],
27
+ "nrf": [".uf2"],
28
+ "renesas-ra": [".hex"],
29
+ }
30
+
31
+ # Token with no permissions to avoid throttling
32
+ # https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#getting-a-higher-rate-limit
33
+ PAT_NO_ACCESS = (
34
+ "github_pat"
35
+ + "_11AAHPVFQ0qAkDnSUaMKSp"
36
+ + "_ZkDl5NRRwBsUN6EYg9ahp1Dvj4FDDONnXVgimxC2EtpY7Q7BUKBoQ0Jq72X"
37
+ )
38
+ PAT = os.environ.get("GITHUB_TOKEN") or PAT_NO_ACCESS
39
+ GH_CLIENT = Github(auth=Auth.Token(PAT))
40
+
41
+
42
+ @dataclass
43
+ class FWInfo:
44
+ """
45
+ Downloaded Firmware information
46
+ is somewhat related to the BOARD class in the mpboard_id module
47
+ """
48
+
49
+ port: str # MicroPython port
50
+ board: str # MicroPython board
51
+ filename: str = field(default="") # relative filename of the firmware image
52
+ firmware: str = field(default="") # url or path to original firmware image
53
+ variant: str = field(default="") # MicroPython variant
54
+ preview: bool = field(default=False) # True if the firmware is a preview version
55
+ version: str = field(default="") # MicroPython version (NO v prefix)
56
+ url: str = field(default="") # url to the firmware image download folder
57
+ build: str = field(default="0") # The build = number of commits since the last release
58
+ ext: str = field(default="") # the file extension of the firmware
59
+ family: str = field(default="micropython") # The family of the firmware
60
+ custom: bool = field(default=False) # True if the firmware is a custom build
61
+ description: str = field(default="") # Description used by this firmware (custom only)
62
+
63
+ def to_dict(self) -> dict:
64
+ """Convert the object to a dictionary"""
65
+ return self.__dict__
66
+
67
+ @classmethod
68
+ def from_dict(cls, data: dict) -> "FWInfo":
69
+ """Create a FWInfo object from a dictionary"""
70
+ # add missing keys
71
+ if "ext" not in data:
72
+ data["ext"] = Path(data["firmware"]).suffix
73
+ if "family" not in data:
74
+ data["family"] = "micropython"
75
+ return cls(**data)
76
+
77
+
78
+ @dataclass
79
+ class Params:
80
+ """Common parameters for downloading and flashing firmware"""
81
+
82
+ ports: List[str] = field(default_factory=list)
83
+ boards: List[str] = field(default_factory=list)
84
+ versions: List[str] = field(default_factory=list)
85
+ fw_folder: Path = Path()
86
+ serial: List[str] = field(default_factory=list)
87
+ ignore: List[str] = field(default_factory=list)
88
+ bluetooth: bool = False
89
+
90
+
91
+ @dataclass
92
+ class DownloadParams(Params):
93
+ """Parameters for downloading firmware"""
94
+
95
+ clean: bool = False
96
+ force: bool = False
97
+
98
+
99
+ class BootloaderMethod(Enum):
100
+ AUTO = "auto"
101
+ MANUAL = "manual"
102
+ MPY = "mpy"
103
+ TOUCH_1200 = "touch1200"
104
+ NONE = "none"
105
+
106
+
107
+ @dataclass
108
+ class FlashParams(Params):
109
+ """Parameters for flashing a board"""
110
+
111
+ erase: bool = True
112
+ bootloader: BootloaderMethod = BootloaderMethod.NONE
113
+ cpu: str = ""
114
+
115
+ def __post_init__(self):
116
+ if isinstance(self.bootloader, str):
117
+ self.bootloader = BootloaderMethod(self.bootloader)
118
+
119
+
120
+ ParamType = Union[DownloadParams, FlashParams]
121
+
122
+
123
+ def filtered_comports(
124
+ ignore: Optional[List[str]] = None,
125
+ include: Optional[List[str]] = None,
126
+ bluetooth: bool = False,
127
+ ) -> List[ListPortInfo]: # sourcery skip: assign-if-exp
128
+ """
129
+ Get a list of filtered comports using the include and ignore lists.
130
+ both can be globs (e.g. COM*) or exact port names (e.g. COM1)
131
+ """
132
+ if not ignore:
133
+ ignore = []
134
+ elif not isinstance(ignore, list): # type: ignore
135
+ ignore = list(ignore)
136
+ if not include:
137
+ include = ["*"]
138
+ elif not isinstance(include, list): # type: ignore
139
+ include = list(include)
140
+
141
+ # remove ports that are to be ignored
142
+ log.trace(f"{include=}, {ignore=}, {bluetooth=}")
143
+
144
+ comports = [
145
+ p for p in list_ports.comports() if not any(fnmatch.fnmatch(p.device, i) for i in ignore)
146
+ ]
147
+ if platform.system() == "Linux":
148
+ # use p.location to filter out the bogus ports on newer Linux kernels
149
+ # filter out the bogus ports on newer Linux kernels
150
+ comports = [p for p in comports if p.location]
151
+
152
+ log.trace(f"comports: {[p.device for p in comports]}")
153
+ # remove bluetooth ports
154
+
155
+ if include != ["*"]:
156
+ # if there are explicit ports to include, add them to the list
157
+ explicit = [
158
+ p for p in list_ports.comports() if any(fnmatch.fnmatch(p.device, i) for i in include)
159
+ ]
160
+ log.trace(f"explicit: {[p.device for p in explicit]}")
161
+ if ignore == []:
162
+ # if nothing to ignore, just use the explicit list as a sinple sane default
163
+ comports = explicit
164
+ else:
165
+ # if there are ports to ignore, add the explicit list to the filtered list
166
+ comports = list(set(explicit) | set(comports))
167
+ if not bluetooth:
168
+ # filter out bluetooth ports
169
+ comports = [p for p in comports if "bluetooth" not in p.description.lower()]
170
+ comports = [p for p in comports if "BTHENUM" not in p.hwid]
171
+ if sys.platform == "darwin":
172
+ comports = [p for p in comports if ".Bluetooth" not in p.device]
173
+ log.trace(f"no Bluetooth: {[p.device for p in comports]}")
174
+ log.debug(f"filtered_comports: {[p.device for p in comports]}")
175
+ # sort
176
+ if sys.platform == "win32":
177
+ # Windows sort of comports by number - but fallback to device name
178
+ return sorted(
179
+ comports,
180
+ key=lambda x: int(x.device.split()[0][3:]) if x.device.split()[0][3:].isdigit() else x,
181
+ )
182
+ # sort by device name
183
+ return sorted(comports, key=lambda x: x.device)
184
+
185
+
186
+ def find_serial_by_path(target_port: str):
187
+ """Find the symbolic link path of a serial port by its device path."""
188
+ # sourcery skip: use-next
189
+
190
+ if os.name == "nt":
191
+ return None
192
+ # List all available serial ports
193
+ available_ports = list_ports.comports()
194
+ # Filter to get the device path of the target port
195
+ target_device_path = None
196
+ for port in available_ports:
197
+ if port.device == target_port:
198
+ target_device_path = port.device
199
+ break
200
+
201
+ if not target_device_path:
202
+ return None # Target port not found among available ports
203
+
204
+ # Search for all symbolic links in /dev/serial/by-path/
205
+ for symlink in glob.glob("/dev/serial/by-path/*"):
206
+ # Resolve the symbolic link to its target
207
+ if os.path.realpath(symlink) == target_device_path:
208
+ return symlink # Return the matching symlink path
209
+
210
+ return None # Return None if no match is found
mpflash/mpflash/config.py CHANGED
@@ -1,44 +1,44 @@
1
- """centralized configuration for mpflash"""
2
-
3
- import os
4
- from importlib.metadata import version
5
- from pathlib import Path
6
- from typing import List
7
-
8
- import platformdirs
9
-
10
- from mpflash.logger import log
11
-
12
-
13
- def get_version():
14
- name = __package__ or "mpflash"
15
- return version(name)
16
-
17
-
18
- class MPtoolConfig:
19
- """Centralized configuration for mpflash"""
20
-
21
- quiet: bool = False
22
- verbose: bool = False
23
- usb: bool = False
24
- ignore_ports: List[str] = []
25
- firmware_folder: Path = platformdirs.user_downloads_path() / "firmware"
26
- # test options specified on the commandline
27
- tests: List[str] = []
28
- _interactive: bool = True
29
-
30
- @property
31
- def interactive(self):
32
- # No interactions in CI
33
- if os.getenv("GITHUB_ACTIONS") == "true":
34
- log.warning("Disabling interactive mode in CI")
35
- return False
36
- return self._interactive
37
-
38
- @interactive.setter
39
- def interactive(self, value: bool):
40
- self._interactive = value
41
-
42
-
43
- config = MPtoolConfig()
44
- __version__ = get_version()
1
+ """centralized configuration for mpflash"""
2
+
3
+ import os
4
+ from importlib.metadata import version
5
+ from pathlib import Path
6
+ from typing import List
7
+
8
+ import platformdirs
9
+
10
+ from mpflash.logger import log
11
+
12
+
13
+ def get_version():
14
+ name = __package__ or "mpflash"
15
+ return version(name)
16
+
17
+
18
+ class MPtoolConfig:
19
+ """Centralized configuration for mpflash"""
20
+
21
+ quiet: bool = False
22
+ verbose: bool = False
23
+ usb: bool = False
24
+ ignore_ports: List[str] = []
25
+ firmware_folder: Path = platformdirs.user_downloads_path() / "firmware"
26
+ # test options specified on the commandline
27
+ tests: List[str] = []
28
+ _interactive: bool = True
29
+
30
+ @property
31
+ def interactive(self):
32
+ # No interactions in CI
33
+ if os.getenv("GITHUB_ACTIONS") == "true":
34
+ log.warning("Disabling interactive mode in CI")
35
+ return False
36
+ return self._interactive
37
+
38
+ @interactive.setter
39
+ def interactive(self, value: bool):
40
+ self._interactive = value
41
+
42
+
43
+ config = MPtoolConfig()
44
+ __version__ = get_version()
@@ -1,78 +1,96 @@
1
- from typing import List, Tuple
2
-
3
- from rich import print
4
- from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
5
- from rich.table import Column
6
-
7
- from mpflash.mpremoteboard import MPRemoteBoard
8
-
9
- from .common import filtered_comports
10
-
11
-
12
- def connected_ports_boards(*, include: List[str], ignore: List[str]) -> Tuple[List[str], List[str], List[MPRemoteBoard]]:
13
- """
14
- Returns a tuple containing lists of unique ports and boards from the connected MCUs.
15
- Boards that are physically connected, but give no tangible response are ignored.
16
-
17
- Returns:
18
- A tuple containing three lists:
19
- - A list of unique ports where MCUs are connected.
20
- - A list of unique board names of the connected MCUs.
21
- - A list of MPRemoteBoard instances of the connected MCUs.
22
- """
23
- mpr_boards = [b for b in list_mcus(include=include, ignore=ignore) if b.connected]
24
- ports = list({b.port for b in mpr_boards})
25
- boards = list({b.board for b in mpr_boards})
26
- return (ports, boards, mpr_boards)
27
-
28
-
29
- # #########################################################################################################
30
- rp_spinner = SpinnerColumn(finished_text="✅")
31
- rp_text = TextColumn("{task.description} {task.fields[device]}", table_column=Column())
32
- rp_bar = BarColumn(bar_width=None, table_column=Column())
33
-
34
-
35
- def list_mcus(*, ignore: List[str], include: List[str], bluetooth: bool = False):
36
- """
37
- Retrieves information about connected microcontroller boards.
38
-
39
- Returns:
40
- List[MPRemoteBoard]: A list of MPRemoteBoard instances with board information.
41
- Raises:
42
- ConnectionError: If there is an error connecting to a board.
43
- """
44
- # conn_mcus = [MPRemoteBoard(sp) for sp in MPRemoteBoard.connected_boards(bluetooth) if sp not in config.ignore_ports]
45
-
46
- comports = filtered_comports(
47
- ignore=ignore,
48
- include=include,
49
- bluetooth=bluetooth,
50
- )
51
- conn_mcus = [MPRemoteBoard(c.device, location=c.location or "?") for c in comports]
52
-
53
- # a lot of boilerplate to show a progress bar with the comport currently scanned
54
- # low update rate to facilitate screen readers/narration
55
- with Progress(
56
- rp_spinner,
57
- rp_text,
58
- rp_bar,
59
- TimeElapsedColumn(),
60
- refresh_per_second=1,
61
- ) as progress:
62
- tsk_scan = progress.add_task("[green]Scanning", visible=False, total=None)
63
- progress.tasks[tsk_scan].fields["device"] = "..."
64
- progress.tasks[tsk_scan].visible = True
65
- progress.start_task(tsk_scan)
66
- try:
67
- for mcu in conn_mcus:
68
- progress.update(tsk_scan, device=mcu.serialport.replace("/dev/", ""))
69
- try:
70
- mcu.get_mcu_info()
71
- except ConnectionError as e:
72
- print(f"Error: {e}")
73
- continue
74
- finally:
75
- # transient
76
- progress.stop_task(tsk_scan)
77
- progress.tasks[tsk_scan].visible = False
78
- return conn_mcus
1
+ from typing import List, Tuple
2
+
3
+ from rich import print
4
+ from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
5
+ from rich.table import Column
6
+
7
+ from mpflash.common import filtered_comports
8
+ from mpflash.mpremoteboard import MPRemoteBoard
9
+
10
+ from mpflash.common import find_serial_by_path
11
+
12
+
13
+ def connected_ports_boards(
14
+ *, include: List[str], ignore: List[str], bluetooth: bool = False
15
+ ) -> Tuple[List[str], List[str], List[MPRemoteBoard]]:
16
+ """
17
+ Returns a tuple containing lists of unique ports and boards from the connected MCUs.
18
+ Boards that are physically connected, but give no tangible response are ignored.
19
+
20
+ Returns:
21
+ A tuple containing three lists:
22
+ - A list of unique ports where MCUs are connected.
23
+ - A list of unique board names of the connected MCUs.
24
+ - A list of MPRemoteBoard instances of the connected MCUs.
25
+ """
26
+ conn_mcus = [
27
+ b for b in list_mcus(include=include, ignore=ignore, bluetooth=bluetooth) if b.connected
28
+ ]
29
+ # ignore boards that have the [micropython-stubber] ignore flag set
30
+ conn_mcus = [
31
+ item for item in conn_mcus if not (item.toml.get("mpflash", {}).get("ignore", False))
32
+ ]
33
+
34
+ ports = list({b.port for b in conn_mcus})
35
+ boards = list({b.board for b in conn_mcus})
36
+ return (ports, boards, conn_mcus)
37
+
38
+
39
+ # #########################################################################################################
40
+ rp_spinner = SpinnerColumn(finished_text="✅")
41
+ rp_text = TextColumn("{task.description} {task.fields[device]}", table_column=Column())
42
+ rp_bar = BarColumn(bar_width=None, table_column=Column())
43
+
44
+
45
+ def list_mcus(
46
+ *, ignore: List[str], include: List[str], bluetooth: bool = False
47
+ ) -> List[MPRemoteBoard]:
48
+ """
49
+ Retrieves information about connected microcontroller boards.
50
+
51
+ Returns:
52
+ List[MPRemoteBoard]: A list of MPRemoteBoard instances with board information.
53
+ Raises:
54
+ ConnectionError: If there is an error connecting to a board.
55
+ """
56
+ # conn_mcus = [MPRemoteBoard(sp) for sp in MPRemoteBoard.connected_boards(bluetooth) if sp not in config.ignore_ports]
57
+
58
+ comports = filtered_comports(
59
+ ignore=ignore,
60
+ include=include,
61
+ bluetooth=bluetooth,
62
+ )
63
+ connected_mcus = [
64
+ MPRemoteBoard(
65
+ c.device,
66
+ location=find_serial_by_path(c.device) or c.location or c.hwid or "?",
67
+ )
68
+ for c in comports
69
+ ]
70
+
71
+ # a lot of boilerplate to show a progress bar with the comport currently scanned
72
+ # low update rate to facilitate screen readers/narration
73
+ with Progress(
74
+ rp_spinner,
75
+ rp_text,
76
+ rp_bar,
77
+ TimeElapsedColumn(),
78
+ refresh_per_second=1,
79
+ ) as progress:
80
+ tsk_scan = progress.add_task("[green]Scanning", visible=False, total=None)
81
+ progress.tasks[tsk_scan].fields["device"] = "..."
82
+ progress.tasks[tsk_scan].visible = True
83
+ progress.start_task(tsk_scan)
84
+ try:
85
+ for mcu in connected_mcus:
86
+ progress.update(tsk_scan, device=mcu.serialport.replace("/dev/tty", "tty"))
87
+ try:
88
+ mcu.get_mcu_info()
89
+ except ConnectionError as e:
90
+ print(f"Error: {e}")
91
+ continue
92
+ finally:
93
+ # transient
94
+ progress.stop_task(tsk_scan)
95
+ progress.tasks[tsk_scan].visible = False
96
+ return connected_mcus