-
Notifications
You must be signed in to change notification settings - Fork 18
/
preamble.py
105 lines (102 loc) · 4 KB
/
preamble.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
# This is helper cell that defines code checking (testing) function Check()
# Please run it once, but you do not need to understand it.
import re
import sys
import jinja2
from IPython.core import display
from prog_edu_assistant_tools.magics import report, autotest, CaptureOutput
from google.colab import _message as google_message
def GetNotebook():
"""Downloads the ipynb source of Colab notebook"""
notebook = google_message.blocking_request(
"get_ipynb", request="", timeout_sec=120)["ipynb"]
return notebook
def RunInlineTests(submission_source, inlinetests):
"""Runs an inline test."""
errors = []
for test_name, test_source in inlinetests.items():
#print(f'Running inline test {test_name}:\n{test_source}', file=sys.stderr)
with CaptureOutput() as (stdout, stderr):
try:
env = {}
exec(submission_source, globals(), env)
exec(test_source, globals(), env)
except AssertionError as e:
errors.append(str(e))
if len(stderr.getvalue()) > 0:
errors.append('STDERR:' + stderr.getvalue())
if len(errors) > 0:
results = {'passed': False, 'error': '\n'.join(errors)}
else:
results = {'passed': True}
template_source = """
<h4 style='color: #387;'>Your submission</h4>
<pre style='background: #F0F0F0; padding: 3pt; margin: 4pt; border: 1pt solid #DDD; border-radius: 3pt;'>{{ formatted_source }}</pre>
<h4 style='color: #387;'>Results</h4>
{% if 'passed' in results and results['passed'] %}
✅
Looks OK.
{% elif 'error' in results %}
❌
{{results['error'] | e}}
{% else %}
❌ Something is wrong.
{% endif %}"""
template = jinja2.Template(template_source)
html = template.render(formatted_source=submission_source, results=results)
return html
def Check(exercise_id):
"""Checks one exercise against embedded inline tests."""
def _get_exercise_id(cell):
if 'metadata' in cell and 'exercise_id' in cell['metadata']:
return cell['metadata']['exercise_id']
if 'source' not in cell or 'cell_type' not in cell or cell['cell_type'] != 'code':
return None
source = ''.join(cell['source'])
m = re.search('(?m)^# *EXERCISE_ID: [\'"]?([a-zA-Z0-9_.-]*)[\'"]? *\n', source)
if m:
return m.group(1)
return None
notebook = GetNotebook()
# 1. Find the first cell with specified exercise ID.
found = False
for (i, cell) in enumerate(notebook['cells']):
if _get_exercise_id(cell) == exercise_id:
found = True
break
if not found:
raise Exception(f'exercise {exercise_id} not found')
submission_source = ''.join(cell['source']) # extract the submission cell
submission_source = re.sub(r'^%%(solution|submission)[ \t]*\n', '', submission_source) # cut %%solution magic
inlinetests = {}
if 'metadata' in cell and 'inlinetests' in cell['metadata']:
inlinetests = cell['metadata']['inlinetests']
if len(inlinetests) == 0:
j = i+1
# 2. If inline tests were not present in metadata, find the inline tests
# that follow this exercise ID.
while j < len(notebook['cells']):
cell = notebook['cells'][j]
if 'source' not in cell or 'cell_type' not in cell or cell['cell_type'] != 'code':
j += 1
continue
id = _get_exercise_id(cell)
source = ''.join(cell['source'])
if id == exercise_id:
# 3. Pick the last marked cell as submission cell.
submission_source = source # extract the submission cell
submission_source = re.sub(r'^%%(solution|submission)[ \t]*\n', '', submission_source) # cut %%solution magic
j += 1
continue
m = re.match(r'^%%inlinetest[ \t]*([a-zA-Z0-9_]*)[ \t]*\n', source)
if m:
test_name = m.group(1)
test_source = source[m.end(0):] # cut %%inlinetest magic
# 2a. Store the inline test.
inlinetests[test_name] = test_source
if id is not None and id != exercise_id:
# 4. Stop at the next exercise_id.
break
j += 1
html = RunInlineTests(submission_source, inlinetests)
return display.HTML(html)