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.
- {micropython_stubber-1.23.1.dist-info → micropython_stubber-1.23.2.dist-info}/LICENSE +30 -30
- {micropython_stubber-1.23.1.dist-info → micropython_stubber-1.23.2.dist-info}/METADATA +32 -15
- micropython_stubber-1.23.2.dist-info/RECORD +158 -0
- micropython_stubber-1.23.2.dist-info/entry_points.txt +5 -0
- mpflash/README.md +220 -194
- mpflash/libusb_flash.ipynb +203 -203
- mpflash/mpflash/add_firmware.py +98 -98
- mpflash/mpflash/ask_input.py +236 -236
- mpflash/mpflash/basicgit.py +284 -284
- mpflash/mpflash/bootloader/__init__.py +2 -2
- mpflash/mpflash/bootloader/activate.py +60 -60
- mpflash/mpflash/bootloader/detect.py +82 -82
- mpflash/mpflash/bootloader/manual.py +101 -101
- mpflash/mpflash/bootloader/micropython.py +12 -12
- mpflash/mpflash/bootloader/touch1200.py +36 -36
- mpflash/mpflash/cli_download.py +129 -129
- mpflash/mpflash/cli_flash.py +224 -219
- mpflash/mpflash/cli_group.py +111 -111
- mpflash/mpflash/cli_list.py +87 -81
- mpflash/mpflash/cli_main.py +39 -39
- mpflash/mpflash/common.py +210 -165
- mpflash/mpflash/config.py +44 -44
- mpflash/mpflash/connected.py +96 -78
- mpflash/mpflash/download.py +364 -364
- mpflash/mpflash/downloaded.py +130 -130
- mpflash/mpflash/errors.py +9 -9
- mpflash/mpflash/flash/__init__.py +55 -55
- mpflash/mpflash/flash/esp.py +59 -59
- mpflash/mpflash/flash/stm32.py +19 -19
- mpflash/mpflash/flash/stm32_dfu.py +104 -104
- mpflash/mpflash/flash/uf2/__init__.py +88 -88
- mpflash/mpflash/flash/uf2/boardid.py +15 -15
- mpflash/mpflash/flash/uf2/linux.py +136 -130
- mpflash/mpflash/flash/uf2/macos.py +42 -42
- mpflash/mpflash/flash/uf2/uf2disk.py +12 -12
- mpflash/mpflash/flash/uf2/windows.py +43 -43
- mpflash/mpflash/flash/worklist.py +170 -170
- mpflash/mpflash/list.py +106 -99
- mpflash/mpflash/logger.py +41 -41
- mpflash/mpflash/mpboard_id/__init__.py +93 -93
- mpflash/mpflash/mpboard_id/add_boards.py +251 -251
- 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 +266 -222
- mpflash/mpflash/mpremoteboard/mpy_fw_info.py +141 -141
- mpflash/mpflash/mpremoteboard/runner.py +140 -140
- 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/versions.py +135 -135
- mpflash/poetry.lock +1599 -1599
- mpflash/pyproject.toml +65 -65
- mpflash/stm32_udev_rules.md +62 -62
- stubber/__init__.py +3 -3
- stubber/board/board_info.csv +193 -193
- stubber/board/boot.py +34 -34
- stubber/board/createstubs.py +1004 -986
- stubber/board/createstubs_db.py +826 -825
- stubber/board/createstubs_db_min.py +332 -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 +767 -766
- stubber/board/createstubs_mem_min.py +307 -306
- stubber/board/createstubs_mem_mpy.mpy +0 -0
- stubber/board/createstubs_min.py +295 -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 +437 -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 +151 -145
- stubber/codemod/merge_docstub.py +284 -284
- stubber/codemod/modify_list.py +54 -54
- stubber/codemod/utils.py +56 -56
- stubber/commands/build_cmd.py +94 -94
- stubber/commands/cli.py +49 -55
- stubber/commands/clone_cmd.py +78 -78
- stubber/commands/config_cmd.py +29 -29
- stubber/commands/enrich_folder_cmd.py +71 -71
- stubber/commands/get_core_cmd.py +71 -71
- stubber/commands/get_docstubs_cmd.py +92 -89
- stubber/commands/get_frozen_cmd.py +117 -114
- stubber/commands/get_mcu_cmd.py +102 -61
- stubber/commands/merge_cmd.py +66 -66
- stubber/commands/publish_cmd.py +118 -118
- stubber/commands/stub_cmd.py +31 -31
- stubber/commands/switch_cmd.py +62 -62
- stubber/commands/variants_cmd.py +48 -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 +37 -36
- stubber/freeze/common.py +72 -68
- stubber/freeze/freeze_folder.py +69 -69
- stubber/freeze/freeze_manifest_2.py +126 -113
- stubber/freeze/get_frozen.py +131 -127
- stubber/get_cpython.py +112 -101
- stubber/get_lobo.py +59 -59
- stubber/minify.py +423 -419
- stubber/publish/bump.py +86 -86
- stubber/publish/candidates.py +275 -256
- stubber/publish/database.py +18 -18
- stubber/publish/defaults.py +40 -40
- stubber/publish/enums.py +24 -24
- stubber/publish/helpers.py +29 -29
- stubber/publish/merge_docstubs.py +136 -130
- stubber/publish/missing_class_methods.py +51 -49
- stubber/publish/package.py +150 -146
- stubber/publish/pathnames.py +51 -51
- stubber/publish/publish.py +120 -120
- stubber/publish/pypi.py +42 -38
- stubber/publish/stubpackage.py +1055 -1027
- stubber/rst/__init__.py +9 -9
- stubber/rst/classsort.py +78 -77
- stubber/rst/lookup.py +533 -530
- stubber/rst/output_dict.py +401 -401
- stubber/rst/reader.py +814 -814
- stubber/rst/report_return.py +77 -69
- stubber/rst/rst_utils.py +541 -540
- stubber/stubber.py +38 -38
- stubber/stubs_from_docs.py +90 -90
- stubber/tools/manifestfile.py +654 -654
- stubber/tools/readme.md +6 -6
- stubber/update_fallback.py +117 -117
- stubber/update_module_list.py +123 -123
- stubber/utils/__init__.py +6 -6
- stubber/utils/config.py +137 -125
- stubber/utils/makeversionhdr.py +54 -54
- stubber/utils/manifest.py +90 -90
- stubber/utils/post.py +80 -79
- stubber/utils/repos.py +156 -150
- stubber/utils/stubmaker.py +139 -139
- stubber/utils/typed_config_toml.py +80 -77
- stubber/variants.py +106 -106
- micropython_stubber-1.23.1.dist-info/RECORD +0 -159
- micropython_stubber-1.23.1.dist-info/entry_points.txt +0 -3
- mpflash/basicgit.py +0 -288
- {micropython_stubber-1.23.1.dist-info → micropython_stubber-1.23.2.dist-info}/WHEEL +0 -0
stubber/rst/output_dict.py
CHANGED
@@ -1,401 +1,401 @@
|
|
1
|
-
"""
|
2
|
-
ModuleSourceDict represents a source file with the following components
|
3
|
-
- docstr
|
4
|
-
- version
|
5
|
-
- comment
|
6
|
-
- typing
|
7
|
-
- Optional: list of constants
|
8
|
-
- optional: ClassSourcedicts
|
9
|
-
- optional: FunctionSourcedicts
|
10
|
-
- optional: individual lines of code
|
11
|
-
|
12
|
-
ClassSourceDict represents a source file with the following components
|
13
|
-
- comment
|
14
|
-
- class
|
15
|
-
- docstr
|
16
|
-
- Optional: list of constants
|
17
|
-
- __init__ : class signature
|
18
|
-
- optional: FunctionSourcedicts
|
19
|
-
- optional: individual lines of code
|
20
|
-
|
21
|
-
FunctionSourceDict represents a source file with the following components
|
22
|
-
- # comments - todo
|
23
|
-
- optional: decorator
|
24
|
-
- def - function definition
|
25
|
-
- docstr
|
26
|
-
- constants
|
27
|
-
- body - ...
|
28
|
-
- optional: individual lines of code
|
29
|
-
|
30
|
-
SourceDict is the 'base class'
|
31
|
-
|
32
|
-
"""
|
33
|
-
from __future__ import annotations
|
34
|
-
|
35
|
-
from typing import List, Optional, OrderedDict, Union
|
36
|
-
|
37
|
-
from .classsort import sort_classes
|
38
|
-
|
39
|
-
# These are shown to import
|
40
|
-
__all__ = [
|
41
|
-
"SourceDict",
|
42
|
-
"ModuleSourceDict",
|
43
|
-
"ClassSourceDict",
|
44
|
-
"FunctionSourceDict",
|
45
|
-
]
|
46
|
-
|
47
|
-
EMPTY_DOCSTR = '""" """'
|
48
|
-
|
49
|
-
|
50
|
-
def spaces(n: int = 4) -> str:
|
51
|
-
return " " * n
|
52
|
-
|
53
|
-
|
54
|
-
class SourceDict(OrderedDict):
|
55
|
-
"(abstract) dict to store source components respecting parent child dependencies and proper definition order"
|
56
|
-
|
57
|
-
def __init__(self, base: List, indent: int = 0, body: int = 0, lf: str = "\n", name=""):
|
58
|
-
super().__init__(base)
|
59
|
-
self.lf = lf # add linefeed
|
60
|
-
self.indent = indent # current base indent
|
61
|
-
self._body = body # for source body is level
|
62
|
-
self._nr = 0 # generate incrementing line numbers
|
63
|
-
self.name = name
|
64
|
-
|
65
|
-
def __str__(self) -> str:
|
66
|
-
"convert the OD into a string ( the to be generated source code )"
|
67
|
-
out = ""
|
68
|
-
for item in self.items():
|
69
|
-
code = item[1]
|
70
|
-
if isinstance(code, str):
|
71
|
-
out += code + self.lf
|
72
|
-
elif isinstance(code, List):
|
73
|
-
for l in code:
|
74
|
-
if isinstance(l, str):
|
75
|
-
out += l + self.lf
|
76
|
-
else:
|
77
|
-
raise TypeError(f"Incorrect structure in Output dict: {l}") # noqa
|
78
|
-
else:
|
79
|
-
out += str(code)
|
80
|
-
return out
|
81
|
-
|
82
|
-
def __add__(self, other: SourceDict):
|
83
|
-
"Aallows instances of the SourceDict class to be added together using the + operator or the += operator."
|
84
|
-
# sd = sd + function
|
85
|
-
# sd += function
|
86
|
-
self.update({other.name: other})
|
87
|
-
return self
|
88
|
-
|
89
|
-
def add_docstr(self, docstr: Union[str, List[str]], extra: int = 0):
|
90
|
-
# indent +4, add triple " to docstring
|
91
|
-
if isinstance(docstr, str):
|
92
|
-
_docstr = [docstr]
|
93
|
-
elif isinstance(docstr, List): # type: ignore
|
94
|
-
_docstr = docstr.copy()
|
95
|
-
else:
|
96
|
-
raise TypeError
|
97
|
-
if len(_docstr) > 0 and not _docstr[0].strip().startswith('"""'):
|
98
|
-
# add triple quotes before & after
|
99
|
-
quotes = '"""'
|
100
|
-
_docstr.insert(0, quotes)
|
101
|
-
_docstr.append(quotes)
|
102
|
-
# add indent + extra
|
103
|
-
_docstr = [spaces(self.indent + extra) + l for l in _docstr]
|
104
|
-
self.update({"docstr": _docstr})
|
105
|
-
|
106
|
-
def add_comment(self, line: Union[str, List[str]]):
|
107
|
-
"Add a comment, or list of comments, to this block."
|
108
|
-
_c = self["comment"] or []
|
109
|
-
if isinstance(line, str):
|
110
|
-
_c += [spaces(self.indent + self._body) + line]
|
111
|
-
elif isinstance(line, list): # type: ignore
|
112
|
-
for l in line:
|
113
|
-
_c += [spaces(self.indent + self._body) + l]
|
114
|
-
self.update({"comment": _c})
|
115
|
-
|
116
|
-
def add_constant(self, line: str, autoindent: bool = True):
|
117
|
-
"add constant to the constant scope of this block"
|
118
|
-
if autoindent:
|
119
|
-
line = spaces(self.indent + self._body) + line
|
120
|
-
self.update({"constants": self["constants"] + [line]})
|
121
|
-
|
122
|
-
def add_constant_smart(
|
123
|
-
self,
|
124
|
-
name: str,
|
125
|
-
type: str = "",
|
126
|
-
docstr: Optional[List[str]] = None,
|
127
|
-
autoindent: bool = True,
|
128
|
-
):
|
129
|
-
"""add literal / constant to the constant scope of this block, or a class in this block"""
|
130
|
-
if docstr is None:
|
131
|
-
docstr = []
|
132
|
-
if "." in name and isinstance(self, ModuleSourceDict):
|
133
|
-
classname, const_name = name.split(".", 1)
|
134
|
-
if not (classfullname := self.find(classname.replace("# ", ""))):
|
135
|
-
raise KeyError(f"const {name} could not be added to Class {classname}")
|
136
|
-
cls_dict: ClassSourceDict = self[classfullname]
|
137
|
-
cls_dict.add_constant_smart(const_name, type, docstr)
|
138
|
-
else:
|
139
|
-
# does the constant contain an = , then it has a value
|
140
|
-
value = None
|
141
|
-
if "=" in name:
|
142
|
-
# in ESPNow.rst there is a constant with a value between brackets : 'MAX_DATA_LEN(=250)'
|
143
|
-
# remove brackets from constant values
|
144
|
-
name = name.replace("(", "").replace(")", "")
|
145
|
-
name, value = name.split("=", 1)
|
146
|
-
# determine more specific type from value
|
147
|
-
if type in {"Any", ""}:
|
148
|
-
try:
|
149
|
-
value_ = eval(value)
|
150
|
-
if isinstance(value_, bool):
|
151
|
-
type = "bool"
|
152
|
-
elif isinstance(value_, int):
|
153
|
-
type = "int"
|
154
|
-
if isinstance(value_, float):
|
155
|
-
type = "float"
|
156
|
-
elif isinstance(value_, str):
|
157
|
-
type = "str"
|
158
|
-
elif isinstance(value_, tuple):
|
159
|
-
type = "Tuple"
|
160
|
-
elif isinstance(value_, dict):
|
161
|
-
type = "Dict"
|
162
|
-
elif isinstance(value_, list):
|
163
|
-
type = "List"
|
164
|
-
except Exception:
|
165
|
-
pass
|
166
|
-
|
167
|
-
if not value:
|
168
|
-
if type == "Any":
|
169
|
-
value = "..."
|
170
|
-
# # if type == "bool":
|
171
|
-
# # value = "True"
|
172
|
-
# # if type == "int":
|
173
|
-
# # value = "1"
|
174
|
-
# # if type == "float":
|
175
|
-
# # value = "1.0"
|
176
|
-
# if type == "str":
|
177
|
-
# value = '""'
|
178
|
-
# elif type == "Tuple":
|
179
|
-
# value = "()"
|
180
|
-
# elif type == "Dict":
|
181
|
-
# value = "{}"
|
182
|
-
# elif type == "List":
|
183
|
-
# value = "[]"
|
184
|
-
if "*" in name:
|
185
|
-
# - if name starts with * it is a type annotation
|
186
|
-
line = f"# {name}: {type}"
|
187
|
-
elif not value:
|
188
|
-
line = f"{name}: {type}"
|
189
|
-
elif type:
|
190
|
-
line = f"{name}: {type} = {value}"
|
191
|
-
else:
|
192
|
-
line = f"{name} = {value}"
|
193
|
-
|
194
|
-
_docstr = docstr
|
195
|
-
if autoindent:
|
196
|
-
line = spaces(self.indent + self._body) + line
|
197
|
-
if len(_docstr):
|
198
|
-
if len(_docstr) == 1:
|
199
|
-
# - if len = 1 add triple quotes before & after, respecting indentaion
|
200
|
-
_docstr = [spaces(self.indent + self._body) + '"""' + _docstr[0].lstrip() + '"""']
|
201
|
-
else:
|
202
|
-
# - if len > 1 add triple quotes on sep lines before & after,respecting indentaion
|
203
|
-
_docstr = (
|
204
|
-
[spaces(self.indent + self._body) + '"""\\']
|
205
|
-
+ [spaces(self.indent + self._body) + l.lstrip() for l in _docstr]
|
206
|
-
+ [spaces(self.indent + self._body) + '"""']
|
207
|
-
)
|
208
|
-
if len(_docstr):
|
209
|
-
# - add docstring after defining constant
|
210
|
-
self.update({"constants": self["constants"] + [line] + _docstr})
|
211
|
-
else:
|
212
|
-
self.update({"constants": self["constants"] + [line]})
|
213
|
-
|
214
|
-
def find(self, name: str) -> Union[str, None]:
|
215
|
-
raise NotImplementedError("Please Implement this method")
|
216
|
-
|
217
|
-
def add_line(self, line: str, autoindent: bool = True):
|
218
|
-
self._nr += 1
|
219
|
-
if autoindent:
|
220
|
-
line = spaces(self.indent + self._body) + line
|
221
|
-
id_ = str(self._nr)
|
222
|
-
self.update({id_: line})
|
223
|
-
return id_
|
224
|
-
|
225
|
-
def index(self, key: str):
|
226
|
-
return list(self.keys()).index(key)
|
227
|
-
|
228
|
-
|
229
|
-
class ModuleSourceDict(SourceDict):
|
230
|
-
def __init__(self, name: str, indent=0, lf: str = "\n"):
|
231
|
-
"""The ModuleSourceDict class is used to represent a Python module as a dictionary of its components,
|
232
|
-
such as its docstring, version, comments, imports, constants, classes, and functions.
|
233
|
-
The class has several methods,
|
234
|
-
sort() which sorts the components of the module in the correct order for a module definition to allow adding class variables,
|
235
|
-
find() which finds a class node based on its name,
|
236
|
-
classes() which returns a list of the class names in parent-child order,
|
237
|
-
add_import() which adds a list of imports to the module.
|
238
|
-
The __str__() method is also defined to return a string representation of the module.
|
239
|
-
"""
|
240
|
-
super().__init__(
|
241
|
-
[
|
242
|
-
("docstr", [EMPTY_DOCSTR]),
|
243
|
-
("version", ""),
|
244
|
-
("comment", []),
|
245
|
-
("imports", []),
|
246
|
-
("constants", []),
|
247
|
-
],
|
248
|
-
indent,
|
249
|
-
body=0,
|
250
|
-
lf=lf,
|
251
|
-
name=name,
|
252
|
-
)
|
253
|
-
|
254
|
-
def sort(self):
|
255
|
-
"make sure all classdefs are in order"
|
256
|
-
# new empty one
|
257
|
-
new = ModuleSourceDict(self.name, self.indent, self.lf)
|
258
|
-
# add the standard stuff using a dict comprehension
|
259
|
-
new.update(
|
260
|
-
{
|
261
|
-
k: v
|
262
|
-
for (k, v) in self.items()
|
263
|
-
if k
|
264
|
-
in [
|
265
|
-
"docstr",
|
266
|
-
"version",
|
267
|
-
"comment",
|
268
|
-
"imports",
|
269
|
-
"constants",
|
270
|
-
]
|
271
|
-
}
|
272
|
-
)
|
273
|
-
# then the classes, already sorted in parent-child order
|
274
|
-
for classname in self.classes():
|
275
|
-
new.update({classname: self[classname]})
|
276
|
-
# then the functions and other
|
277
|
-
new.update({k: v for (k, v) in self.items() if k.isdecimal() or k.startswith("def ")})
|
278
|
-
# now clear and update with new order
|
279
|
-
if len(self) != len(new):
|
280
|
-
raise ValueError("Sort() changed the length of the dictionary")
|
281
|
-
self.clear()
|
282
|
-
self.update(new)
|
283
|
-
|
284
|
-
def __str__(self):
|
285
|
-
"""\
|
286
|
-
sort in the correct parent-child order,
|
287
|
-
then convert to string (the code)
|
288
|
-
"""
|
289
|
-
self.sort()
|
290
|
-
return super().__str__()
|
291
|
-
|
292
|
-
def find(self, name: str) -> Union[str, None]:
|
293
|
-
"find a classnode based on the name with or without the superclass"
|
294
|
-
keys = list(self.keys())
|
295
|
-
if not name.startswith("class "):
|
296
|
-
name = "class " + name
|
297
|
-
# try full match first
|
298
|
-
if name in keys:
|
299
|
-
return name
|
300
|
-
# is there a partial ? - only match before `(`
|
301
|
-
name = name.split("(")[0]
|
302
|
-
return next((k for k in keys if name == k.split("(")[0]), None)
|
303
|
-
|
304
|
-
def classes(self):
|
305
|
-
"get a list of the class names in parent-child order"
|
306
|
-
classes = [k for k in self.keys() if isinstance(self[k], ClassSourceDict)]
|
307
|
-
# return sorted list
|
308
|
-
return sort_classes(classes)
|
309
|
-
|
310
|
-
def add_import(self, imports: Union[str, List[str]]):
|
311
|
-
"add a [list of] imports this module"
|
312
|
-
_imports = self["imports"] or []
|
313
|
-
if isinstance(imports, str):
|
314
|
-
imports = [imports]
|
315
|
-
if "from __future__ import annotations" in imports:
|
316
|
-
# from future ... must be the first import
|
317
|
-
_imports = imports + _imports
|
318
|
-
else:
|
319
|
-
_imports += imports
|
320
|
-
self.update({"imports": _imports})
|
321
|
-
|
322
|
-
|
323
|
-
class ClassSourceDict(SourceDict):
|
324
|
-
def __init__(
|
325
|
-
self,
|
326
|
-
name: str,
|
327
|
-
*,
|
328
|
-
docstr: Optional[List[str]] = None,
|
329
|
-
init: str = "",
|
330
|
-
indent: int = 0,
|
331
|
-
lf="\n",
|
332
|
-
):
|
333
|
-
"set correct order for class and exception definitions to allow adding class variables"
|
334
|
-
# Defaults
|
335
|
-
if docstr is None:
|
336
|
-
docstr = [EMPTY_DOCSTR]
|
337
|
-
|
338
|
-
_init: List[str] = []
|
339
|
-
if init != "":
|
340
|
-
_init = [spaces(indent + 4) + init]
|
341
|
-
# add ...
|
342
|
-
_init.append(spaces(indent + 4 + 4) + "...")
|
343
|
-
super().__init__(
|
344
|
-
[
|
345
|
-
("comment", []),
|
346
|
-
("class", spaces(indent) + name), # includes indentation
|
347
|
-
("docstr", [EMPTY_DOCSTR]),
|
348
|
-
("constants", []),
|
349
|
-
("__init__", _init),
|
350
|
-
],
|
351
|
-
indent,
|
352
|
-
body=4, # class body indent +4
|
353
|
-
lf="\n",
|
354
|
-
name=name,
|
355
|
-
)
|
356
|
-
self.add_docstr(docstr, extra=4)
|
357
|
-
|
358
|
-
|
359
|
-
class FunctionSourceDict(SourceDict):
|
360
|
-
def __init__(
|
361
|
-
self,
|
362
|
-
name: str,
|
363
|
-
*,
|
364
|
-
definition: Optional[List[str]] = None,
|
365
|
-
docstr: Optional[List[str]] = None,
|
366
|
-
indent: int = 0,
|
367
|
-
decorators: Optional[List[str]] = None,
|
368
|
-
lf="\n",
|
369
|
-
is_async: bool = False,
|
370
|
-
):
|
371
|
-
"set correct order for function and method definitions"
|
372
|
-
# defaults
|
373
|
-
if definition is None:
|
374
|
-
definition = []
|
375
|
-
if docstr is None:
|
376
|
-
docstr = [EMPTY_DOCSTR]
|
377
|
-
if decorators is None:
|
378
|
-
decorators = []
|
379
|
-
|
380
|
-
# add indent
|
381
|
-
if is_async:
|
382
|
-
_def = [spaces(indent) + "async " + l for l in definition]
|
383
|
-
else:
|
384
|
-
_def = [spaces(indent) + l for l in definition]
|
385
|
-
|
386
|
-
# add ...
|
387
|
-
super().__init__(
|
388
|
-
[
|
389
|
-
("decorator", [spaces(indent) + d for d in decorators]),
|
390
|
-
("def", _def), # includes indentation
|
391
|
-
("docstr", '""'), # just a placeholder
|
392
|
-
# ("comments", []),
|
393
|
-
("constants", []),
|
394
|
-
("body", spaces(indent + 4) + "..."),
|
395
|
-
],
|
396
|
-
indent,
|
397
|
-
body=4, # function body indent +4
|
398
|
-
lf="\n",
|
399
|
-
name=name,
|
400
|
-
)
|
401
|
-
self.add_docstr(docstr, extra=4)
|
1
|
+
"""
|
2
|
+
ModuleSourceDict represents a source file with the following components
|
3
|
+
- docstr
|
4
|
+
- version
|
5
|
+
- comment
|
6
|
+
- typing
|
7
|
+
- Optional: list of constants
|
8
|
+
- optional: ClassSourcedicts
|
9
|
+
- optional: FunctionSourcedicts
|
10
|
+
- optional: individual lines of code
|
11
|
+
|
12
|
+
ClassSourceDict represents a source file with the following components
|
13
|
+
- comment
|
14
|
+
- class
|
15
|
+
- docstr
|
16
|
+
- Optional: list of constants
|
17
|
+
- __init__ : class signature
|
18
|
+
- optional: FunctionSourcedicts
|
19
|
+
- optional: individual lines of code
|
20
|
+
|
21
|
+
FunctionSourceDict represents a source file with the following components
|
22
|
+
- # comments - todo
|
23
|
+
- optional: decorator
|
24
|
+
- def - function definition
|
25
|
+
- docstr
|
26
|
+
- constants
|
27
|
+
- body - ...
|
28
|
+
- optional: individual lines of code
|
29
|
+
|
30
|
+
SourceDict is the 'base class'
|
31
|
+
|
32
|
+
"""
|
33
|
+
from __future__ import annotations
|
34
|
+
|
35
|
+
from typing import List, Optional, OrderedDict, Union
|
36
|
+
|
37
|
+
from .classsort import sort_classes
|
38
|
+
|
39
|
+
# These are shown to import
|
40
|
+
__all__ = [
|
41
|
+
"SourceDict",
|
42
|
+
"ModuleSourceDict",
|
43
|
+
"ClassSourceDict",
|
44
|
+
"FunctionSourceDict",
|
45
|
+
]
|
46
|
+
|
47
|
+
EMPTY_DOCSTR = '""" """'
|
48
|
+
|
49
|
+
|
50
|
+
def spaces(n: int = 4) -> str:
|
51
|
+
return " " * n
|
52
|
+
|
53
|
+
|
54
|
+
class SourceDict(OrderedDict):
|
55
|
+
"(abstract) dict to store source components respecting parent child dependencies and proper definition order"
|
56
|
+
|
57
|
+
def __init__(self, base: List, indent: int = 0, body: int = 0, lf: str = "\n", name=""):
|
58
|
+
super().__init__(base)
|
59
|
+
self.lf = lf # add linefeed
|
60
|
+
self.indent = indent # current base indent
|
61
|
+
self._body = body # for source body is level
|
62
|
+
self._nr = 0 # generate incrementing line numbers
|
63
|
+
self.name = name
|
64
|
+
|
65
|
+
def __str__(self) -> str:
|
66
|
+
"convert the OD into a string ( the to be generated source code )"
|
67
|
+
out = ""
|
68
|
+
for item in self.items():
|
69
|
+
code = item[1]
|
70
|
+
if isinstance(code, str):
|
71
|
+
out += code + self.lf
|
72
|
+
elif isinstance(code, List):
|
73
|
+
for l in code:
|
74
|
+
if isinstance(l, str):
|
75
|
+
out += l + self.lf
|
76
|
+
else:
|
77
|
+
raise TypeError(f"Incorrect structure in Output dict: {l}") # noqa
|
78
|
+
else:
|
79
|
+
out += str(code)
|
80
|
+
return out
|
81
|
+
|
82
|
+
def __add__(self, other: SourceDict):
|
83
|
+
"Aallows instances of the SourceDict class to be added together using the + operator or the += operator."
|
84
|
+
# sd = sd + function
|
85
|
+
# sd += function
|
86
|
+
self.update({other.name: other})
|
87
|
+
return self
|
88
|
+
|
89
|
+
def add_docstr(self, docstr: Union[str, List[str]], extra: int = 0):
|
90
|
+
# indent +4, add triple " to docstring
|
91
|
+
if isinstance(docstr, str):
|
92
|
+
_docstr = [docstr]
|
93
|
+
elif isinstance(docstr, List): # type: ignore
|
94
|
+
_docstr = docstr.copy()
|
95
|
+
else:
|
96
|
+
raise TypeError
|
97
|
+
if len(_docstr) > 0 and not _docstr[0].strip().startswith('"""'):
|
98
|
+
# add triple quotes before & after
|
99
|
+
quotes = '"""'
|
100
|
+
_docstr.insert(0, quotes)
|
101
|
+
_docstr.append(quotes)
|
102
|
+
# add indent + extra
|
103
|
+
_docstr = [spaces(self.indent + extra) + l for l in _docstr]
|
104
|
+
self.update({"docstr": _docstr})
|
105
|
+
|
106
|
+
def add_comment(self, line: Union[str, List[str]]):
|
107
|
+
"Add a comment, or list of comments, to this block."
|
108
|
+
_c = self["comment"] or []
|
109
|
+
if isinstance(line, str):
|
110
|
+
_c += [spaces(self.indent + self._body) + line]
|
111
|
+
elif isinstance(line, list): # type: ignore
|
112
|
+
for l in line:
|
113
|
+
_c += [spaces(self.indent + self._body) + l]
|
114
|
+
self.update({"comment": _c})
|
115
|
+
|
116
|
+
def add_constant(self, line: str, autoindent: bool = True):
|
117
|
+
"add constant to the constant scope of this block"
|
118
|
+
if autoindent:
|
119
|
+
line = spaces(self.indent + self._body) + line
|
120
|
+
self.update({"constants": self["constants"] + [line]})
|
121
|
+
|
122
|
+
def add_constant_smart(
|
123
|
+
self,
|
124
|
+
name: str,
|
125
|
+
type: str = "",
|
126
|
+
docstr: Optional[List[str]] = None,
|
127
|
+
autoindent: bool = True,
|
128
|
+
):
|
129
|
+
"""add literal / constant to the constant scope of this block, or a class in this block"""
|
130
|
+
if docstr is None:
|
131
|
+
docstr = []
|
132
|
+
if "." in name and isinstance(self, ModuleSourceDict):
|
133
|
+
classname, const_name = name.split(".", 1)
|
134
|
+
if not (classfullname := self.find(classname.replace("# ", ""))):
|
135
|
+
raise KeyError(f"const {name} could not be added to Class {classname}")
|
136
|
+
cls_dict: ClassSourceDict = self[classfullname]
|
137
|
+
cls_dict.add_constant_smart(const_name, type, docstr)
|
138
|
+
else:
|
139
|
+
# does the constant contain an = , then it has a value
|
140
|
+
value = None
|
141
|
+
if "=" in name:
|
142
|
+
# in ESPNow.rst there is a constant with a value between brackets : 'MAX_DATA_LEN(=250)'
|
143
|
+
# remove brackets from constant values
|
144
|
+
name = name.replace("(", "").replace(")", "")
|
145
|
+
name, value = name.split("=", 1)
|
146
|
+
# determine more specific type from value
|
147
|
+
if type in {"Any", ""}:
|
148
|
+
try:
|
149
|
+
value_ = eval(value)
|
150
|
+
if isinstance(value_, bool):
|
151
|
+
type = "bool"
|
152
|
+
elif isinstance(value_, int):
|
153
|
+
type = "int"
|
154
|
+
if isinstance(value_, float):
|
155
|
+
type = "float"
|
156
|
+
elif isinstance(value_, str):
|
157
|
+
type = "str"
|
158
|
+
elif isinstance(value_, tuple):
|
159
|
+
type = "Tuple"
|
160
|
+
elif isinstance(value_, dict):
|
161
|
+
type = "Dict"
|
162
|
+
elif isinstance(value_, list):
|
163
|
+
type = "List"
|
164
|
+
except Exception:
|
165
|
+
pass
|
166
|
+
|
167
|
+
if not value:
|
168
|
+
if type == "Any":
|
169
|
+
value = "..."
|
170
|
+
# # if type == "bool":
|
171
|
+
# # value = "True"
|
172
|
+
# # if type == "int":
|
173
|
+
# # value = "1"
|
174
|
+
# # if type == "float":
|
175
|
+
# # value = "1.0"
|
176
|
+
# if type == "str":
|
177
|
+
# value = '""'
|
178
|
+
# elif type == "Tuple":
|
179
|
+
# value = "()"
|
180
|
+
# elif type == "Dict":
|
181
|
+
# value = "{}"
|
182
|
+
# elif type == "List":
|
183
|
+
# value = "[]"
|
184
|
+
if "*" in name:
|
185
|
+
# - if name starts with * it is a type annotation
|
186
|
+
line = f"# {name}: {type}"
|
187
|
+
elif not value:
|
188
|
+
line = f"{name}: {type}"
|
189
|
+
elif type:
|
190
|
+
line = f"{name}: {type} = {value}"
|
191
|
+
else:
|
192
|
+
line = f"{name} = {value}"
|
193
|
+
|
194
|
+
_docstr = docstr
|
195
|
+
if autoindent:
|
196
|
+
line = spaces(self.indent + self._body) + line
|
197
|
+
if len(_docstr):
|
198
|
+
if len(_docstr) == 1:
|
199
|
+
# - if len = 1 add triple quotes before & after, respecting indentaion
|
200
|
+
_docstr = [spaces(self.indent + self._body) + '"""' + _docstr[0].lstrip() + '"""']
|
201
|
+
else:
|
202
|
+
# - if len > 1 add triple quotes on sep lines before & after,respecting indentaion
|
203
|
+
_docstr = (
|
204
|
+
[spaces(self.indent + self._body) + '"""\\']
|
205
|
+
+ [spaces(self.indent + self._body) + l.lstrip() for l in _docstr]
|
206
|
+
+ [spaces(self.indent + self._body) + '"""']
|
207
|
+
)
|
208
|
+
if len(_docstr):
|
209
|
+
# - add docstring after defining constant
|
210
|
+
self.update({"constants": self["constants"] + [line] + _docstr})
|
211
|
+
else:
|
212
|
+
self.update({"constants": self["constants"] + [line]})
|
213
|
+
|
214
|
+
def find(self, name: str) -> Union[str, None]:
|
215
|
+
raise NotImplementedError("Please Implement this method")
|
216
|
+
|
217
|
+
def add_line(self, line: str, autoindent: bool = True):
|
218
|
+
self._nr += 1
|
219
|
+
if autoindent:
|
220
|
+
line = spaces(self.indent + self._body) + line
|
221
|
+
id_ = str(self._nr)
|
222
|
+
self.update({id_: line})
|
223
|
+
return id_
|
224
|
+
|
225
|
+
def index(self, key: str):
|
226
|
+
return list(self.keys()).index(key)
|
227
|
+
|
228
|
+
|
229
|
+
class ModuleSourceDict(SourceDict):
|
230
|
+
def __init__(self, name: str, indent=0, lf: str = "\n"):
|
231
|
+
"""The ModuleSourceDict class is used to represent a Python module as a dictionary of its components,
|
232
|
+
such as its docstring, version, comments, imports, constants, classes, and functions.
|
233
|
+
The class has several methods,
|
234
|
+
sort() which sorts the components of the module in the correct order for a module definition to allow adding class variables,
|
235
|
+
find() which finds a class node based on its name,
|
236
|
+
classes() which returns a list of the class names in parent-child order,
|
237
|
+
add_import() which adds a list of imports to the module.
|
238
|
+
The __str__() method is also defined to return a string representation of the module.
|
239
|
+
"""
|
240
|
+
super().__init__(
|
241
|
+
[
|
242
|
+
("docstr", [EMPTY_DOCSTR]),
|
243
|
+
("version", ""),
|
244
|
+
("comment", []),
|
245
|
+
("imports", []),
|
246
|
+
("constants", []),
|
247
|
+
],
|
248
|
+
indent,
|
249
|
+
body=0,
|
250
|
+
lf=lf,
|
251
|
+
name=name,
|
252
|
+
)
|
253
|
+
|
254
|
+
def sort(self):
|
255
|
+
"make sure all classdefs are in order"
|
256
|
+
# new empty one
|
257
|
+
new = ModuleSourceDict(self.name, self.indent, self.lf)
|
258
|
+
# add the standard stuff using a dict comprehension
|
259
|
+
new.update(
|
260
|
+
{
|
261
|
+
k: v
|
262
|
+
for (k, v) in self.items()
|
263
|
+
if k
|
264
|
+
in [
|
265
|
+
"docstr",
|
266
|
+
"version",
|
267
|
+
"comment",
|
268
|
+
"imports",
|
269
|
+
"constants",
|
270
|
+
]
|
271
|
+
}
|
272
|
+
)
|
273
|
+
# then the classes, already sorted in parent-child order
|
274
|
+
for classname in self.classes():
|
275
|
+
new.update({classname: self[classname]})
|
276
|
+
# then the functions and other
|
277
|
+
new.update({k: v for (k, v) in self.items() if k.isdecimal() or k.startswith("def ")})
|
278
|
+
# now clear and update with new order
|
279
|
+
if len(self) != len(new):
|
280
|
+
raise ValueError("Sort() changed the length of the dictionary")
|
281
|
+
self.clear()
|
282
|
+
self.update(new)
|
283
|
+
|
284
|
+
def __str__(self):
|
285
|
+
"""\
|
286
|
+
sort in the correct parent-child order,
|
287
|
+
then convert to string (the code)
|
288
|
+
"""
|
289
|
+
self.sort()
|
290
|
+
return super().__str__()
|
291
|
+
|
292
|
+
def find(self, name: str) -> Union[str, None]:
|
293
|
+
"find a classnode based on the name with or without the superclass"
|
294
|
+
keys = list(self.keys())
|
295
|
+
if not name.startswith("class "):
|
296
|
+
name = "class " + name
|
297
|
+
# try full match first
|
298
|
+
if name in keys:
|
299
|
+
return name
|
300
|
+
# is there a partial ? - only match before `(`
|
301
|
+
name = name.split("(")[0]
|
302
|
+
return next((k for k in keys if name == k.split("(")[0]), None)
|
303
|
+
|
304
|
+
def classes(self):
|
305
|
+
"get a list of the class names in parent-child order"
|
306
|
+
classes = [k for k in self.keys() if isinstance(self[k], ClassSourceDict)]
|
307
|
+
# return sorted list
|
308
|
+
return sort_classes(classes)
|
309
|
+
|
310
|
+
def add_import(self, imports: Union[str, List[str]]):
|
311
|
+
"add a [list of] imports this module"
|
312
|
+
_imports = self["imports"] or []
|
313
|
+
if isinstance(imports, str):
|
314
|
+
imports = [imports]
|
315
|
+
if "from __future__ import annotations" in imports:
|
316
|
+
# from future ... must be the first import
|
317
|
+
_imports = imports + _imports
|
318
|
+
else:
|
319
|
+
_imports += imports
|
320
|
+
self.update({"imports": _imports})
|
321
|
+
|
322
|
+
|
323
|
+
class ClassSourceDict(SourceDict):
|
324
|
+
def __init__(
|
325
|
+
self,
|
326
|
+
name: str,
|
327
|
+
*,
|
328
|
+
docstr: Optional[List[str]] = None,
|
329
|
+
init: str = "",
|
330
|
+
indent: int = 0,
|
331
|
+
lf="\n",
|
332
|
+
):
|
333
|
+
"set correct order for class and exception definitions to allow adding class variables"
|
334
|
+
# Defaults
|
335
|
+
if docstr is None:
|
336
|
+
docstr = [EMPTY_DOCSTR]
|
337
|
+
|
338
|
+
_init: List[str] = []
|
339
|
+
if init != "":
|
340
|
+
_init = [spaces(indent + 4) + init]
|
341
|
+
# add ...
|
342
|
+
_init.append(spaces(indent + 4 + 4) + "...")
|
343
|
+
super().__init__(
|
344
|
+
[
|
345
|
+
("comment", []),
|
346
|
+
("class", spaces(indent) + name), # includes indentation
|
347
|
+
("docstr", [EMPTY_DOCSTR]),
|
348
|
+
("constants", []),
|
349
|
+
("__init__", _init),
|
350
|
+
],
|
351
|
+
indent,
|
352
|
+
body=4, # class body indent +4
|
353
|
+
lf="\n",
|
354
|
+
name=name,
|
355
|
+
)
|
356
|
+
self.add_docstr(docstr, extra=4)
|
357
|
+
|
358
|
+
|
359
|
+
class FunctionSourceDict(SourceDict):
|
360
|
+
def __init__(
|
361
|
+
self,
|
362
|
+
name: str,
|
363
|
+
*,
|
364
|
+
definition: Optional[List[str]] = None,
|
365
|
+
docstr: Optional[List[str]] = None,
|
366
|
+
indent: int = 0,
|
367
|
+
decorators: Optional[List[str]] = None,
|
368
|
+
lf="\n",
|
369
|
+
is_async: bool = False,
|
370
|
+
):
|
371
|
+
"set correct order for function and method definitions"
|
372
|
+
# defaults
|
373
|
+
if definition is None:
|
374
|
+
definition = []
|
375
|
+
if docstr is None:
|
376
|
+
docstr = [EMPTY_DOCSTR]
|
377
|
+
if decorators is None:
|
378
|
+
decorators = []
|
379
|
+
|
380
|
+
# add indent
|
381
|
+
if is_async:
|
382
|
+
_def = [spaces(indent) + "async " + l for l in definition]
|
383
|
+
else:
|
384
|
+
_def = [spaces(indent) + l for l in definition]
|
385
|
+
|
386
|
+
# add ...
|
387
|
+
super().__init__(
|
388
|
+
[
|
389
|
+
("decorator", [spaces(indent) + d for d in decorators]),
|
390
|
+
("def", _def), # includes indentation
|
391
|
+
("docstr", '""'), # just a placeholder
|
392
|
+
# ("comments", []),
|
393
|
+
("constants", []),
|
394
|
+
("body", spaces(indent + 4) + "..."),
|
395
|
+
],
|
396
|
+
indent,
|
397
|
+
body=4, # function body indent +4
|
398
|
+
lf="\n",
|
399
|
+
name=name,
|
400
|
+
)
|
401
|
+
self.add_docstr(docstr, extra=4)
|