sphinxext-bsb 0.2.6__py3-none-any.whl → 6.0.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.
Potentially problematic release.
This version of sphinxext-bsb might be problematic. Click here for more details.
- {bsb/sphinxext → sphinxext/bsb}/__init__.py +396 -375
- sphinxext/bsb/project.py +108 -0
- sphinxext_bsb-6.0.0.dist-info/METADATA +18 -0
- sphinxext_bsb-6.0.0.dist-info/RECORD +6 -0
- {sphinxext_bsb-0.2.6.dist-info → sphinxext_bsb-6.0.0.dist-info}/WHEEL +1 -1
- sphinxext_bsb-6.0.0.dist-info/licenses/LICENSE +674 -0
- sphinxext_bsb-0.2.6.dist-info/METADATA +0 -14
- sphinxext_bsb-0.2.6.dist-info/RECORD +0 -4
|
@@ -1,375 +1,396 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
from
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
from bsb.config
|
|
14
|
-
from
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
self.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
"",
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
""
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
def
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
lines
|
|
328
|
-
lines.
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
return
|
|
336
|
-
|
|
337
|
-
def
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
return
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
1
|
+
import contextlib
|
|
2
|
+
import importlib.metadata
|
|
3
|
+
import itertools
|
|
4
|
+
from inspect import isclass
|
|
5
|
+
from types import FunctionType
|
|
6
|
+
|
|
7
|
+
import docutils.parsers.rst.directives
|
|
8
|
+
from docutils import nodes
|
|
9
|
+
from docutils.parsers.rst import Directive
|
|
10
|
+
from docutils.statemachine import StringList
|
|
11
|
+
from sphinx.util.docutils import SphinxDirective
|
|
12
|
+
|
|
13
|
+
from bsb.config import get_config_attributes
|
|
14
|
+
from bsb.config._attrs import (
|
|
15
|
+
ConfigurationDictAttribute,
|
|
16
|
+
ConfigurationListAttribute,
|
|
17
|
+
)
|
|
18
|
+
from bsb.config._make import MISSING
|
|
19
|
+
from bsb.config.parsers import get_configuration_parser_classes
|
|
20
|
+
from bsb.config.types import class_
|
|
21
|
+
|
|
22
|
+
from .project import Project
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def example_function():
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
_example_values = {
|
|
30
|
+
bool: True,
|
|
31
|
+
list: [],
|
|
32
|
+
str: "example",
|
|
33
|
+
int: 42,
|
|
34
|
+
float: 3.14,
|
|
35
|
+
FunctionType: "my_module.my_function",
|
|
36
|
+
dict: {},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ComponentIntro(Directive):
|
|
41
|
+
has_content = False
|
|
42
|
+
|
|
43
|
+
def run(self):
|
|
44
|
+
# Use the state machine to generate a block quote for us and parse our text :)
|
|
45
|
+
return self.state.block_quote(
|
|
46
|
+
StringList(
|
|
47
|
+
[
|
|
48
|
+
":octicon:`light-bulb;1em;sd-text-info` New to components?"
|
|
49
|
+
+ " Write your first one with :doc:`our guide </guides/components>`"
|
|
50
|
+
]
|
|
51
|
+
),
|
|
52
|
+
self.content_offset,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class AutoconfigNode(nodes.General, nodes.Element):
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def visit_autoconfig_node(self, node):
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def depart_autoconfig_node(self, node):
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class AutoconfigDirective(SphinxDirective):
|
|
69
|
+
required_arguments = 1
|
|
70
|
+
has_content = False
|
|
71
|
+
option_spec = {
|
|
72
|
+
"no-imports": docutils.parsers.rst.directives.flag,
|
|
73
|
+
"max-depth": docutils.parsers.rst.directives.positive_int,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
def run(self):
|
|
77
|
+
clsref = self.arguments[0]
|
|
78
|
+
cls = class_()(clsref)
|
|
79
|
+
max_depth = self.options.get("max-depth", 100)
|
|
80
|
+
tree = self.guess_example(cls, max_depth)
|
|
81
|
+
elem = AutoconfigNode()
|
|
82
|
+
self.state.nested_parse(
|
|
83
|
+
StringList(
|
|
84
|
+
[
|
|
85
|
+
".. tab-set-code::",
|
|
86
|
+
"",
|
|
87
|
+
" .. code-block:: Python",
|
|
88
|
+
"",
|
|
89
|
+
*self.get_python_tab(
|
|
90
|
+
cls, tree, "no-imports" not in self.options, max_depth
|
|
91
|
+
),
|
|
92
|
+
"",
|
|
93
|
+
*itertools.chain.from_iterable(
|
|
94
|
+
self.get_parser_lines(key, parser(), tree)
|
|
95
|
+
for key, parser in get_configuration_parser_classes().items()
|
|
96
|
+
),
|
|
97
|
+
]
|
|
98
|
+
),
|
|
99
|
+
self.content_offset,
|
|
100
|
+
elem,
|
|
101
|
+
)
|
|
102
|
+
return [elem]
|
|
103
|
+
|
|
104
|
+
def get_python_tab(self, cls, tree, imports, max_depth):
|
|
105
|
+
lines = [
|
|
106
|
+
*(
|
|
107
|
+
f" {imp}"
|
|
108
|
+
for imp in self.get_import_lines(cls, max_depth if imports else 0)
|
|
109
|
+
),
|
|
110
|
+
"",
|
|
111
|
+
*(f" {line}" for line in self.get_python_lines(cls, tree)),
|
|
112
|
+
]
|
|
113
|
+
return self.collapse_empties(lines)
|
|
114
|
+
|
|
115
|
+
def collapse_empties(self, lines, chars=None):
|
|
116
|
+
if chars is None:
|
|
117
|
+
chars = [("(", ")"), ("[", "]"), ("{", "}")]
|
|
118
|
+
outlines = []
|
|
119
|
+
skip = False
|
|
120
|
+
for i in range(len(lines)):
|
|
121
|
+
if skip:
|
|
122
|
+
skip = False
|
|
123
|
+
continue
|
|
124
|
+
line1 = lines[i]
|
|
125
|
+
if i == len(lines) - 1:
|
|
126
|
+
outlines.append(line1)
|
|
127
|
+
break
|
|
128
|
+
line2 = lines[i + 1]
|
|
129
|
+
for schar, echar in chars:
|
|
130
|
+
if line1.endswith(schar) and line2.strip().startswith(echar):
|
|
131
|
+
outlines.append(line1 + f"{echar},")
|
|
132
|
+
skip = True
|
|
133
|
+
break
|
|
134
|
+
else:
|
|
135
|
+
outlines.append(line1)
|
|
136
|
+
return outlines
|
|
137
|
+
|
|
138
|
+
def guess_example(self, cls, deeper):
|
|
139
|
+
if deeper == 0:
|
|
140
|
+
return ...
|
|
141
|
+
attrs = get_config_attributes(cls)
|
|
142
|
+
tree = {
|
|
143
|
+
attr.attr_name: self.guess_default(attr, deeper) for attr in attrs.values()
|
|
144
|
+
}
|
|
145
|
+
return tree
|
|
146
|
+
|
|
147
|
+
def guess_default(self, attr, deeper):
|
|
148
|
+
"""
|
|
149
|
+
Guess a default value/structure for the given attribute. Defaults are paired in
|
|
150
|
+
tuples with functions that can unpack the tree value to their Python
|
|
151
|
+
representation. The most basic "argument unpacker" exemplifies this, and turns
|
|
152
|
+
the value ``x`` into ``f"{key}={repr(x)}"``.
|
|
153
|
+
|
|
154
|
+
:param attr:
|
|
155
|
+
:return:
|
|
156
|
+
"""
|
|
157
|
+
type_ = self.get_attr_type(attr)
|
|
158
|
+
|
|
159
|
+
# If the attribute is a node type, we have to guess recursively.
|
|
160
|
+
if attr.is_node_type():
|
|
161
|
+
# Node types that come from private modules shouldn't be promoted,
|
|
162
|
+
# so instead we make use of the dictionary notation. Or, this autoconfig
|
|
163
|
+
# has the "no-imports" option set, which disables the prepended import stmnt
|
|
164
|
+
if "no-imports" in self.options or any(
|
|
165
|
+
m.startswith("_") for m in type_.__module__.split(".")
|
|
166
|
+
):
|
|
167
|
+
untree = self.private_untree(type_)
|
|
168
|
+
else:
|
|
169
|
+
untree = self.public_untree(type_)
|
|
170
|
+
value = self.guess_example(type_, deeper - 1)
|
|
171
|
+
else:
|
|
172
|
+
untree = self.argument_untree
|
|
173
|
+
value = self.guess_example_value(attr)
|
|
174
|
+
# Configuration lists and dicts should be packed into a list/dict
|
|
175
|
+
if isinstance(attr, ConfigurationListAttribute):
|
|
176
|
+
untree = self.list_untree(untree)
|
|
177
|
+
elif isinstance(attr, ConfigurationDictAttribute):
|
|
178
|
+
untree = self.dict_untree(untree)
|
|
179
|
+
return untree, value
|
|
180
|
+
|
|
181
|
+
def guess_example_value(self, attr):
|
|
182
|
+
type_ = self.get_attr_type(attr)
|
|
183
|
+
# The attribute may have a hinted example from the declaration `hint` kwarg,
|
|
184
|
+
# or from the `__hint__` method of its type handler
|
|
185
|
+
hint = attr.get_hint()
|
|
186
|
+
if hint is not MISSING:
|
|
187
|
+
example = hint
|
|
188
|
+
else:
|
|
189
|
+
# No hint, so check if the default is sensible
|
|
190
|
+
default = attr.get_default()
|
|
191
|
+
example = None
|
|
192
|
+
if default is not None:
|
|
193
|
+
example = default
|
|
194
|
+
elif isclass(type_):
|
|
195
|
+
# No default value, and likely a primitive was passed as type handler
|
|
196
|
+
for parent_type, value in _example_values.items():
|
|
197
|
+
# Loop over some basic possible basic primitives
|
|
198
|
+
if issubclass(type_, parent_type):
|
|
199
|
+
example = value
|
|
200
|
+
break
|
|
201
|
+
if example is None:
|
|
202
|
+
# Try to have the type handler cast some primitive examples,
|
|
203
|
+
# if no error is raised we assume it's a good example
|
|
204
|
+
example = self.try_types(type_, *_example_values.values())
|
|
205
|
+
# `str` is a kind of greedy type handler, so correct the casts
|
|
206
|
+
if example == "[]":
|
|
207
|
+
example = []
|
|
208
|
+
elif example == "{}":
|
|
209
|
+
example = {}
|
|
210
|
+
elif example == "true":
|
|
211
|
+
example = True
|
|
212
|
+
# Some values need to be cast back to a tree-form, so we create a shim
|
|
213
|
+
# for the attribute descriptor to use as an instance.
|
|
214
|
+
shim = type("AttrShim", (), {})()
|
|
215
|
+
setattr(shim, f"_{attr.attr_name}", example)
|
|
216
|
+
with contextlib.suppress(Exception):
|
|
217
|
+
example = attr.tree(shim)
|
|
218
|
+
# Hope we have a default value.
|
|
219
|
+
return example
|
|
220
|
+
|
|
221
|
+
def get_attr_type(self, attr):
|
|
222
|
+
type_ = attr.get_type()
|
|
223
|
+
type_ = str if type_ is None else type_
|
|
224
|
+
return type_
|
|
225
|
+
|
|
226
|
+
def try_types(self, type_, arg, *args):
|
|
227
|
+
try:
|
|
228
|
+
return type_(arg)
|
|
229
|
+
except Exception:
|
|
230
|
+
if args:
|
|
231
|
+
return self.try_types(type_, *args)
|
|
232
|
+
|
|
233
|
+
def get_python_lines(self, cls, tree):
|
|
234
|
+
return [
|
|
235
|
+
f"{cls.__name__}(",
|
|
236
|
+
*(
|
|
237
|
+
f" {line}"
|
|
238
|
+
for line in itertools.chain.from_iterable(
|
|
239
|
+
self.get_argument_lines(key, *value) for key, value in tree.items()
|
|
240
|
+
)
|
|
241
|
+
),
|
|
242
|
+
")",
|
|
243
|
+
]
|
|
244
|
+
|
|
245
|
+
def get_argument_lines(self, key, untree, value):
|
|
246
|
+
return untree(key, value)
|
|
247
|
+
|
|
248
|
+
def get_parser_lines(self, name, parser, tree):
|
|
249
|
+
raw = self.raw_untree((None, tree))
|
|
250
|
+
language = getattr(parser, "data_syntax", False) or name
|
|
251
|
+
return [
|
|
252
|
+
f" .. code-block:: {language}",
|
|
253
|
+
"",
|
|
254
|
+
*(
|
|
255
|
+
f" {line}"
|
|
256
|
+
for line in parser.generate(raw, pretty=True).split("\n")
|
|
257
|
+
),
|
|
258
|
+
"",
|
|
259
|
+
]
|
|
260
|
+
|
|
261
|
+
def raw_untree(self, tree):
|
|
262
|
+
try:
|
|
263
|
+
node_data = tree[1]
|
|
264
|
+
except TypeError:
|
|
265
|
+
# If the element wasn't packed as a tuple it's just a list/dict attr
|
|
266
|
+
return tree
|
|
267
|
+
list_repack = getattr(tree[0], "list_repack", False)
|
|
268
|
+
dict_repack = getattr(tree[0], "dict_repack", False)
|
|
269
|
+
if node_data is ...:
|
|
270
|
+
if list_repack:
|
|
271
|
+
return []
|
|
272
|
+
else:
|
|
273
|
+
return {}
|
|
274
|
+
if dict_repack:
|
|
275
|
+
node_data = {"name_of_the_thing": (None, node_data)}
|
|
276
|
+
if list_repack:
|
|
277
|
+
node_data = [(None, node_data)]
|
|
278
|
+
if isinstance(node_data, dict):
|
|
279
|
+
return {k: self.raw_untree(v) for k, v in node_data.items()}
|
|
280
|
+
elif isinstance(node_data, list):
|
|
281
|
+
return [self.raw_untree(v) for v in node_data]
|
|
282
|
+
else:
|
|
283
|
+
return node_data
|
|
284
|
+
|
|
285
|
+
def public_untree(self, cls):
|
|
286
|
+
def untree(key, value):
|
|
287
|
+
if value is ...:
|
|
288
|
+
lines = self.get_python_lines(cls, {})
|
|
289
|
+
else:
|
|
290
|
+
lines = self.get_python_lines(cls, value)
|
|
291
|
+
lines[0] = f"{key}={lines[0]}"
|
|
292
|
+
lines[-1] += ","
|
|
293
|
+
return lines
|
|
294
|
+
|
|
295
|
+
return untree
|
|
296
|
+
|
|
297
|
+
def private_untree(self, cls):
|
|
298
|
+
def untree(key, value):
|
|
299
|
+
if value is ...:
|
|
300
|
+
lines = self.get_python_lines(cls, {})
|
|
301
|
+
else:
|
|
302
|
+
lines = self.get_python_lines(cls, value)
|
|
303
|
+
lines[0] = key + "={"
|
|
304
|
+
for i in range(1, len(lines) - 1):
|
|
305
|
+
line = lines[i]
|
|
306
|
+
if "=" in line:
|
|
307
|
+
lp = line.split("=")
|
|
308
|
+
indent = len(lp[0]) - len(lp[0].lstrip())
|
|
309
|
+
lines[i] = f"{' ' * indent}'{lp[0].lstrip()}': " + "=".join(lp[1:])
|
|
310
|
+
lines[-1] = "},"
|
|
311
|
+
return lines
|
|
312
|
+
|
|
313
|
+
return untree
|
|
314
|
+
|
|
315
|
+
def dict_untree(self, inner_untree):
|
|
316
|
+
def untree(key, value):
|
|
317
|
+
lines = inner_untree(key, value)
|
|
318
|
+
return lines
|
|
319
|
+
|
|
320
|
+
untree.dict_repack = True
|
|
321
|
+
return untree
|
|
322
|
+
|
|
323
|
+
def list_untree(self, inner_untree):
|
|
324
|
+
def untree(key, value):
|
|
325
|
+
if value is ...:
|
|
326
|
+
return [f"{key}=[", "],"]
|
|
327
|
+
lines = inner_untree(key, value)
|
|
328
|
+
lines[0] = lines[0].split("=")[0] + "=["
|
|
329
|
+
lines.insert(1, " {")
|
|
330
|
+
lines[2:] = [" " + line for line in lines[2:]]
|
|
331
|
+
lines.append("],")
|
|
332
|
+
return lines
|
|
333
|
+
|
|
334
|
+
untree.list_repack = True
|
|
335
|
+
return untree
|
|
336
|
+
|
|
337
|
+
def argument_untree(self, key, value):
|
|
338
|
+
return [f"{key}={repr(value)},"]
|
|
339
|
+
|
|
340
|
+
def get_imports(self, cls, deeper):
|
|
341
|
+
imports = {}
|
|
342
|
+
if not any(m.startswith("_") for m in cls.__module__.split(".")):
|
|
343
|
+
imports.setdefault(cls.__module__, set()).add(cls.__name__)
|
|
344
|
+
if deeper > 0:
|
|
345
|
+
for attr in get_config_attributes(cls).values():
|
|
346
|
+
if attr.is_node_type():
|
|
347
|
+
for k, v in self.get_imports(attr.get_type(), deeper - 1).items():
|
|
348
|
+
imports.setdefault(k, set()).update(v)
|
|
349
|
+
|
|
350
|
+
return imports
|
|
351
|
+
|
|
352
|
+
def get_import_lines(self, cls, depth):
|
|
353
|
+
return [
|
|
354
|
+
f"from {key} import {', '.join(value)}"
|
|
355
|
+
for key, value in self.get_imports(cls, depth).items()
|
|
356
|
+
]
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def resolve_type_aliases(app, env, node, contnode):
|
|
360
|
+
if node["refdomain"] == "py" and node["reftype"] == "class":
|
|
361
|
+
return app.env.get_domain("py").resolve_xref(
|
|
362
|
+
env, node["refdoc"], app.builder, "data", node["reftarget"], node, contnode
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def setup(app):
|
|
367
|
+
if "sphinx_design" not in app.extensions:
|
|
368
|
+
from sphinx_design import setup as sphinx_design_setup
|
|
369
|
+
|
|
370
|
+
sphinx_design_setup(app)
|
|
371
|
+
|
|
372
|
+
app.add_node(
|
|
373
|
+
AutoconfigNode,
|
|
374
|
+
html=(visit_autoconfig_node, depart_autoconfig_node),
|
|
375
|
+
latex=(visit_autoconfig_node, depart_autoconfig_node),
|
|
376
|
+
text=(visit_autoconfig_node, depart_autoconfig_node),
|
|
377
|
+
)
|
|
378
|
+
app.add_directive("bsb_component_intro", ComponentIntro)
|
|
379
|
+
app.add_directive("autoconfig", AutoconfigDirective)
|
|
380
|
+
|
|
381
|
+
app.connect("missing-reference", resolve_type_aliases)
|
|
382
|
+
|
|
383
|
+
return {
|
|
384
|
+
"version": importlib.metadata.version("sphinxext-bsb"),
|
|
385
|
+
"parallel_read_safe": True,
|
|
386
|
+
"parallel_write_safe": True,
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
__all__ = [
|
|
391
|
+
"AutoconfigDirective",
|
|
392
|
+
"AutoconfigNode",
|
|
393
|
+
"ComponentIntro",
|
|
394
|
+
"Project",
|
|
395
|
+
"setup",
|
|
396
|
+
]
|