micropython-stubber 1.20.5__py3-none-any.whl → 1.23.0__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.
- {micropython_stubber-1.20.5.dist-info → micropython_stubber-1.23.0.dist-info}/LICENSE +30 -30
- {micropython_stubber-1.20.5.dist-info → micropython_stubber-1.23.0.dist-info}/METADATA +1 -1
- micropython_stubber-1.23.0.dist-info/RECORD +159 -0
- mpflash/README.md +184 -184
- mpflash/libusb_flash.ipynb +203 -203
- mpflash/mpflash/add_firmware.py +98 -98
- mpflash/mpflash/ask_input.py +236 -236
- mpflash/mpflash/bootloader/__init__.py +37 -36
- mpflash/mpflash/bootloader/manual.py +102 -102
- mpflash/mpflash/bootloader/micropython.py +10 -10
- mpflash/mpflash/bootloader/touch1200.py +45 -45
- mpflash/mpflash/cli_download.py +129 -129
- mpflash/mpflash/cli_flash.py +219 -219
- mpflash/mpflash/cli_group.py +98 -98
- mpflash/mpflash/cli_list.py +81 -81
- mpflash/mpflash/cli_main.py +41 -41
- mpflash/mpflash/common.py +164 -164
- mpflash/mpflash/config.py +43 -47
- mpflash/mpflash/connected.py +74 -74
- mpflash/mpflash/download.py +360 -360
- mpflash/mpflash/downloaded.py +130 -129
- mpflash/mpflash/errors.py +9 -9
- mpflash/mpflash/flash.py +55 -52
- mpflash/mpflash/flash_esp.py +59 -59
- mpflash/mpflash/flash_stm32.py +18 -24
- mpflash/mpflash/flash_stm32_cube.py +111 -111
- mpflash/mpflash/flash_stm32_dfu.py +104 -101
- mpflash/mpflash/flash_uf2.py +89 -67
- mpflash/mpflash/flash_uf2_boardid.py +15 -15
- mpflash/mpflash/flash_uf2_linux.py +129 -123
- mpflash/mpflash/flash_uf2_macos.py +37 -34
- mpflash/mpflash/flash_uf2_windows.py +38 -34
- mpflash/mpflash/list.py +89 -89
- mpflash/mpflash/logger.py +41 -41
- mpflash/mpflash/mpboard_id/__init__.py +93 -93
- mpflash/mpflash/mpboard_id/add_boards.py +255 -255
- mpflash/mpflash/mpboard_id/board.py +37 -37
- mpflash/mpflash/mpboard_id/board_id.py +86 -86
- mpflash/mpflash/mpboard_id/store.py +43 -43
- mpflash/mpflash/mpremoteboard/__init__.py +226 -221
- mpflash/mpflash/mpremoteboard/mpy_fw_info.py +141 -141
- mpflash/mpflash/mpremoteboard/runner.py +140 -140
- mpflash/mpflash/uf2disk.py +12 -12
- mpflash/mpflash/vendor/basicgit.py +288 -288
- mpflash/mpflash/vendor/click_aliases.py +91 -91
- mpflash/mpflash/vendor/dfu.py +165 -165
- mpflash/mpflash/vendor/pydfu.py +605 -605
- mpflash/mpflash/vendor/readme.md +2 -2
- mpflash/mpflash/vendor/versions.py +119 -117
- mpflash/mpflash/worklist.py +171 -170
- mpflash/poetry.lock +1588 -1588
- mpflash/pyproject.toml +64 -60
- mpflash/stm32_udev_rules.md +62 -62
- stubber/__init__.py +3 -3
- stubber/basicgit.py +294 -288
- stubber/board/board_info.csv +193 -193
- stubber/board/boot.py +34 -34
- stubber/board/createstubs.py +986 -986
- stubber/board/createstubs_db.py +825 -825
- stubber/board/createstubs_db_min.py +331 -331
- stubber/board/createstubs_db_mpy.mpy +0 -0
- stubber/board/createstubs_lvgl.py +741 -741
- stubber/board/createstubs_lvgl_min.py +741 -741
- stubber/board/createstubs_mem.py +766 -766
- stubber/board/createstubs_mem_min.py +306 -306
- stubber/board/createstubs_mem_mpy.mpy +0 -0
- stubber/board/createstubs_min.py +294 -294
- stubber/board/createstubs_mpy.mpy +0 -0
- stubber/board/fw_info.py +141 -141
- stubber/board/info.py +183 -183
- stubber/board/main.py +19 -19
- stubber/board/modulelist.txt +247 -247
- stubber/board/pyrightconfig.json +34 -34
- stubber/bulk/mcu_stubber.py +454 -454
- stubber/codemod/_partials/__init__.py +48 -48
- stubber/codemod/_partials/db_main.py +147 -147
- stubber/codemod/_partials/lvgl_main.py +77 -77
- stubber/codemod/_partials/modules_reader.py +80 -80
- stubber/codemod/add_comment.py +53 -53
- stubber/codemod/add_method.py +65 -65
- stubber/codemod/board.py +317 -317
- stubber/codemod/enrich.py +145 -145
- stubber/codemod/merge_docstub.py +284 -284
- stubber/codemod/modify_list.py +54 -54
- stubber/codemod/utils.py +57 -57
- stubber/commands/build_cmd.py +94 -94
- stubber/commands/cli.py +55 -51
- stubber/commands/clone_cmd.py +77 -66
- stubber/commands/config_cmd.py +29 -29
- stubber/commands/enrich_folder_cmd.py +71 -70
- stubber/commands/get_core_cmd.py +71 -69
- stubber/commands/get_docstubs_cmd.py +89 -87
- stubber/commands/get_frozen_cmd.py +114 -112
- stubber/commands/get_mcu_cmd.py +61 -56
- stubber/commands/merge_cmd.py +67 -66
- stubber/commands/publish_cmd.py +119 -119
- stubber/commands/stub_cmd.py +31 -30
- stubber/commands/switch_cmd.py +62 -54
- stubber/commands/variants_cmd.py +49 -48
- stubber/cst_transformer.py +178 -178
- stubber/data/board_info.csv +193 -193
- stubber/data/board_info.json +1729 -1729
- stubber/data/micropython_tags.csv +15 -15
- stubber/data/requirements-core-micropython.txt +38 -38
- stubber/data/requirements-core-pycopy.txt +39 -39
- stubber/downloader.py +36 -36
- stubber/freeze/common.py +68 -68
- stubber/freeze/freeze_folder.py +69 -69
- stubber/freeze/freeze_manifest_2.py +113 -113
- stubber/freeze/get_frozen.py +127 -127
- stubber/get_cpython.py +101 -101
- stubber/get_lobo.py +59 -59
- stubber/minify.py +418 -418
- stubber/publish/bump.py +86 -86
- stubber/publish/candidates.py +262 -262
- stubber/publish/database.py +18 -18
- stubber/publish/defaults.py +45 -45
- stubber/publish/enums.py +24 -24
- stubber/publish/helpers.py +29 -29
- stubber/publish/merge_docstubs.py +130 -130
- stubber/publish/missing_class_methods.py +49 -49
- stubber/publish/package.py +146 -146
- stubber/publish/pathnames.py +51 -51
- stubber/publish/publish.py +120 -120
- stubber/publish/pypi.py +38 -38
- stubber/publish/stubpackage.py +1029 -1029
- stubber/rst/__init__.py +9 -9
- stubber/rst/classsort.py +77 -77
- stubber/rst/lookup.py +530 -530
- stubber/rst/output_dict.py +401 -401
- stubber/rst/reader.py +822 -822
- stubber/rst/report_return.py +69 -69
- stubber/rst/rst_utils.py +540 -540
- stubber/stubber.py +38 -38
- stubber/stubs_from_docs.py +90 -90
- stubber/tools/manifestfile.py +655 -610
- stubber/tools/readme.md +7 -6
- stubber/update_fallback.py +117 -117
- stubber/update_module_list.py +123 -123
- stubber/utils/__init__.py +5 -5
- stubber/utils/config.py +127 -127
- stubber/utils/makeversionhdr.py +54 -54
- stubber/utils/manifest.py +92 -92
- stubber/utils/post.py +79 -79
- stubber/utils/repos.py +157 -154
- stubber/utils/stubmaker.py +139 -139
- stubber/utils/typed_config_toml.py +77 -77
- stubber/utils/versions.py +128 -120
- stubber/variants.py +106 -106
- micropython_stubber-1.20.5.dist-info/RECORD +0 -159
- {micropython_stubber-1.20.5.dist-info → micropython_stubber-1.23.0.dist-info}/WHEEL +0 -0
- {micropython_stubber-1.20.5.dist-info → micropython_stubber-1.23.0.dist-info}/entry_points.txt +0 -0
stubber/cst_transformer.py
CHANGED
@@ -1,178 +1,178 @@
|
|
1
|
-
"""helper functions for stub transformations"""
|
2
|
-
|
3
|
-
# sourcery skip: snake-case-functions
|
4
|
-
from dataclasses import dataclass
|
5
|
-
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
|
6
|
-
|
7
|
-
import libcst as cst
|
8
|
-
|
9
|
-
|
10
|
-
@dataclass
|
11
|
-
class TypeInfo:
|
12
|
-
"contains the functiondefs and classdefs info read from the stubs source"
|
13
|
-
name: str
|
14
|
-
decorators: Sequence[cst.Decorator]
|
15
|
-
params: Optional[cst.Parameters] = None
|
16
|
-
returns: Optional[cst.Annotation] = None
|
17
|
-
docstr_node: Optional[cst.SimpleStatementLine] = None
|
18
|
-
def_node: Optional[Union[cst.FunctionDef, cst.ClassDef]] = None
|
19
|
-
def_type: str = "?" # funcdef or classdef or module
|
20
|
-
|
21
|
-
|
22
|
-
class TransformError(Exception):
|
23
|
-
"""
|
24
|
-
Error raised upon encountering a known error while attempting to transform
|
25
|
-
the tree.
|
26
|
-
"""
|
27
|
-
|
28
|
-
|
29
|
-
MODULE_KEY = ("__module",)
|
30
|
-
MODDOC_KEY = ("__module_docstring",)
|
31
|
-
|
32
|
-
# debug helper
|
33
|
-
_m = cst.parse_module("")
|
34
|
-
|
35
|
-
|
36
|
-
class StubTypingCollector(cst.CSTVisitor):
|
37
|
-
"""
|
38
|
-
Collect the function/method and class definitions from the stubs source
|
39
|
-
"""
|
40
|
-
|
41
|
-
def __init__(self):
|
42
|
-
# stack for storing the canonical name of the current function
|
43
|
-
self.stack: List[str] = []
|
44
|
-
# store the annotations
|
45
|
-
self.annotations: Dict[
|
46
|
-
Tuple[str, ...], # key: tuple of canonical class/function name
|
47
|
-
Union[TypeInfo, str],
|
48
|
-
] = {}
|
49
|
-
self.comments :List[str] = []
|
50
|
-
|
51
|
-
# ------------------------------------------------------------
|
52
|
-
def visit_Module(self, node: cst.Module) -> bool:
|
53
|
-
"""Store the module docstring"""
|
54
|
-
docstr = node.get_docstring()
|
55
|
-
if docstr:
|
56
|
-
self.annotations[MODULE_KEY] = docstr
|
57
|
-
return True
|
58
|
-
def visit_Comment(self, node: cst.Comment) -> None:
|
59
|
-
"""
|
60
|
-
connect comments from the source
|
61
|
-
"""
|
62
|
-
comment = node.value
|
63
|
-
if comment.startswith("# MCU: ") or comment.startswith("# Stubber:"):
|
64
|
-
# very basic way to detect the stubber comments that we want to copy over
|
65
|
-
self.comments.append(comment)
|
66
|
-
|
67
|
-
# ------------------------------------------------------------
|
68
|
-
# keep track of the the (class, method) names to the stack
|
69
|
-
def visit_ClassDef(self, node: cst.ClassDef) -> Optional[bool]:
|
70
|
-
"""
|
71
|
-
collect info from a classdef:
|
72
|
-
- name, decorators, docstring
|
73
|
-
"""
|
74
|
-
# "Store the class docstring
|
75
|
-
docstr_node = self.update_append_first_node(node)
|
76
|
-
ti = TypeInfo(
|
77
|
-
name=node.name.value,
|
78
|
-
params=None,
|
79
|
-
returns=None,
|
80
|
-
docstr_node=docstr_node,
|
81
|
-
decorators=node.decorators,
|
82
|
-
def_type="classdef",
|
83
|
-
def_node=node,
|
84
|
-
)
|
85
|
-
self.annotations[tuple(self.stack)] = ti
|
86
|
-
|
87
|
-
def leave_ClassDef(self, original_node: cst.ClassDef) -> None:
|
88
|
-
"""remove the class name from the stack"""
|
89
|
-
self.stack.pop()
|
90
|
-
|
91
|
-
# ------------------------------------------------------------
|
92
|
-
def visit_FunctionDef(self, node: cst.FunctionDef) -> Optional[bool]:
|
93
|
-
"""
|
94
|
-
collect info from a function/method
|
95
|
-
- name, decorators, params, returns, docstring
|
96
|
-
"""
|
97
|
-
# "store each function/method signature"
|
98
|
-
docstr_node = self.update_append_first_node(node)
|
99
|
-
ti = TypeInfo(
|
100
|
-
name=node.name.value,
|
101
|
-
params=node.params,
|
102
|
-
returns=node.returns,
|
103
|
-
docstr_node=docstr_node,
|
104
|
-
decorators=node.decorators,
|
105
|
-
def_type="funcdef",
|
106
|
-
def_node=node,
|
107
|
-
)
|
108
|
-
self.annotations[tuple(self.stack)] = ti
|
109
|
-
|
110
|
-
def update_append_first_node(self, node):
|
111
|
-
"""Store the function/method docstring or function/method sig"""
|
112
|
-
self.stack.append(node.name.value)
|
113
|
-
if node.get_docstring():
|
114
|
-
assert isinstance(node.body.body[0], cst.SimpleStatementLine)
|
115
|
-
return node.body.body[0]
|
116
|
-
else:
|
117
|
-
return None
|
118
|
-
|
119
|
-
def leave_FunctionDef(self, original_node: cst.FunctionDef) -> None:
|
120
|
-
"""remove the function/method name from the stack"""
|
121
|
-
self.stack.pop()
|
122
|
-
|
123
|
-
|
124
|
-
def update_def_docstr(
|
125
|
-
dest_node: Union[cst.FunctionDef, cst.ClassDef],
|
126
|
-
src_comment: Optional[cst.SimpleStatementLine],
|
127
|
-
src_node=None,
|
128
|
-
) -> Any:
|
129
|
-
"""
|
130
|
-
Update the docstring of a function/method or class
|
131
|
-
|
132
|
-
for functiondefs ending in an ellipsis, the entire body needs to be replaced.
|
133
|
-
in this case the src_body is mandatory.
|
134
|
-
"""
|
135
|
-
if not src_comment:
|
136
|
-
return dest_node
|
137
|
-
|
138
|
-
# function def on a single line ending with an ellipsis (...)
|
139
|
-
if isinstance(dest_node.body, cst.SimpleStatementSuite):
|
140
|
-
# in order to add a boy the simple hack is to copy the src_node.body
|
141
|
-
return dest_node.with_changes(body=src_node.body) if src_node else dest_node
|
142
|
-
# just checking
|
143
|
-
if not isinstance(dest_node.body, cst.IndentedBlock):
|
144
|
-
raise TransformError("Expected Def with Indented body")
|
145
|
-
|
146
|
-
# classdef of functiondef with an indented body
|
147
|
-
# need some funcky casting to avoid issues with changing the body
|
148
|
-
# note : indented body is nested : body.body
|
149
|
-
if dest_node.get_docstring() is None:
|
150
|
-
# append the new docstring and append the function body
|
151
|
-
body = tuple([src_comment] + list(dest_node.body.body))
|
152
|
-
else:
|
153
|
-
body = tuple([src_comment] + list(dest_node.body.body[1:]))
|
154
|
-
body_2 = dest_node.body.with_changes(body=body)
|
155
|
-
|
156
|
-
return dest_node.with_changes(body=body_2)
|
157
|
-
|
158
|
-
|
159
|
-
def update_module_docstr(
|
160
|
-
node: cst.Module,
|
161
|
-
doc_tree: Optional[Union[str, cst.SimpleStatementLine, cst.BaseCompoundStatement]],
|
162
|
-
) -> Any:
|
163
|
-
"""
|
164
|
-
Add or update the docstring of a module
|
165
|
-
"""
|
166
|
-
if not doc_tree:
|
167
|
-
return node
|
168
|
-
if not isinstance(doc_tree, (str, cst.SimpleStatementLine, cst.BaseCompoundStatement)): # type: ignore
|
169
|
-
raise TransformError("Expected a docstring or a statement")
|
170
|
-
if isinstance(doc_tree, str):
|
171
|
-
doc_tree = cst.parse_statement(doc_tree)
|
172
|
-
# need some funcky casting to avoid issues with changing the body
|
173
|
-
if node.get_docstring() is None:
|
174
|
-
# append the new docstring and append the function body
|
175
|
-
body = tuple([doc_tree] + list(node.body)) # type: ignore
|
176
|
-
else:
|
177
|
-
body = tuple([doc_tree] + list(node.body[1:])) # type: ignore
|
178
|
-
return node.with_changes(body=body)
|
1
|
+
"""helper functions for stub transformations"""
|
2
|
+
|
3
|
+
# sourcery skip: snake-case-functions
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
|
6
|
+
|
7
|
+
import libcst as cst
|
8
|
+
|
9
|
+
|
10
|
+
@dataclass
|
11
|
+
class TypeInfo:
|
12
|
+
"contains the functiondefs and classdefs info read from the stubs source"
|
13
|
+
name: str
|
14
|
+
decorators: Sequence[cst.Decorator]
|
15
|
+
params: Optional[cst.Parameters] = None
|
16
|
+
returns: Optional[cst.Annotation] = None
|
17
|
+
docstr_node: Optional[cst.SimpleStatementLine] = None
|
18
|
+
def_node: Optional[Union[cst.FunctionDef, cst.ClassDef]] = None
|
19
|
+
def_type: str = "?" # funcdef or classdef or module
|
20
|
+
|
21
|
+
|
22
|
+
class TransformError(Exception):
|
23
|
+
"""
|
24
|
+
Error raised upon encountering a known error while attempting to transform
|
25
|
+
the tree.
|
26
|
+
"""
|
27
|
+
|
28
|
+
|
29
|
+
MODULE_KEY = ("__module",)
|
30
|
+
MODDOC_KEY = ("__module_docstring",)
|
31
|
+
|
32
|
+
# debug helper
|
33
|
+
_m = cst.parse_module("")
|
34
|
+
|
35
|
+
|
36
|
+
class StubTypingCollector(cst.CSTVisitor):
|
37
|
+
"""
|
38
|
+
Collect the function/method and class definitions from the stubs source
|
39
|
+
"""
|
40
|
+
|
41
|
+
def __init__(self):
|
42
|
+
# stack for storing the canonical name of the current function
|
43
|
+
self.stack: List[str] = []
|
44
|
+
# store the annotations
|
45
|
+
self.annotations: Dict[
|
46
|
+
Tuple[str, ...], # key: tuple of canonical class/function name
|
47
|
+
Union[TypeInfo, str],
|
48
|
+
] = {}
|
49
|
+
self.comments :List[str] = []
|
50
|
+
|
51
|
+
# ------------------------------------------------------------
|
52
|
+
def visit_Module(self, node: cst.Module) -> bool:
|
53
|
+
"""Store the module docstring"""
|
54
|
+
docstr = node.get_docstring()
|
55
|
+
if docstr:
|
56
|
+
self.annotations[MODULE_KEY] = docstr
|
57
|
+
return True
|
58
|
+
def visit_Comment(self, node: cst.Comment) -> None:
|
59
|
+
"""
|
60
|
+
connect comments from the source
|
61
|
+
"""
|
62
|
+
comment = node.value
|
63
|
+
if comment.startswith("# MCU: ") or comment.startswith("# Stubber:"):
|
64
|
+
# very basic way to detect the stubber comments that we want to copy over
|
65
|
+
self.comments.append(comment)
|
66
|
+
|
67
|
+
# ------------------------------------------------------------
|
68
|
+
# keep track of the the (class, method) names to the stack
|
69
|
+
def visit_ClassDef(self, node: cst.ClassDef) -> Optional[bool]:
|
70
|
+
"""
|
71
|
+
collect info from a classdef:
|
72
|
+
- name, decorators, docstring
|
73
|
+
"""
|
74
|
+
# "Store the class docstring
|
75
|
+
docstr_node = self.update_append_first_node(node)
|
76
|
+
ti = TypeInfo(
|
77
|
+
name=node.name.value,
|
78
|
+
params=None,
|
79
|
+
returns=None,
|
80
|
+
docstr_node=docstr_node,
|
81
|
+
decorators=node.decorators,
|
82
|
+
def_type="classdef",
|
83
|
+
def_node=node,
|
84
|
+
)
|
85
|
+
self.annotations[tuple(self.stack)] = ti
|
86
|
+
|
87
|
+
def leave_ClassDef(self, original_node: cst.ClassDef) -> None:
|
88
|
+
"""remove the class name from the stack"""
|
89
|
+
self.stack.pop()
|
90
|
+
|
91
|
+
# ------------------------------------------------------------
|
92
|
+
def visit_FunctionDef(self, node: cst.FunctionDef) -> Optional[bool]:
|
93
|
+
"""
|
94
|
+
collect info from a function/method
|
95
|
+
- name, decorators, params, returns, docstring
|
96
|
+
"""
|
97
|
+
# "store each function/method signature"
|
98
|
+
docstr_node = self.update_append_first_node(node)
|
99
|
+
ti = TypeInfo(
|
100
|
+
name=node.name.value,
|
101
|
+
params=node.params,
|
102
|
+
returns=node.returns,
|
103
|
+
docstr_node=docstr_node,
|
104
|
+
decorators=node.decorators,
|
105
|
+
def_type="funcdef",
|
106
|
+
def_node=node,
|
107
|
+
)
|
108
|
+
self.annotations[tuple(self.stack)] = ti
|
109
|
+
|
110
|
+
def update_append_first_node(self, node):
|
111
|
+
"""Store the function/method docstring or function/method sig"""
|
112
|
+
self.stack.append(node.name.value)
|
113
|
+
if node.get_docstring():
|
114
|
+
assert isinstance(node.body.body[0], cst.SimpleStatementLine)
|
115
|
+
return node.body.body[0]
|
116
|
+
else:
|
117
|
+
return None
|
118
|
+
|
119
|
+
def leave_FunctionDef(self, original_node: cst.FunctionDef) -> None:
|
120
|
+
"""remove the function/method name from the stack"""
|
121
|
+
self.stack.pop()
|
122
|
+
|
123
|
+
|
124
|
+
def update_def_docstr(
|
125
|
+
dest_node: Union[cst.FunctionDef, cst.ClassDef],
|
126
|
+
src_comment: Optional[cst.SimpleStatementLine],
|
127
|
+
src_node=None,
|
128
|
+
) -> Any:
|
129
|
+
"""
|
130
|
+
Update the docstring of a function/method or class
|
131
|
+
|
132
|
+
for functiondefs ending in an ellipsis, the entire body needs to be replaced.
|
133
|
+
in this case the src_body is mandatory.
|
134
|
+
"""
|
135
|
+
if not src_comment:
|
136
|
+
return dest_node
|
137
|
+
|
138
|
+
# function def on a single line ending with an ellipsis (...)
|
139
|
+
if isinstance(dest_node.body, cst.SimpleStatementSuite):
|
140
|
+
# in order to add a boy the simple hack is to copy the src_node.body
|
141
|
+
return dest_node.with_changes(body=src_node.body) if src_node else dest_node
|
142
|
+
# just checking
|
143
|
+
if not isinstance(dest_node.body, cst.IndentedBlock):
|
144
|
+
raise TransformError("Expected Def with Indented body")
|
145
|
+
|
146
|
+
# classdef of functiondef with an indented body
|
147
|
+
# need some funcky casting to avoid issues with changing the body
|
148
|
+
# note : indented body is nested : body.body
|
149
|
+
if dest_node.get_docstring() is None:
|
150
|
+
# append the new docstring and append the function body
|
151
|
+
body = tuple([src_comment] + list(dest_node.body.body))
|
152
|
+
else:
|
153
|
+
body = tuple([src_comment] + list(dest_node.body.body[1:]))
|
154
|
+
body_2 = dest_node.body.with_changes(body=body)
|
155
|
+
|
156
|
+
return dest_node.with_changes(body=body_2)
|
157
|
+
|
158
|
+
|
159
|
+
def update_module_docstr(
|
160
|
+
node: cst.Module,
|
161
|
+
doc_tree: Optional[Union[str, cst.SimpleStatementLine, cst.BaseCompoundStatement]],
|
162
|
+
) -> Any:
|
163
|
+
"""
|
164
|
+
Add or update the docstring of a module
|
165
|
+
"""
|
166
|
+
if not doc_tree:
|
167
|
+
return node
|
168
|
+
if not isinstance(doc_tree, (str, cst.SimpleStatementLine, cst.BaseCompoundStatement)): # type: ignore
|
169
|
+
raise TransformError("Expected a docstring or a statement")
|
170
|
+
if isinstance(doc_tree, str):
|
171
|
+
doc_tree = cst.parse_statement(doc_tree)
|
172
|
+
# need some funcky casting to avoid issues with changing the body
|
173
|
+
if node.get_docstring() is None:
|
174
|
+
# append the new docstring and append the function body
|
175
|
+
body = tuple([doc_tree] + list(node.body)) # type: ignore
|
176
|
+
else:
|
177
|
+
body = tuple([doc_tree] + list(node.body[1:])) # type: ignore
|
178
|
+
return node.with_changes(body=body)
|