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,288 +1,288 @@
1
- """
2
- Simple Git module, where needed via powershell
3
-
4
- Some of the functions are based on the PyGithub module
5
- """
6
-
7
- import os
8
- import subprocess
9
- from pathlib import Path
10
- from typing import List, Optional, Union
11
-
12
- import cachetools.func
13
- from github import Auth, Github
14
- from loguru import logger as log
15
- from packaging.version import parse
16
-
17
- # from stubber.utils.versions import SET_PREVIEW
18
-
19
- # Token with no permissions to avoid throttling
20
- # 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
21
- PAT_NO_ACCESS = (
22
- "github_pat" + "_11AAHPVFQ0qAkDnSUaMKSp" + "_ZkDl5NRRwBsUN6EYg9ahp1Dvj4FDDONnXVgimxC2EtpY7Q7BUKBoQ0Jq72X"
23
- )
24
- PAT = os.environ.get("GITHUB_TOKEN") or PAT_NO_ACCESS
25
- GH_CLIENT = Github(auth=Auth.Token(PAT))
26
-
27
-
28
- def _run_local_git(
29
- cmd: List[str],
30
- repo: Optional[Union[Path, str]] = None,
31
- expect_stderr=False,
32
- capture_output=True,
33
- echo_output=True,
34
- ):
35
- "run a external (git) command in the repo's folder and deal with some of the errors"
36
- try:
37
- if repo:
38
- if isinstance(repo, str):
39
- repo = Path(repo)
40
- result = subprocess.run(
41
- cmd, capture_output=capture_output, check=True, cwd=repo.absolute().as_posix(), encoding="utf-8"
42
- )
43
- else:
44
- result = subprocess.run(cmd, capture_output=capture_output, check=True, encoding="utf-8")
45
- except (NotADirectoryError, FileNotFoundError) as e: # pragma: no cover
46
- return None
47
- except subprocess.CalledProcessError as e: # pragma: no cover
48
- # add some logging for github actions
49
- log.error(f"{str(e)} : { e.stderr}")
50
- return None
51
- if result.stderr and result.stderr != b"":
52
- stderr = result.stderr
53
- if "cloning into" in stderr.lower():
54
- # log.info(stderr)
55
- expect_stderr = True
56
- if "warning" in stderr.lower():
57
- log.warning(stderr)
58
- expect_stderr = True
59
- elif capture_output and echo_output: # pragma: no cover
60
- log.info(stderr)
61
- if not expect_stderr:
62
- raise ChildProcessError(stderr)
63
-
64
- if result.returncode and result.returncode < 0:
65
- raise ChildProcessError(result.stderr)
66
- return result
67
-
68
-
69
- def clone(remote_repo: str, path: Path, shallow: bool = False, tag: Optional[str] = None) -> bool:
70
- """git clone [--depth 1] [--branch <tag_name>] <remote> <directory>"""
71
- cmd = ["git", "clone"]
72
- if shallow:
73
- cmd += ["--depth", "1"]
74
- if tag in {"preview", "latest", "master"}:
75
- tag = None
76
- cmd += [remote_repo, "--branch", tag, str(path)] if tag else [remote_repo, str(path)]
77
- if result := _run_local_git(cmd, expect_stderr=True, capture_output=False):
78
- return result.returncode == 0
79
- else:
80
- return False
81
-
82
-
83
- def get_local_tag(repo: Optional[Union[str, Path]] = None, abbreviate: bool = True) -> Union[str, None]:
84
- """
85
- get the most recent git version tag of a local repo
86
- repo Path should be in the form of : repo = "./repo/micropython"
87
-
88
- returns the tag or None
89
- """
90
- if not repo:
91
- repo = Path(".")
92
- elif isinstance(repo, str):
93
- repo = Path(repo)
94
-
95
- result = _run_local_git(
96
- # ["git", "describe", "--tags"],
97
- ["git", "describe", "--tags", "--dirty", "--always", "--match", "v[1-9].*"],
98
- repo=repo.as_posix(),
99
- expect_stderr=True,
100
- )
101
- if not result:
102
- return None
103
- tag: str = result.stdout
104
- tag = tag.replace("\r", "").replace("\n", "")
105
- if not abbreviate or "-" not in tag:
106
- return tag
107
- if "-preview" in tag:
108
- tag = tag.split("-preview")[0] + "-preview"
109
- return tag
110
-
111
-
112
- def get_local_tags(repo: Optional[Path] = None, minver: Optional[str] = None) -> List[str]:
113
- """
114
- get list of all tags of a local repo
115
- """
116
- if not repo:
117
- repo = Path(".")
118
-
119
- result = _run_local_git(["git", "tag", "-l"], repo=repo.as_posix(), expect_stderr=True)
120
- if not result or result.returncode != 0:
121
- return []
122
- tags = result.stdout.replace("\r", "").split("\n")
123
- tags = [tag for tag in tags if tag.startswith("v")]
124
- if minver:
125
- tags = [tag for tag in tags if parse(tag) >= parse(minver)]
126
- return sorted(tags)
127
-
128
-
129
- @cachetools.func.ttl_cache(maxsize=16, ttl=60) # 60 seconds
130
- def get_tags(repo: str, minver: Optional[str] = None) -> List[str]:
131
- """
132
- Get list of tag of a repote github repo
133
- """
134
- if not repo or not isinstance(repo, str) or "/" not in repo: # type: ignore
135
- return []
136
- try:
137
- gh_repo = GH_CLIENT.get_repo(repo)
138
- except ConnectionError as e:
139
- # TODO: unable to capture the exeption
140
- log.warning(f"Unable to get tags - {e}")
141
- return []
142
- tags = [tag.name for tag in gh_repo.get_tags()]
143
- if minver:
144
- tags = [tag for tag in tags if parse(tag) >= parse(minver)]
145
- return sorted(tags)
146
-
147
-
148
- def checkout_tag(tag: str, repo: Optional[Union[str, Path]] = None) -> bool:
149
- """
150
- checkout a specific git tag
151
- """
152
- cmd = ["git", "checkout", tag, "--quiet", "--force"]
153
- result = _run_local_git(cmd, repo=repo, expect_stderr=True, capture_output=True)
154
- if not result:
155
- return False
156
- # actually a good result
157
- msg = {result.stdout}
158
- if msg != {""}:
159
- log.warning(f"git message: {msg}")
160
- return True
161
-
162
-
163
- def sync_submodules(repo: Optional[Union[Path, str]] = None) -> bool:
164
- """
165
- make sure any submodules are in sync
166
- """
167
- cmds = [
168
- ["git", "submodule", "sync", "--quiet"],
169
- # ["git", "submodule", "update", "--quiet"],
170
- ["git", "submodule", "update", "--init", "lib/micropython-lib"],
171
- ]
172
- for cmd in cmds:
173
- if result := _run_local_git(cmd, repo=repo, expect_stderr=True):
174
- # actually a good result
175
- log.debug(result.stderr)
176
- else:
177
- return False
178
- return True
179
-
180
-
181
- def checkout_commit(commit_hash: str, repo: Optional[Union[Path, str]] = None) -> bool:
182
- """
183
- Checkout a specific commit
184
- """
185
- cmd = ["git", "checkout", commit_hash, "--quiet", "--force"]
186
- result = _run_local_git(cmd, repo=repo, expect_stderr=True)
187
- if not result:
188
- return False
189
- # actually a good result
190
- log.debug(result.stderr)
191
- return True
192
-
193
-
194
- def switch_tag(tag: Union[str, Path], repo: Optional[Union[Path, str]] = None) -> bool:
195
- """
196
- switch to the specified version tag of a local repo
197
- repo should be in the form of : path/.git
198
- repo = '../micropython/.git'
199
- returns None
200
- """
201
-
202
- cmd = ["git", "switch", "--detach", tag, "--quiet", "--force"]
203
- result = _run_local_git(cmd, repo=repo, expect_stderr=True)
204
- if not result:
205
- return False
206
- # actually a good result
207
- log.debug(result.stderr)
208
- return True
209
-
210
-
211
- def switch_branch(branch: str, repo: Optional[Union[Path, str]] = None) -> bool:
212
- """
213
- switch to the specified branch in a local repo"
214
- repo should be in the form of : path/.git
215
- repo = '../micropython/.git'
216
- returns None
217
- """
218
- cmd = ["git", "switch", branch, "--quiet", "--force"]
219
- result = _run_local_git(cmd, repo=repo, expect_stderr=True)
220
- if not result:
221
- return False
222
- # actually a good result
223
- log.debug(result.stderr)
224
- return True
225
-
226
-
227
- def fetch(repo: Union[Path, str]) -> bool:
228
- """
229
- fetches a repo
230
- repo should be in the form of : path/.git
231
- repo = '../micropython/.git'
232
- returns True on success
233
- """
234
- if not repo:
235
- raise NotADirectoryError
236
-
237
- cmd = ["git", "fetch", "--all", "--tags", "--quiet"]
238
- result = _run_local_git(cmd, repo=repo, echo_output=False)
239
- return result.returncode == 0 if result else False
240
-
241
-
242
- def pull(repo: Union[Path, str], branch: str = "main") -> bool:
243
- """
244
- pull a repo origin into main
245
- repo should be in the form of : path/.git
246
- repo = '../micropython/.git'
247
- returns True on success
248
- """
249
- if not repo:
250
- raise NotADirectoryError
251
- repo = Path(repo)
252
- # first checkout HEAD
253
- cmd = ["git", "checkout", branch, "--quiet", "--force"]
254
- result = _run_local_git(cmd, repo=repo, expect_stderr=True)
255
- if not result:
256
- log.error("error during git checkout main", result)
257
- return False
258
-
259
- cmd = ["git", "pull", "origin", branch, "--quiet", "--autostash"]
260
- result = _run_local_git(cmd, repo=repo, expect_stderr=True)
261
- if not result:
262
- log.error("error durign pull", result)
263
- return False
264
- return result.returncode == 0
265
-
266
-
267
- def get_git_describe(folder: Optional[str] = None):
268
- """
269
- Based on MicroPython makeversionhdr.py
270
- returns : current git tag, commits ,commit hash : "v1.19.1-841-g3446"
271
- """
272
- # Note: git describe doesn't work if no tag is available
273
- try:
274
- git_describe = subprocess.check_output(
275
- ["git", "describe", "--tags", "--dirty", "--always", "--match", "v[1-9].*"],
276
- stderr=subprocess.STDOUT,
277
- universal_newlines=True,
278
- cwd=folder,
279
- ).strip()
280
- except subprocess.CalledProcessError as er:
281
- if er.returncode == 128:
282
- # git exit code of 128 means no repository found
283
- return None
284
- git_describe = ""
285
- except OSError:
286
- return None
287
- # format
288
- return git_describe
1
+ """
2
+ Simple Git module, where needed via powershell
3
+
4
+ Some of the functions are based on the PyGithub module
5
+ """
6
+
7
+ import os
8
+ import subprocess
9
+ from pathlib import Path
10
+ from typing import List, Optional, Union
11
+
12
+ import cachetools.func
13
+ from github import Auth, Github
14
+ from loguru import logger as log
15
+ from packaging.version import parse
16
+
17
+ # from stubber.utils.versions import SET_PREVIEW
18
+
19
+ # Token with no permissions to avoid throttling
20
+ # 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
21
+ PAT_NO_ACCESS = (
22
+ "github_pat" + "_11AAHPVFQ0qAkDnSUaMKSp" + "_ZkDl5NRRwBsUN6EYg9ahp1Dvj4FDDONnXVgimxC2EtpY7Q7BUKBoQ0Jq72X"
23
+ )
24
+ PAT = os.environ.get("GITHUB_TOKEN") or PAT_NO_ACCESS
25
+ GH_CLIENT = Github(auth=Auth.Token(PAT))
26
+
27
+
28
+ def _run_local_git(
29
+ cmd: List[str],
30
+ repo: Optional[Union[Path, str]] = None,
31
+ expect_stderr=False,
32
+ capture_output=True,
33
+ echo_output=True,
34
+ ):
35
+ "run a external (git) command in the repo's folder and deal with some of the errors"
36
+ try:
37
+ if repo:
38
+ if isinstance(repo, str):
39
+ repo = Path(repo)
40
+ result = subprocess.run(
41
+ cmd, capture_output=capture_output, check=True, cwd=repo.absolute().as_posix(), encoding="utf-8"
42
+ )
43
+ else:
44
+ result = subprocess.run(cmd, capture_output=capture_output, check=True, encoding="utf-8")
45
+ except (NotADirectoryError, FileNotFoundError) as e: # pragma: no cover
46
+ return None
47
+ except subprocess.CalledProcessError as e: # pragma: no cover
48
+ # add some logging for github actions
49
+ log.error(f"{str(e)} : { e.stderr}")
50
+ return None
51
+ if result.stderr and result.stderr != b"":
52
+ stderr = result.stderr
53
+ if "cloning into" in stderr.lower():
54
+ # log.info(stderr)
55
+ expect_stderr = True
56
+ if "warning" in stderr.lower():
57
+ log.warning(stderr)
58
+ expect_stderr = True
59
+ elif capture_output and echo_output: # pragma: no cover
60
+ log.info(stderr)
61
+ if not expect_stderr:
62
+ raise ChildProcessError(stderr)
63
+
64
+ if result.returncode and result.returncode < 0:
65
+ raise ChildProcessError(result.stderr)
66
+ return result
67
+
68
+
69
+ def clone(remote_repo: str, path: Path, shallow: bool = False, tag: Optional[str] = None) -> bool:
70
+ """git clone [--depth 1] [--branch <tag_name>] <remote> <directory>"""
71
+ cmd = ["git", "clone"]
72
+ if shallow:
73
+ cmd += ["--depth", "1"]
74
+ if tag in {"preview", "latest", "master"}:
75
+ tag = None
76
+ cmd += [remote_repo, "--branch", tag, str(path)] if tag else [remote_repo, str(path)]
77
+ if result := _run_local_git(cmd, expect_stderr=True, capture_output=False):
78
+ return result.returncode == 0
79
+ else:
80
+ return False
81
+
82
+
83
+ def get_local_tag(repo: Optional[Union[str, Path]] = None, abbreviate: bool = True) -> Union[str, None]:
84
+ """
85
+ get the most recent git version tag of a local repo
86
+ repo Path should be in the form of : repo = "./repo/micropython"
87
+
88
+ returns the tag or None
89
+ """
90
+ if not repo:
91
+ repo = Path(".")
92
+ elif isinstance(repo, str):
93
+ repo = Path(repo)
94
+
95
+ result = _run_local_git(
96
+ # ["git", "describe", "--tags"],
97
+ ["git", "describe", "--tags", "--dirty", "--always", "--match", "v[1-9].*"],
98
+ repo=repo.as_posix(),
99
+ expect_stderr=True,
100
+ )
101
+ if not result:
102
+ return None
103
+ tag: str = result.stdout
104
+ tag = tag.replace("\r", "").replace("\n", "")
105
+ if not abbreviate or "-" not in tag:
106
+ return tag
107
+ if "-preview" in tag:
108
+ tag = tag.split("-preview")[0] + "-preview"
109
+ return tag
110
+
111
+
112
+ def get_local_tags(repo: Optional[Path] = None, minver: Optional[str] = None) -> List[str]:
113
+ """
114
+ get list of all tags of a local repo
115
+ """
116
+ if not repo:
117
+ repo = Path(".")
118
+
119
+ result = _run_local_git(["git", "tag", "-l"], repo=repo.as_posix(), expect_stderr=True)
120
+ if not result or result.returncode != 0:
121
+ return []
122
+ tags = result.stdout.replace("\r", "").split("\n")
123
+ tags = [tag for tag in tags if tag.startswith("v")]
124
+ if minver:
125
+ tags = [tag for tag in tags if parse(tag) >= parse(minver)]
126
+ return sorted(tags)
127
+
128
+
129
+ @cachetools.func.ttl_cache(maxsize=16, ttl=60) # 60 seconds
130
+ def get_tags(repo: str, minver: Optional[str] = None) -> List[str]:
131
+ """
132
+ Get list of tag of a repote github repo
133
+ """
134
+ if not repo or not isinstance(repo, str) or "/" not in repo: # type: ignore
135
+ return []
136
+ try:
137
+ gh_repo = GH_CLIENT.get_repo(repo)
138
+ except ConnectionError as e:
139
+ # TODO: unable to capture the exeption
140
+ log.warning(f"Unable to get tags - {e}")
141
+ return []
142
+ tags = [tag.name for tag in gh_repo.get_tags()]
143
+ if minver:
144
+ tags = [tag for tag in tags if parse(tag) >= parse(minver)]
145
+ return sorted(tags)
146
+
147
+
148
+ def checkout_tag(tag: str, repo: Optional[Union[str, Path]] = None) -> bool:
149
+ """
150
+ checkout a specific git tag
151
+ """
152
+ cmd = ["git", "checkout", tag, "--quiet", "--force"]
153
+ result = _run_local_git(cmd, repo=repo, expect_stderr=True, capture_output=True)
154
+ if not result:
155
+ return False
156
+ # actually a good result
157
+ msg = {result.stdout}
158
+ if msg != {""}:
159
+ log.warning(f"git message: {msg}")
160
+ return True
161
+
162
+
163
+ def sync_submodules(repo: Optional[Union[Path, str]] = None) -> bool:
164
+ """
165
+ make sure any submodules are in sync
166
+ """
167
+ cmds = [
168
+ ["git", "submodule", "sync", "--quiet"],
169
+ # ["git", "submodule", "update", "--quiet"],
170
+ ["git", "submodule", "update", "--init", "lib/micropython-lib"],
171
+ ]
172
+ for cmd in cmds:
173
+ if result := _run_local_git(cmd, repo=repo, expect_stderr=True):
174
+ # actually a good result
175
+ log.debug(result.stderr)
176
+ else:
177
+ return False
178
+ return True
179
+
180
+
181
+ def checkout_commit(commit_hash: str, repo: Optional[Union[Path, str]] = None) -> bool:
182
+ """
183
+ Checkout a specific commit
184
+ """
185
+ cmd = ["git", "checkout", commit_hash, "--quiet", "--force"]
186
+ result = _run_local_git(cmd, repo=repo, expect_stderr=True)
187
+ if not result:
188
+ return False
189
+ # actually a good result
190
+ log.debug(result.stderr)
191
+ return True
192
+
193
+
194
+ def switch_tag(tag: Union[str, Path], repo: Optional[Union[Path, str]] = None) -> bool:
195
+ """
196
+ switch to the specified version tag of a local repo
197
+ repo should be in the form of : path/.git
198
+ repo = '../micropython/.git'
199
+ returns None
200
+ """
201
+
202
+ cmd = ["git", "switch", "--detach", tag, "--quiet", "--force"]
203
+ result = _run_local_git(cmd, repo=repo, expect_stderr=True)
204
+ if not result:
205
+ return False
206
+ # actually a good result
207
+ log.debug(result.stderr)
208
+ return True
209
+
210
+
211
+ def switch_branch(branch: str, repo: Optional[Union[Path, str]] = None) -> bool:
212
+ """
213
+ switch to the specified branch in a local repo"
214
+ repo should be in the form of : path/.git
215
+ repo = '../micropython/.git'
216
+ returns None
217
+ """
218
+ cmd = ["git", "switch", branch, "--quiet", "--force"]
219
+ result = _run_local_git(cmd, repo=repo, expect_stderr=True)
220
+ if not result:
221
+ return False
222
+ # actually a good result
223
+ log.debug(result.stderr)
224
+ return True
225
+
226
+
227
+ def fetch(repo: Union[Path, str]) -> bool:
228
+ """
229
+ fetches a repo
230
+ repo should be in the form of : path/.git
231
+ repo = '../micropython/.git'
232
+ returns True on success
233
+ """
234
+ if not repo:
235
+ raise NotADirectoryError
236
+
237
+ cmd = ["git", "fetch", "--all", "--tags", "--quiet"]
238
+ result = _run_local_git(cmd, repo=repo, echo_output=False)
239
+ return result.returncode == 0 if result else False
240
+
241
+
242
+ def pull(repo: Union[Path, str], branch: str = "main") -> bool:
243
+ """
244
+ pull a repo origin into main
245
+ repo should be in the form of : path/.git
246
+ repo = '../micropython/.git'
247
+ returns True on success
248
+ """
249
+ if not repo:
250
+ raise NotADirectoryError
251
+ repo = Path(repo)
252
+ # first checkout HEAD
253
+ cmd = ["git", "checkout", branch, "--quiet", "--force"]
254
+ result = _run_local_git(cmd, repo=repo, expect_stderr=True)
255
+ if not result:
256
+ log.error("error during git checkout main", result)
257
+ return False
258
+
259
+ cmd = ["git", "pull", "origin", branch, "--quiet", "--autostash"]
260
+ result = _run_local_git(cmd, repo=repo, expect_stderr=True)
261
+ if not result:
262
+ log.error("error durign pull", result)
263
+ return False
264
+ return result.returncode == 0
265
+
266
+
267
+ def get_git_describe(folder: Optional[str] = None):
268
+ """
269
+ Based on MicroPython makeversionhdr.py
270
+ returns : current git tag, commits ,commit hash : "v1.19.1-841-g3446"
271
+ """
272
+ # Note: git describe doesn't work if no tag is available
273
+ try:
274
+ git_describe = subprocess.check_output(
275
+ ["git", "describe", "--tags", "--dirty", "--always", "--match", "v[1-9].*"],
276
+ stderr=subprocess.STDOUT,
277
+ universal_newlines=True,
278
+ cwd=folder,
279
+ ).strip()
280
+ except subprocess.CalledProcessError as er:
281
+ if er.returncode == 128:
282
+ # git exit code of 128 means no repository found
283
+ return None
284
+ git_describe = ""
285
+ except OSError:
286
+ return None
287
+ # format
288
+ return git_describe
@@ -0,0 +1,91 @@
1
+ # Copyright (c) 2016 Robbin Bonthond
2
+
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ # ------------------------------------------------------------------------------------
13
+ # modified to avoid conflcts with rich_click
14
+
15
+ # sourcery skip: assign-if-exp, use-named-expression
16
+
17
+ import rich_click as click
18
+
19
+ _click7 = click.__version__[0] >= "7"
20
+
21
+
22
+ class ClickAliasedGroup(click.RichGroup):
23
+ def __init__(self, *args, **kwargs):
24
+ super().__init__(*args, **kwargs)
25
+ self._commands = {}
26
+ self._aliases = {}
27
+
28
+ def add_command(self, *args, **kwargs):
29
+ aliases = kwargs.pop("aliases", [])
30
+ super().add_command(*args, **kwargs)
31
+ if aliases:
32
+ cmd = args[0]
33
+ name = args[1] if len(args) > 1 else None
34
+ name = name or cmd.name
35
+ if name is None:
36
+ raise TypeError("Command has no name.")
37
+
38
+ self._commands[name] = aliases
39
+ for alias in aliases:
40
+ self._aliases[alias] = cmd.name
41
+
42
+ def command(self, *args, **kwargs):
43
+ aliases = kwargs.pop("aliases", [])
44
+ decorator = super().command(*args, **kwargs)
45
+ if not aliases:
46
+ return decorator
47
+
48
+ def _decorator(f):
49
+ cmd = decorator(f)
50
+ if aliases:
51
+ self._commands[cmd.name] = aliases
52
+ for alias in aliases:
53
+ self._aliases[alias] = cmd.name
54
+ return cmd
55
+
56
+ return _decorator
57
+
58
+ def group(self, *args, **kwargs):
59
+ aliases = kwargs.pop("aliases", [])
60
+ decorator = super().group(*args, **kwargs)
61
+ if not aliases:
62
+ return decorator
63
+
64
+ def _decorator(f):
65
+ cmd = decorator(f)
66
+ if aliases:
67
+ self._commands[cmd.name] = aliases
68
+ for alias in aliases:
69
+ self._aliases[alias] = cmd.name
70
+ return cmd
71
+
72
+ return _decorator
73
+
74
+ def resolve_alias(self, cmd_name):
75
+ if cmd_name in self._aliases:
76
+ return self._aliases[cmd_name]
77
+ return cmd_name
78
+
79
+ def get_command(self, ctx, cmd_name):
80
+ cmd_name = self.resolve_alias(cmd_name)
81
+ command = super().get_command(ctx, cmd_name)
82
+ if command:
83
+ return command
84
+ return None
85
+
86
+ # def format_commands(self, ctx, formatter):
87
+ # TODO: output alias with commands - but that is a significant re-write
88
+ # for now add alias to help text
89
+
90
+
91
+ # ------------------------------------------------------------------------------------