-
Notifications
You must be signed in to change notification settings - Fork 162
/
macros.py
401 lines (318 loc) · 11.9 KB
/
macros.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
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
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
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
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
"""Functions used by the macros mkdocs plugin."""
import os
import sys
from bidsschematools import render, schema
code_path = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
sys.path.append(code_path)
from examplecode import example
def _get_source_path(level=1):
"""Detect the path of the file we are rendering a macro in.
This (ab)uses the Python call stack to find its way to the Jinja2 function
that is calling the macro. From there, it looks at Jinja2's Context object,
which contains all the variables available to the Markdown snippet that is
calling the macro.
One variable provided by mkdocs-macros is called ``page``, which includes a
``file`` attribute that would allow us to insert the page name into the text
of the page, or in this case, pass it as a variable. The ``file`` attribute
has a ``src_path`` attribute of its own that is a path relative to the ``src/``
directory.
The level parameter indicates how many steps above the calling function Jinja2
is. Currently it's always 1, but refactors may justify passing a larger number.
This allows us to use
```{markdown}
{{ MACRO__make_glossary() }}
```
instead of:
```{markdown}
{{ MACRO__make_glossary(page.file.src_path) }}
```
Why are we doing all this? We need to render links that are defined in the schema
relative to the source tree as paths relative to the Markdown file they're being
rendered in. So [SPEC_ROOT/02-common-principles.md](Common principles) becomes
[./02-common-principles.md](Common principles) or
[../02-common-principles.md](Common principles), depending on which file it
appears in.
If a future maintainer decides that this is terrible, or a bug can't be fixed,
just go back to explicitly using the ``page.file`` variable throughout the macros.
"""
import inspect
# currentframe = _get_source_path()
# caller = the macro calling this function, e.g. make_glossary()
caller = inspect.currentframe().f_back
# We need to go one level higher to find Jinja2
for _ in range(level):
caller = caller.f_back
# Jinja2 equivalent: {{ page.file.src_path }}
return caller.f_locals["_Context__self"]["page"].file.src_path
def make_filename_template(dstype="raw", src_path=None, pdf_format=False, **kwargs):
"""Generate a filename template snippet from the schema, based on specific filters.
Parameters
----------
pdf_format : bool, optional
If True, the filename template will be compiled as a standard markdown code block,
without any hyperlinks, so that the specification's PDF build will look right.
If False, the filename template will use HTML and include hyperlinks.
This works on the website.
Default is False.
Other Parameters
----------------
**kwargs : dict
Keyword arguments used to filter the schema.
Example kwargs that may be used include: "suffixes", "datatypes",
"extensions".
Returns
-------
codeblock : str
A multiline string containing the filename templates for file types
in the schema, after filtering.
"""
if src_path is None:
src_path = _get_source_path()
schema_obj = schema.load_schema()
codeblock = render.make_filename_template(
dstype,
schema_obj,
src_path=src_path,
pdf_format=pdf_format,
**kwargs,
)
return codeblock
def make_entity_table(src_path=None, **kwargs):
"""Generate an entity table from the schema, based on specific filters.
Parameters
----------
src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
Other Parameters
----------------
**kwargs : dict
Keyword arguments used to filter the schema.
Example kwargs that may be used include: "suffixes", "datatypes",
"extensions".
Returns
-------
table : str
A Markdown-format table containing the corresponding entity table for
a subset of the schema.
"""
if src_path is None:
src_path = _get_source_path()
schema_obj = schema.load_schema()
table = render.make_entity_table(schema_obj, src_path=src_path, **kwargs)
return table
def make_entity_definitions(src_path=None):
"""Generate definitions and other relevant information for entities in the
specification.
Parameters
----------
src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
Returns
-------
text : str
A multiline string containing descriptions and some formatting
information about the entities in the schema.
"""
if src_path is None:
src_path = _get_source_path()
schema_obj = schema.load_schema()
text = render.make_entity_definitions(schema_obj, src_path=src_path)
return text
def make_glossary(src_path=None):
"""Generate glossary.
Parameters
----------
src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
Returns
-------
text : str
A multiline string containing descriptions and some formatting
information about the entities in the schema.
"""
if src_path is None:
src_path = _get_source_path()
schema_obj = schema.load_schema()
text = render.make_glossary(schema_obj, src_path=src_path)
return text
def make_suffix_table(suffixes, src_path=None):
"""Generate a markdown table of suffix information.
Parameters
----------
suffixes : list of str
A list of the suffixes to include in the table.
src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
Returns
-------
table : str
A Markdown-format table containing the corresponding table for
the requested fields.
"""
if src_path is None:
src_path = _get_source_path()
schema_obj = schema.load_schema()
table = render.make_suffix_table(schema_obj, suffixes, src_path=src_path)
return table
def make_metadata_table(field_info, src_path=None):
"""Generate a markdown table of metadata field information.
Parameters
----------
field_info : dict
A list of the field names.
Field names correspond to filenames in the "metadata" directory of the
schema.
Until requirement levels can be codified in the schema,
this argument will be dictionary, with the field names as keys and
the requirement levels as values.
src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
Returns
-------
table : str
A Markdown-format table containing the corresponding table for
the requested fields.
"""
if src_path is None:
src_path = _get_source_path()
schema_obj = schema.load_schema()
table = render.make_metadata_table(schema_obj, field_info, src_path=src_path)
return table
def make_json_table(table_name, src_path=None):
"""Generate a markdown table of metadata field information.
Parameters
----------
table_name : str or list of str
Qualified name(s) in schema.rules.sidecars
src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
Returns
-------
table : str
A Markdown-format table containing the corresponding table for
the requested fields.
"""
if src_path is None:
src_path = _get_source_path()
schema_obj = schema.load_schema()
table = render.make_json_table(schema_obj, table_name, src_path=src_path)
return table
def make_sidecar_table(table_name, src_path=None):
"""Generate a markdown table of metadata field information.
Parameters
----------
table_name : str or list of str
Qualified name(s) in schema.rules.sidecars
src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
Returns
-------
table : str
A Markdown-format table containing the corresponding table for
the requested fields.
"""
if src_path is None:
src_path = _get_source_path()
schema_obj = schema.load_schema()
table = render.make_sidecar_table(schema_obj, table_name, src_path=src_path)
return table
def make_subobject_table(object_name, src_path=None):
"""Generate a markdown table of a metadata object's field information.
Parameters
----------
object_tuple : tuple of string
A tuple pointing to the object to render.
src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
Returns
-------
table : str
A Markdown-format table containing the corresponding table for
the requested fields.
"""
if src_path is None:
src_path = _get_source_path()
schema_obj = schema.load_schema()
table = render.make_subobject_table(
schema_obj,
object_name,
src_path=src_path,
)
return table
def make_columns_table(table_name, src_path=None):
"""Generate a markdown table of TSV column information.
Parameters
----------
column_info : dict
A list of the column names.
Column names correspond to filenames in the "columns" directory of the
schema.
Until requirement levels can be codified in the schema,
this argument will be a dictionary, with the column names as keys and
the requirement levels as values.
src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
Returns
-------
table : str
A Markdown-format table containing the corresponding table for
the requested columns.
"""
if src_path is None:
src_path = _get_source_path()
schema_obj = schema.load_schema()
table = render.make_columns_table(schema_obj, table_name, src_path=src_path)
return table
def make_filetree_example(filetree_info, use_pipe=True):
"""Generate a filetree snippet from example content.
Parameters
----------
filetree_info : dict
Dictionary to represent the directory content.
use_pipe : bool
Set to ``False`` to avoid using pdf unfriendly pipes: "│ └─ ├─"
Returns
-------
tree : str
A multiline string containing the filetree example.
"""
tree = example.DirectoryTree(filetree_info, use_pipe)
return tree.generate()
def define_common_principles(src_path=None):
"""Enumerate the common principles defined in the schema.
Parameters
----------
src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
Returns
-------
string : str
The definitions of the common principles in a multiline string.
"""
if src_path is None:
src_path = _get_source_path()
schema_obj = schema.load_schema()
string = render.define_common_principles(schema_obj, src_path=src_path)
return string
def define_allowed_top_directories(src_path=None):
if src_path is None:
src_path = _get_source_path()
schema_obj = schema.load_schema()
string = render.define_allowed_top_directories(schema_obj, src_path=src_path)
return string
def render_text(key, src_path=None):
if src_path is None:
src_path = _get_source_path()
schema_obj = schema.load_schema()
string = render.render_text(schema_obj, key, src_path=src_path)
return string