forked from ucbds-infra/otter-grader
/
plugins.py
93 lines (73 loc) · 2.43 KB
/
plugins.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
"""
Plugin replacement for Otter Assign
"""
import re
import yaml
import nbformat
from .utils import get_source
BEGIN = "# BEGIN PLUGIN"
END = "# END PLUGIN"
BEGIN_EXPORT = "# BEGIN PLUGIN EXPORT"
# TODO: comments, docstrings
def replace_plugins(lines):
"""
Replaces plugins with calls in ``lines``
Args:
lines (``list`` of ``str``): cell contents as a list of strings
Returns:
``list`` of ``str``: stripped version of lines with plugin calls
"""
starts, ends = [], []
stripped = [[]]
exports = []
plugin = False
for i, line in enumerate(lines):
if line.rstrip().endswith(END):
assert plugin, f"END PLUGIN without BEGIN PLUGIN found in {lines}"
plugin = False
ends.append(i)
stripped.append([])
elif line.rstrip().endswith(BEGIN):
assert not plugin, f"Nested plugins found in {lines}"
starts.append(i)
exports.append(False)
plugin = True
elif line.rstrip().endswith(BEGIN_EXPORT):
assert not plugin, f"Nested plugins found in {lines}"
starts.append(i)
exports.append(True)
plugin = True
elif plugin:
stripped[len(starts) - 1].append(line)
assert len(stripped) == len(starts) + 1 == len(ends) + 1 == len(exports) + 1, f"Error processing plugins in {lines}"
assert all(s < e for s, e in zip(starts, ends))
starts.reverse()
ends.reverse()
stripped.reverse()
stripped = stripped[1:]
lines = lines.copy()
for i, (s, e) in enumerate(zip(starts, ends)):
config = yaml.full_load("\n".join(stripped[i]))
export = exports[i]
pg = config["plugin"]
args = ", ".join(config.get("args", []))
kwargs = ", ".join([f"{k}={v}" for k, v in config.get("kwargs", {}).items()])
call = ("run_plugin", "add_plugin_files")[export]
call = f'grader.{call}("{pg}"'
if args:
call += f', {args}'
if kwargs:
call += f', {kwargs}'
call += ')'
del lines[s:e+1]
lines.insert(s, call)
return lines
def replace_plugins_with_calls(nb):
"""
Write a notebook with plugins replaced with calls
Args:
nb (``nbformat.NotebookNode``): the notebook
"""
for cell in nb['cells']:
cell['source'] = '\n'.join(replace_plugins(get_source(cell)))
return nb