micropython-stubber 1.20.4__py3-none-any.whl → 1.20.6__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 (152) hide show
  1. {micropython_stubber-1.20.4.dist-info → micropython_stubber-1.20.6.dist-info}/LICENSE +30 -30
  2. {micropython_stubber-1.20.4.dist-info → micropython_stubber-1.20.6.dist-info}/METADATA +4 -4
  3. micropython_stubber-1.20.6.dist-info/RECORD +159 -0
  4. mpflash/README.md +184 -184
  5. mpflash/libusb_flash.ipynb +203 -203
  6. mpflash/mpflash/add_firmware.py +98 -98
  7. mpflash/mpflash/ask_input.py +236 -226
  8. mpflash/mpflash/bootloader/__init__.py +37 -0
  9. mpflash/mpflash/bootloader/manual.py +102 -0
  10. mpflash/mpflash/bootloader/micropython.py +10 -0
  11. mpflash/mpflash/bootloader/touch1200.py +45 -0
  12. mpflash/mpflash/cli_download.py +129 -128
  13. mpflash/mpflash/cli_flash.py +219 -212
  14. mpflash/mpflash/cli_group.py +98 -92
  15. mpflash/mpflash/cli_list.py +81 -77
  16. mpflash/mpflash/cli_main.py +41 -38
  17. mpflash/mpflash/common.py +164 -151
  18. mpflash/mpflash/config.py +47 -31
  19. mpflash/mpflash/connected.py +74 -74
  20. mpflash/mpflash/download.py +360 -361
  21. mpflash/mpflash/downloaded.py +129 -129
  22. mpflash/mpflash/errors.py +9 -5
  23. mpflash/mpflash/flash.py +52 -69
  24. mpflash/mpflash/flash_esp.py +59 -59
  25. mpflash/mpflash/flash_stm32.py +24 -24
  26. mpflash/mpflash/flash_stm32_cube.py +111 -111
  27. mpflash/mpflash/flash_stm32_dfu.py +101 -101
  28. mpflash/mpflash/flash_uf2.py +67 -67
  29. mpflash/mpflash/flash_uf2_boardid.py +15 -15
  30. mpflash/mpflash/flash_uf2_linux.py +123 -123
  31. mpflash/mpflash/flash_uf2_macos.py +34 -37
  32. mpflash/mpflash/flash_uf2_windows.py +34 -34
  33. mpflash/mpflash/list.py +89 -89
  34. mpflash/mpflash/logger.py +41 -41
  35. mpflash/mpflash/mpboard_id/__init__.py +93 -93
  36. mpflash/mpflash/mpboard_id/add_boards.py +255 -255
  37. mpflash/mpflash/mpboard_id/board.py +37 -37
  38. mpflash/mpflash/mpboard_id/board_id.py +86 -86
  39. mpflash/mpflash/mpboard_id/store.py +43 -43
  40. mpflash/mpflash/mpremoteboard/__init__.py +221 -221
  41. mpflash/mpflash/mpremoteboard/mpy_fw_info.py +141 -141
  42. mpflash/mpflash/mpremoteboard/runner.py +140 -140
  43. mpflash/mpflash/uf2disk.py +12 -12
  44. mpflash/mpflash/vendor/basicgit.py +288 -288
  45. mpflash/mpflash/vendor/click_aliases.py +91 -0
  46. mpflash/mpflash/vendor/dfu.py +165 -165
  47. mpflash/mpflash/vendor/pydfu.py +605 -605
  48. mpflash/mpflash/vendor/readme.md +2 -2
  49. mpflash/mpflash/vendor/versions.py +119 -117
  50. mpflash/mpflash/worklist.py +170 -170
  51. mpflash/poetry.lock +1588 -1623
  52. mpflash/pyproject.toml +60 -60
  53. mpflash/stm32_udev_rules.md +62 -62
  54. stubber/__init__.py +3 -3
  55. stubber/basicgit.py +294 -288
  56. stubber/board/board_info.csv +193 -193
  57. stubber/board/boot.py +34 -34
  58. stubber/board/createstubs.py +986 -987
  59. stubber/board/createstubs_db.py +825 -826
  60. stubber/board/createstubs_db_min.py +331 -331
  61. stubber/board/createstubs_db_mpy.mpy +0 -0
  62. stubber/board/createstubs_lvgl.py +741 -741
  63. stubber/board/createstubs_lvgl_min.py +741 -741
  64. stubber/board/createstubs_mem.py +766 -767
  65. stubber/board/createstubs_mem_min.py +306 -306
  66. stubber/board/createstubs_mem_mpy.mpy +0 -0
  67. stubber/board/createstubs_min.py +294 -294
  68. stubber/board/createstubs_mpy.mpy +0 -0
  69. stubber/board/fw_info.py +141 -141
  70. stubber/board/info.py +183 -183
  71. stubber/board/main.py +19 -19
  72. stubber/board/modulelist.txt +247 -247
  73. stubber/board/pyrightconfig.json +34 -34
  74. stubber/bulk/mcu_stubber.py +454 -455
  75. stubber/codemod/_partials/__init__.py +48 -50
  76. stubber/codemod/_partials/db_main.py +147 -147
  77. stubber/codemod/_partials/lvgl_main.py +77 -77
  78. stubber/codemod/_partials/modules_reader.py +80 -80
  79. stubber/codemod/add_comment.py +53 -53
  80. stubber/codemod/add_method.py +65 -65
  81. stubber/codemod/board.py +317 -317
  82. stubber/codemod/enrich.py +145 -145
  83. stubber/codemod/merge_docstub.py +284 -284
  84. stubber/codemod/modify_list.py +54 -54
  85. stubber/codemod/utils.py +57 -57
  86. stubber/commands/build_cmd.py +94 -94
  87. stubber/commands/cli.py +51 -51
  88. stubber/commands/clone_cmd.py +66 -66
  89. stubber/commands/config_cmd.py +29 -29
  90. stubber/commands/enrich_folder_cmd.py +70 -70
  91. stubber/commands/get_core_cmd.py +69 -69
  92. stubber/commands/get_docstubs_cmd.py +87 -87
  93. stubber/commands/get_frozen_cmd.py +112 -112
  94. stubber/commands/get_mcu_cmd.py +56 -56
  95. stubber/commands/merge_cmd.py +66 -66
  96. stubber/commands/publish_cmd.py +119 -119
  97. stubber/commands/stub_cmd.py +30 -30
  98. stubber/commands/switch_cmd.py +54 -54
  99. stubber/commands/variants_cmd.py +48 -48
  100. stubber/cst_transformer.py +178 -178
  101. stubber/data/board_info.csv +193 -193
  102. stubber/data/board_info.json +1729 -1729
  103. stubber/data/micropython_tags.csv +15 -15
  104. stubber/data/requirements-core-micropython.txt +38 -38
  105. stubber/data/requirements-core-pycopy.txt +39 -39
  106. stubber/downloader.py +36 -36
  107. stubber/freeze/common.py +68 -68
  108. stubber/freeze/freeze_folder.py +69 -69
  109. stubber/freeze/freeze_manifest_2.py +113 -113
  110. stubber/freeze/get_frozen.py +127 -127
  111. stubber/get_cpython.py +101 -101
  112. stubber/get_lobo.py +59 -59
  113. stubber/minify.py +418 -418
  114. stubber/publish/bump.py +86 -86
  115. stubber/publish/candidates.py +262 -283
  116. stubber/publish/database.py +18 -18
  117. stubber/publish/defaults.py +45 -45
  118. stubber/publish/enums.py +24 -30
  119. stubber/publish/helpers.py +29 -29
  120. stubber/publish/merge_docstubs.py +130 -130
  121. stubber/publish/missing_class_methods.py +49 -49
  122. stubber/publish/package.py +146 -177
  123. stubber/publish/pathnames.py +51 -51
  124. stubber/publish/publish.py +120 -121
  125. stubber/publish/pypi.py +38 -38
  126. stubber/publish/stubpackage.py +1029 -1029
  127. stubber/rst/__init__.py +9 -9
  128. stubber/rst/classsort.py +77 -77
  129. stubber/rst/lookup.py +530 -530
  130. stubber/rst/output_dict.py +401 -401
  131. stubber/rst/reader.py +822 -823
  132. stubber/rst/report_return.py +69 -69
  133. stubber/rst/rst_utils.py +540 -540
  134. stubber/stubber.py +38 -38
  135. stubber/stubs_from_docs.py +90 -90
  136. stubber/tools/manifestfile.py +610 -610
  137. stubber/tools/readme.md +5 -5
  138. stubber/update_fallback.py +117 -117
  139. stubber/update_module_list.py +123 -125
  140. stubber/utils/__init__.py +5 -5
  141. stubber/utils/config.py +127 -127
  142. stubber/utils/makeversionhdr.py +54 -54
  143. stubber/utils/manifest.py +92 -92
  144. stubber/utils/post.py +79 -79
  145. stubber/utils/repos.py +157 -154
  146. stubber/utils/stubmaker.py +139 -139
  147. stubber/utils/typed_config_toml.py +77 -77
  148. stubber/utils/versions.py +128 -120
  149. stubber/variants.py +106 -106
  150. micropython_stubber-1.20.4.dist-info/RECORD +0 -154
  151. {micropython_stubber-1.20.4.dist-info → micropython_stubber-1.20.6.dist-info}/WHEEL +0 -0
  152. {micropython_stubber-1.20.4.dist-info → micropython_stubber-1.20.6.dist-info}/entry_points.txt +0 -0
@@ -1,77 +1,81 @@
1
- import json
2
- from typing import List
3
-
4
- import rich_click as click
5
- from rich import print
6
-
7
- from .cli_group import cli
8
- from .list import show_mcus
9
- from .logger import make_quiet
10
- from .connected import list_mcus
11
-
12
-
13
- @cli.command("list", help="List the connected MCU boards.")
14
- @click.option(
15
- "--json",
16
- "-j",
17
- "as_json",
18
- is_flag=True,
19
- default=False,
20
- show_default=True,
21
- help="""Output in json format""",
22
- )
23
- @click.option(
24
- "--serial",
25
- "--serial-port",
26
- "-s",
27
- "serial",
28
- default=["*"],
29
- multiple=True,
30
- show_default=True,
31
- help="Which serial port(s) to list. ",
32
- metavar="SERIALPORT",
33
- )
34
- @click.option(
35
- "--ignore",
36
- "-i",
37
- is_eager=True,
38
- help="Serial port(s) to ignore. Defaults to MPFLASH_IGNORE.",
39
- multiple=True,
40
- default=[],
41
- envvar="MPFLASH_IGNORE",
42
- show_default=True,
43
- metavar="SERIALPORT",
44
- )
45
- @click.option(
46
- "--bluetooth/--no-bluetooth",
47
- "-b/-nb",
48
- is_flag=True,
49
- default=False,
50
- show_default=True,
51
- help="""Include bluetooth ports in the list""",
52
- )
53
- @click.option(
54
- "--progress/--no-progress",
55
- # "-p/-np", -p is already used for --port
56
- "progress",
57
- is_flag=True,
58
- default=True,
59
- show_default=True,
60
- help="""Show progress""",
61
- )
62
- def cli_list_mcus(serial: List[str], ignore: List[str], bluetooth: bool, as_json: bool, progress: bool = True) -> int:
63
- """List the connected MCU boards, and output in a nice table or json."""
64
- serial = list(serial)
65
- ignore = list(ignore)
66
- if as_json:
67
- # avoid noise in json output
68
- make_quiet()
69
- # TODO? Ask user to select a serialport if [?] is given ?
70
-
71
- conn_mcus = list_mcus(ignore=ignore, include=serial, bluetooth=bluetooth)
72
- if as_json:
73
- print(json.dumps([mcu.__dict__ for mcu in conn_mcus], indent=4))
74
- progress = False
75
- if progress:
76
- show_mcus(conn_mcus, refresh=False)
77
- return 0 if conn_mcus else 1
1
+ import json
2
+ from typing import List
3
+
4
+ import rich_click as click
5
+ from rich import print
6
+
7
+ from .cli_group import cli
8
+ from .connected import list_mcus
9
+ from .list import show_mcus
10
+ from .logger import make_quiet
11
+
12
+
13
+ @cli.command(
14
+ "list",
15
+ help="List the connected MCU boards. alias: devs",
16
+ aliases=["devs"],
17
+ )
18
+ @click.option(
19
+ "--json",
20
+ "-j",
21
+ "as_json",
22
+ is_flag=True,
23
+ default=False,
24
+ show_default=True,
25
+ help="""Output in json format""",
26
+ )
27
+ @click.option(
28
+ "--serial",
29
+ "--serial-port",
30
+ "-s",
31
+ "serial",
32
+ default=["*"],
33
+ multiple=True,
34
+ show_default=True,
35
+ help="Which serial port(s) to list. ",
36
+ metavar="SERIALPORT",
37
+ )
38
+ @click.option(
39
+ "--ignore",
40
+ "-i",
41
+ is_eager=True,
42
+ help="Serial port(s) to ignore. Defaults to MPFLASH_IGNORE.",
43
+ multiple=True,
44
+ default=[],
45
+ envvar="MPFLASH_IGNORE",
46
+ show_default=True,
47
+ metavar="SERIALPORT",
48
+ )
49
+ @click.option(
50
+ "--bluetooth/--no-bluetooth",
51
+ "-b/-nb",
52
+ is_flag=True,
53
+ default=False,
54
+ show_default=True,
55
+ help="""Include bluetooth ports in the list""",
56
+ )
57
+ @click.option(
58
+ "--progress/--no-progress",
59
+ # "-p/-np", -p is already used for --port
60
+ "progress",
61
+ is_flag=True,
62
+ default=True,
63
+ show_default=True,
64
+ help="""Show progress""",
65
+ )
66
+ def cli_list_mcus(serial: List[str], ignore: List[str], bluetooth: bool, as_json: bool, progress: bool = True) -> int:
67
+ """List the connected MCU boards, and output in a nice table or json."""
68
+ serial = list(serial)
69
+ ignore = list(ignore)
70
+ if as_json:
71
+ # avoid noise in json output
72
+ make_quiet()
73
+ # TODO? Ask user to select a serialport if [?] is given ?
74
+
75
+ conn_mcus = list_mcus(ignore=ignore, include=serial, bluetooth=bluetooth)
76
+ if as_json:
77
+ print(json.dumps([mcu.__dict__ for mcu in conn_mcus], indent=4))
78
+ progress = False
79
+ if progress:
80
+ show_mcus(conn_mcus, refresh=False)
81
+ return 0 if conn_mcus else 1
@@ -1,38 +1,41 @@
1
- """mpflash is a CLI to download and flash MicroPython firmware to various boards."""
2
-
3
- # import rich_click as click
4
-
5
- import os
6
-
7
- import click
8
- from loguru import logger as log
9
-
10
- from .cli_download import cli_download
11
- from .cli_flash import cli_flash_board
12
- from .cli_group import cli
13
- from .cli_list import cli_list_mcus
14
-
15
-
16
- def mpflash():
17
- cli.add_command(cli_list_mcus)
18
- cli.add_command(cli_download)
19
- cli.add_command(cli_flash_board)
20
-
21
- # cli(auto_envvar_prefix="MPFLASH")
22
- if False and os.environ.get("COMPUTERNAME") == "JOSVERL-S4":
23
- # intentional less error suppression on dev machine
24
- result = cli(standalone_mode=False)
25
- else:
26
- try:
27
- result = cli(standalone_mode=False)
28
- exit(result)
29
- except AttributeError as e:
30
- log.error(f"Error: {e}")
31
- exit(-1)
32
- except click.exceptions.ClickException as e:
33
- log.error(f"Error: {e}")
34
- exit(-2)
35
-
36
-
37
- if __name__ == "__main__":
38
- mpflash()
1
+ """mpflash is a CLI to download and flash MicroPython firmware to various boards."""
2
+
3
+ # import rich_click as click
4
+
5
+ import os
6
+
7
+ import click
8
+ from loguru import logger as log
9
+
10
+ from .cli_download import cli_download
11
+ from .cli_flash import cli_flash_board
12
+ from .cli_group import cli
13
+ from .cli_list import cli_list_mcus
14
+
15
+
16
+ def mpflash():
17
+ cli.add_command(cli_list_mcus)
18
+ cli.add_command(cli_download)
19
+ cli.add_command(cli_flash_board)
20
+
21
+ # cli(auto_envvar_prefix="MPFLASH")
22
+ if False and os.environ.get("COMPUTERNAME").startswith("JOSVERL"):
23
+ # intentional less error suppression on dev machine
24
+ result = cli(standalone_mode=False)
25
+ else:
26
+ try:
27
+ result = cli(standalone_mode=True)
28
+ exit(result)
29
+ except AttributeError as e:
30
+ log.error(f"Error: {e}")
31
+ exit(-1)
32
+ except click.exceptions.ClickException as e:
33
+ log.error(f"Error: {e}")
34
+ exit(-2)
35
+ except click.exceptions.Abort as e:
36
+ # Aborted - Ctrl-C
37
+ exit(-3)
38
+
39
+
40
+ if __name__ == "__main__":
41
+ mpflash()
mpflash/mpflash/common.py CHANGED
@@ -1,151 +1,164 @@
1
- import fnmatch
2
- import os
3
- import sys
4
- from dataclasses import dataclass, field
5
- from pathlib import Path
6
- from typing import List, Optional, Union
7
-
8
- from github import Auth, Github
9
- from serial.tools import list_ports
10
- from serial.tools.list_ports_common import ListPortInfo
11
-
12
- from .logger import log
13
-
14
- # from mpflash.mpremoteboard import MPRemoteBoard
15
-
16
- PORT_FWTYPES = {
17
- "stm32": [".dfu"], # need .dfu for pydfu.py - .hex for cube cli/GUI
18
- "esp32": [".bin"],
19
- "esp8266": [".bin"],
20
- "rp2": [".uf2"],
21
- "samd": [".uf2"],
22
- # below this not yet implemented / tested
23
- "mimxrt": [".hex"],
24
- "nrf": [".uf2"],
25
- "renesas-ra": [".hex"],
26
- }
27
-
28
- # Token with no permissions to avoid throttling
29
- # 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
30
- PAT_NO_ACCESS = (
31
- "github_pat" + "_11AAHPVFQ0qAkDnSUaMKSp" + "_ZkDl5NRRwBsUN6EYg9ahp1Dvj4FDDONnXVgimxC2EtpY7Q7BUKBoQ0Jq72X"
32
- )
33
- PAT = os.environ.get("GITHUB_TOKEN") or PAT_NO_ACCESS
34
- GH_CLIENT = Github(auth=Auth.Token(PAT))
35
-
36
-
37
- @dataclass
38
- class FWInfo:
39
- """
40
- Downloaded Firmware information
41
- is somewhat related to the BOARD class in the mpboard_id module
42
- """
43
-
44
- port: str # MicroPython port
45
- board: str # MicroPython board
46
- filename: str = field(default="") # relative filename of the firmware image
47
- firmware: str = field(default="") # url or path to original firmware image
48
- variant: str = field(default="") # MicroPython variant
49
- preview: bool = field(default=False) # True if the firmware is a preview version
50
- version: str = field(default="") # MicroPython version (NO v prefix)
51
- url: str = field(default="") # url to the firmware image download folder
52
- build: str = field(default="0") # The build = number of commits since the last release
53
- ext: str = field(default="") # the file extension of the firmware
54
- family: str = field(default="micropython") # The family of the firmware
55
- custom: bool = field(default=False) # True if the firmware is a custom build
56
- description: str = field(default="") # Description used by this firmware (custom only)
57
-
58
- def to_dict(self) -> dict:
59
- """Convert the object to a dictionary"""
60
- return self.__dict__
61
-
62
- @classmethod
63
- def from_dict(cls, data: dict) -> "FWInfo":
64
- """Create a FWInfo object from a dictionary"""
65
- # add missing keys
66
- if "ext" not in data:
67
- data["ext"] = Path(data["firmware"]).suffix
68
- if "family" not in data:
69
- data["family"] = "micropython"
70
- return cls(**data)
71
-
72
-
73
- @dataclass
74
- class Params:
75
- """Common parameters for downloading and flashing firmware"""
76
-
77
- ports: List[str] = field(default_factory=list)
78
- boards: List[str] = field(default_factory=list)
79
- versions: List[str] = field(default_factory=list)
80
- fw_folder: Path = Path()
81
- serial: List[str] = field(default_factory=list)
82
- ignore: List[str] = field(default_factory=list)
83
-
84
-
85
- @dataclass
86
- class DownloadParams(Params):
87
- """Parameters for downloading firmware"""
88
-
89
- clean: bool = False
90
- force: bool = False
91
-
92
-
93
- @dataclass
94
- class FlashParams(Params):
95
- """Parameters for flashing a board"""
96
-
97
- erase: bool = True
98
- bootloader: bool = True
99
- cpu: str = ""
100
-
101
-
102
- ParamType = Union[DownloadParams, FlashParams]
103
-
104
-
105
- def filtered_comports(
106
- ignore: Optional[List[str]] = None,
107
- include: Optional[List[str]] = None,
108
- bluetooth: bool = False,
109
- ) -> List[ListPortInfo]: # sourcery skip: assign-if-exp
110
- """
111
- Get a list of filtered comports.
112
- """
113
- if not ignore:
114
- ignore = []
115
- elif not isinstance(ignore, list): # type: ignore
116
- ignore = list(ignore)
117
- if not include:
118
- include = ["*"]
119
- elif not isinstance(include, list): # type: ignore
120
- include = list(include)
121
-
122
- # remove ports that are to be ignored
123
- log.trace(f"{include=}, {ignore=}, {bluetooth=}")
124
- comports = [p for p in list_ports.comports() if not any(fnmatch.fnmatch(p.device, i) for i in ignore)]
125
- log.trace(f"comports: {[p.device for p in comports]}")
126
- # remove bluetooth ports
127
-
128
- if include != ["*"]:
129
- # if there are explicit ports to include, add them to the list
130
- explicit = [p for p in list_ports.comports() if any(fnmatch.fnmatch(p.device, i) for i in include)]
131
- log.trace(f"explicit: {[p.device for p in explicit]}")
132
- if ignore == []:
133
- # if nothing to ignore, just use the explicit list as a sinple sane default
134
- comports = explicit
135
- else:
136
- # if there are ports to ignore, add the explicit list to the filtered list
137
- comports = list(set(explicit) | set(comports))
138
- if not bluetooth:
139
- # filter out bluetooth ports
140
- comports = [p for p in comports if "bluetooth" not in p.description.lower()]
141
- comports = [p for p in comports if "BTHENUM" not in p.hwid]
142
- if sys.platform == "darwin":
143
- comports = [p for p in comports if ".Bluetooth" not in p.device]
144
- log.trace(f"no Bluetooth: {[p.device for p in comports]}")
145
- log.debug(f"filtered_comports: {[p.device for p in comports]}")
146
- # sort
147
- if sys.platform == "win32":
148
- # Windows sort of comports by number - but fallback to device name
149
- return sorted(comports, key=lambda x: int(x.device.split()[0][3:]) if x.device.split()[0][3:].isdigit() else x)
150
- # sort by device name
151
- return sorted(comports, key=lambda x: x.device)
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
+ MANUAL = "manual"
96
+ MPY = "mpy"
97
+ TOUCH_1200 = "touch1200"
98
+ NONE = "none"
99
+
100
+
101
+
102
+ @dataclass
103
+ class FlashParams(Params):
104
+ """Parameters for flashing a board"""
105
+
106
+ erase: bool = True
107
+ bootloader: BootloaderMethod = BootloaderMethod.NONE
108
+ cpu: str = ""
109
+
110
+ def __post_init__(self):
111
+ if isinstance(self.bootloader, str):
112
+ self.bootloader = BootloaderMethod(self.bootloader)
113
+
114
+
115
+ ParamType = Union[DownloadParams, FlashParams]
116
+
117
+
118
+ def filtered_comports(
119
+ ignore: Optional[List[str]] = None,
120
+ include: Optional[List[str]] = None,
121
+ bluetooth: bool = False,
122
+ ) -> List[ListPortInfo]: # sourcery skip: assign-if-exp
123
+ """
124
+ Get a list of filtered comports.
125
+ """
126
+ if not ignore:
127
+ ignore = []
128
+ elif not isinstance(ignore, list): # type: ignore
129
+ ignore = list(ignore)
130
+ if not include:
131
+ include = ["*"]
132
+ elif not isinstance(include, list): # type: ignore
133
+ include = list(include)
134
+
135
+ # remove ports that are to be ignored
136
+ log.trace(f"{include=}, {ignore=}, {bluetooth=}")
137
+ comports = [p for p in list_ports.comports() if not any(fnmatch.fnmatch(p.device, i) for i in ignore)]
138
+ log.trace(f"comports: {[p.device for p in comports]}")
139
+ # remove bluetooth ports
140
+
141
+ if include != ["*"]:
142
+ # if there are explicit ports to include, add them to the list
143
+ explicit = [p for p in list_ports.comports() if any(fnmatch.fnmatch(p.device, i) for i in include)]
144
+ log.trace(f"explicit: {[p.device for p in explicit]}")
145
+ if ignore == []:
146
+ # if nothing to ignore, just use the explicit list as a sinple sane default
147
+ comports = explicit
148
+ else:
149
+ # if there are ports to ignore, add the explicit list to the filtered list
150
+ comports = list(set(explicit) | set(comports))
151
+ if not bluetooth:
152
+ # filter out bluetooth ports
153
+ comports = [p for p in comports if "bluetooth" not in p.description.lower()]
154
+ comports = [p for p in comports if "BTHENUM" not in p.hwid]
155
+ if sys.platform == "darwin":
156
+ comports = [p for p in comports if ".Bluetooth" not in p.device]
157
+ log.trace(f"no Bluetooth: {[p.device for p in comports]}")
158
+ log.debug(f"filtered_comports: {[p.device for p in comports]}")
159
+ # sort
160
+ if sys.platform == "win32":
161
+ # Windows sort of comports by number - but fallback to device name
162
+ return sorted(comports, key=lambda x: int(x.device.split()[0][3:]) if x.device.split()[0][3:].isdigit() else x)
163
+ # sort by device name
164
+ return sorted(comports, key=lambda x: x.device)
mpflash/mpflash/config.py CHANGED
@@ -1,31 +1,47 @@
1
- """centralized configuration for mpflash"""
2
-
3
- from pathlib import Path
4
- from typing import List
5
-
6
- import pkg_resources
7
- import platformdirs
8
-
9
-
10
- def get_version():
11
- name = __package__ or "mpflash"
12
- try:
13
- return pkg_resources.get_distribution(name).version
14
- except pkg_resources.DistributionNotFound:
15
- return "Package not found"
16
-
17
-
18
- class MPtoolConfig:
19
- """Centralized configuration for mpflash"""
20
-
21
- quiet: bool = False
22
- verbose: bool = False
23
- ignore_ports: List[str] = []
24
- interactive: bool = True
25
- firmware_folder: Path = platformdirs.user_downloads_path() / "firmware"
26
- # test options specified on the commandline
27
- tests: List[str] = []
28
-
29
-
30
- config = MPtoolConfig()
31
- __version__ = get_version()
1
+ """centralized configuration for mpflash"""
2
+
3
+ import os
4
+ from pathlib import Path
5
+ from typing import List
6
+
7
+ import pkg_resources
8
+ import platformdirs
9
+
10
+ from mpflash.logger import log
11
+
12
+
13
+ def get_version():
14
+ name = __package__ or "mpflash"
15
+ try:
16
+ return pkg_resources.get_distribution(name).version
17
+ except pkg_resources.DistributionNotFound:
18
+ return "Package not found"
19
+
20
+
21
+ class MPtoolConfig:
22
+ """Centralized configuration for mpflash"""
23
+
24
+ quiet: bool = False
25
+ verbose: bool = False
26
+ ignore_ports: List[str] = []
27
+ firmware_folder: Path = platformdirs.user_downloads_path() / "firmware"
28
+ # test options specified on the commandline
29
+ tests: List[str] = []
30
+ _interactive: bool = True
31
+
32
+ @property
33
+ def interactive(self):
34
+ # No interactions in CI
35
+ if os.getenv('GITHUB_ACTIONS') == 'true':
36
+ log.warning("Disabling interactive mode in CI")
37
+ return False
38
+ return self._interactive
39
+
40
+ @interactive.setter
41
+ def interactive(self, value:bool):
42
+ self._interactive = value
43
+
44
+
45
+
46
+ config = MPtoolConfig()
47
+ __version__ = get_version()