sphinxext-bsb 0.2.0__py3-none-any.whl → 0.2.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.

Potentially problematic release.


This version of sphinxext-bsb might be problematic. Click here for more details.

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