-
Notifications
You must be signed in to change notification settings - Fork 133
/
instructor_api_documentation.py
661 lines (585 loc) · 19.7 KB
/
instructor_api_documentation.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
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
'''
This file documents the Python Instructor API.
The format is Google Style Python Docstrings, based on
`http://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html`
'''
## Core functions
def compliment(message):
'''
Adds message to the list of complimentary feedback.
Args:
message (str): The text string to display to the user in the Feedback
panel as complimentary feedback. This will appear as
a tooltip.
'''
def set_success():
'''
Marks the problem as completely correct. This immediately ends execution
by throwing a GracefulExit exception.
'''
def explain(message, priority="medium", line=None):
'''
Gives student the message as corrective feedback (and higlights line
number). If this function is called multiple times, the student is given the
first feedback of the highest priority found. By default, this overrides
the analyzer and runtime errors that are given - if you want to avoid overriding
those, then use `gently`.
Args:
message (str): The HTML string to display to the user in the Feedback
panel as corrective feedback.
priority (str): Either "low", "medium", or "high", indicating the
ordering of the feedback if multiple occur. Also "student"
is an option to put it after the analyzer and runtime
errors, and "verifier" will put it before syntax errors.
line (int): The specific line number to highlight for the user.
'''
def gently(message):
'''
Gives students the message as corrective feedback, at a priority level
below the analyzer and runtime errors - that way, it can be used to
more "gently" guide the student.
'''
def suppress(type, subtype):
'''
Suppresses feedback of the given "type". When also given a subtype, it
instead suppresses the subtype (which is typically a specific error message
or type of exception). Multiple subtypes can be specified with multiple
calls, each call suppressing additional feedback.
Args:
type (str): Either 'verifier', 'parser', 'analyzer', or 'student',
corresponding to the phase of execution feedback that will
be suppressed.
subtype (str): A specific type of exception (if 'student') or
specific type of issue (if 'analyzer'). Available issues:
- 'Unconnected blocks'
- 'Iteration variable is iteration list'
- "Undefined variables"
- "Possibly undefined variables"
- "Unread variables"
- "Overwritten variables"
- "Empty iterations"
- "Non-list iterations"
- "Incompatible types"
'''
def run_student():
'''
Reruns the students' submitted code ("__main__") as a function call.
This allows the instructor to run the student code under new conditions.
After a call to run_student(), the instructor will most likely want
to use get_output() or some other function to access the new state.
If the students' code failed to parse, then the body is instead replaced
with "pass" to prevent compilation errors.
If the students' code raises an exception, that exception is caught and
returned by the function. This is necessary to avoid the Instructor feedback
hijacking the runtime exceptions that students would see.
Returns:
Exception or None: If the students' code fails for some reason, the
raised exception is returned. Otherwise, the None
value is returned.
'''
def get_output():
'''
Returns a List containing the students' output from the last run. Note that
multiple invocations of run_student(), without calls to reset_output(), will
possibly result in duplicate output.
Different types of student output are represented differently:
- Print: Represented as strings WITHOUT the trailing newline.
- Plot: Represented as Lists of "Plot Dictionaries". Each call to
plot(), hist(), and scatter() adds another "Plot Dictionary" to
the list, and the list is only "snipped off" when show() is
called. Each "Plot Dictionary" contains two fields:
"type": One of "hist", "line", or "scatter".
"data": A 1- or 2- dimensional list of numeric data.
Returns:
list: The output that the students code generated on its last execution.
'''
def queue_input(input):
'''
Add a new string to be set as input when the "input" function is called.
You can repeatedly queue_input to satisfy loops, too.
If there are no queued inputs, a blank string will be returned.
Args:
input (str): The string to queue for input.
'''
def reset_output():
'''
Removes any output generated on a previous run of the student code. This is
typically used between executions of the `run_student` function.
'''
def log(message):
'''
Print the given message to the JS console in the browser. This is useful
for debugging purposes.
Args:
message: The logging message. Any kind of data is allowable, and will
be printed as JavaScript (using remapToJs).)
'''
def log_ast():
'''
Prints the AST to the JS console in the browser. This is useful for
debugging purposes.
'''
def log_variables():
'''
Prints a mapping between the names of the programs' variables and their
estimated type (according to the abstract interpreter) to the JS console
in the browser. This is useful for debugging purposes.
'''
def log_behavior():
'''
Prints a list of each variables' read/write behavior to the JS console
in the browser. This is useful for debugging purposes.
'''
def log_trace():
'''
Prints a list of each step of the programs' execution to the JS console
in the browser. This is useful for debugging purposes.
'''
def log_issues():
'''
Prints a list of all found Abstract Interperter issues to the JS console
in the browser. This is useful for debugging purposes.
'''
class StudentData():
'''
A class that wraps a dictionary of data created after the execution of
the students' code.
A common use case for this is to require students to declare a particular
function, and then to access that function via the singleton instance of
this class.
Attributes:
data (dict): A dictionary containing all the students' data. In other
words, if they declare a variable named "alpha", you can
access that variable's final value via
student['alpha']
Because this is a dictionary, you can also test membership,
access element dynamically, and anything else you may want.
'''
def get_values_by_type(type):
'''
Returns a list of values from the students' data where each value
will have the given type.
Args:
type (type): A python type (e.g., int or str) that will be used
in the comparison. Does not respect inheritance!
Returns:
list: A list of the values with that type.
'''
def get_names_by_type(type):
'''
Returns a list of the variable names from the students' data where each
variable's final value will have the given type.
Args:
type (type): A python type (e.g., int or str) that will be used
in the comparison. Does not respect inheritance!
Returns:
list of str: A list of the variables with that type.
'''
student = StudentData()
'''
A top-level variable that holds all of the students' created data.
Acts as a convenient singleton for the code.
'''
def analyze_program():
'''
Triggers the analyzer to run so that type information can be available
on the AST nodes.
'''
def parse_program():
'''
Returns the root node of the AST of the student code as an AstNode.
See the AstNode class for more information.
Returns:
AstNode: The root node of the AST
'''
def get_program():
'''
Returns the students' code as a string.
Returns:
String: The string representation of the student code.
'''
def def_use_error(node):
'''
Determines if the given AstNode (with the astname "Name"), and if so,
if the name associated with that node has not been initialized according
to the Analyzer.
Args:
node (AstNode): The Name node to analyze.
Returns:
bool: Returns whether the associated name has been initialized.
'''
class AstNode():
'''
A representation of the students' Abstract Syntax Tree. Can be traversed
and analyzed in order to make assertions about the students' code.
The fields of the AstNode, in addition to the two listed below, are the
fields listed in the Green Tree Snakes documentation.
https://greentreesnakes.readthedocs.io/en/latest/nodes.html
Attributes:
ast_name (str): The type of AST Node of this node (e.g. "Name", "For",
"Assign", etc.). For a complete list, see the Green Tree
Snakes API, which closely mimics our own.
Some notable exceptions are:
- Assign: Currently, Assign.targets returns a single
AstNode instead of a list.
- Op Nodes: For nodes with an "ops" field, this
only returns the FIRST operator, so no
comparison operator chaining.
data_type
if this node is a Name node, returns the first data type that this variable has taken on
next_tree
For the AST node to which this node belongs, returns the next node in that AST that is NOT
in this node's subtree.
'''
def __init__(self, id):
'''
This should NOT be used by an instructor, this is strictly used
internally to match up with the already parsed Skulpt AST.
Args:
id (int): The index for the AST node when doing an in-order
traversal of the tree.
'''
def __eq__(self, other):
'''
If other is an AstNode, checks whether they are the same AstNode. If
both nodes originate from the same AST, then a return true will indicate
it is the same node, and false will indicate it's a different node. If
it's not an AstNode, this will crash the program
Args:
other (AstNode): The other AstNode to compare to.
Returns:
bool: A boolean indicating if they are equal.
'''
def has(self, astNode):
'''
Returns whether the given astNode is a Name astNode (or a number) AND
if the name of the variable associated with astNode node is in the
subtree of this node.
Args:
astNode (AstNode or int): The potential child node to find.
Returns:
bool: Whether the node is a descendent.
'''
def find_all(self, type):
'''
Returns all AstNodes in this node's subtrees that are an AstNode of the
given type.
Args:
type (str): The ast name to search for ("For", "Assign", "BinOp",
etc.). A complete list of options can be found in the
Green Tree Snakes documentation.
Returns:
list of AstNode: The AstNodes descended from this one.
'''
def numeric_logic_check(self, mag, expr):
'''
Returns whether the numerical logical expression represented by the AST self likely
equivalent to the numerical logical expression represented by expr. Likely is determined by
testing boundary conditions
Args:
mag (number): a tolerance value with which to check against. This is used for checking
boundary conditions
expr (string): A string written in JAVASCRIPT syntax that is equivalent to the logic
that you want self to be equivalent to. Eval is run on this string
Returns:
None: This means that either there was more than one variable in expr or self,
that self wasn't a Compare or BoolOp node, or that
True: This means that self matched all detectable edge cases testing between expr and self within
the specified tolerance mag.
False: This means that self and expr didn't return the same thing for one of the automatically
generated test inputs.
'''
## instructor_utility.py
def ensure_literal(*literals):
'''
Raises an Explanation if the literal values (strings, ints, floats) are not
in the source code.
Args:
*literals (int, float or str): Any literal value.
Returns: False if the literals were all in the code, otherwise
returns the first missing literal value.
'''
def prevent_literal(*literals):
'''
Raises an Explanation if the literal values (strings, ints, floats) are
in the source code.
Args:
*literals (int, float or str): Any literal value.
Returns: False if the literals were not in the code, otherwise
returns the first present literal value.
'''
def ensure_operation(op_name, root=None):
'''
Gently rebukes if the given operator is not found in the source code.
Args:
op_name (str): The name of the operator, as it is written in Python
(e.g., "==" and not "Eq"). Works for BoolOps, BinOps,
UnaryOps, and Compares.
Returns: False if the operator was not in the code, otherwise returns the
first AST node apperance of the operator.
'''
def prevent_operation(op_name, root=None):
'''
Gently rebukes if the given operator is found in the source code.
Args:
op_name (str): The name of the operator, as it is written in Python
(e.g., "==" and not "Eq"). Works for BoolOps, BinOps,
UnaryOps, and Compares.
Returns: False if the operator was not in the code, otherwise returns the
first AST node apperance of the operator.
'''
def function_is_called(name):
'''
Returns whether the given function or method has been called from within
the students' code.
Args:
name (str): The name of the function or method (e.g., "sum").
Returns:
bool: Whether the function or method is called.
'''
def only_printing_variables():
'''
Returns whether the students' code is only printing variables, as opposed
to the anything else (e.g., literal values).
Returns:
bool: Whether any print function calls print non-variables.
'''
def find_prior_initializations(node):
'''
Given a name ast node at a specific location in code, returns a list of
all previous assignments that have written to that name
Returns:
None if node is not a Name node, otherwise returns a (possibly empty)
list
'''
def prevent_builtin_usage(names):
'''
Checks that a given list of function names are not being called, and
explains a warning if they are. Also prevents against simply redeclaring
the function_names.
Args:
names (list of str): A list of the function names to check for.
Returns:
None: No usages occurred
str: The name of the first function that was used.
'''
def prevent_advanced_iteration():
'''
Checks that the program does not use While loops or any of the built-in
functions for processing lists (e.g., sum or len).
'''
##instructor_filter
def missing_if_in_for():
'''
'''
def append_not_in_if():
'''
'''
##iteration_context
def list_length_3_or_more():
'''
'''
def missing_list_initialization_8_2():
'''
'''
def wrong_list_initialization_placement_8_3():
'''
'''
def wrong_accumulator_initialization_placement_8_3():
'''
'''
def wrong_iteration_body_8_3():
'''
'''
def wrong_print_8_3():
'''
'''
def missing_target_slot_empty_8_4():
'''
'''
def missing_addition_slot_empty_8_4():
'''
'''
def wrong_names_not_agree_8_4():
'''
'''
def wrong_should_be_counting():
'''
'''
def wrong_should_be_summing():
'''
'''
def wrong_cannot_sum_list():
'''
'''
def missing_no_print():
'''
'''
def missing_counting_list():
'''
'''
def missing_summing_list():
'''
'''
def missing_zero_initialization():
'''
'''
def missing_average():
'''
'''
def warning_average_in_iteration():
'''
'''
def wrong_average_demoninator():
'''
'''
def wrong_average_numerator():
'''
'''
def wrong_compare_list():
'''
'''
def wrong_for_inside_if():
'''
'''
def wrong_list_initialization_9_1():
'''
'''
def wrong_accumulator_initialization_9_1():
'''
'''
def wrong_accumulation_9_1():
'''
'''
def wrong_list_initialization_placement_9_1():
'''
'''
def wrong_accumulator_initialization_placement_9_1():
'''
'''
def wrong_iteration_body_9_1():
'''
'''
def wrong_print_9_1():
'''
'''
def wrong_list_initialization_9_2():
'''
'''
def wrong_accumulator_initialization_9_2():
'''
'''
def wrong_accumulation_9_2():
'''
'''
def wrong_list_initialization_placement_9_2():
'''
'''
def wrong_accumulator_initialization_placement_9_2():
'''
'''
def wrong_iteration_body_9_2():
'''
'''
def wrong_decision_body_9_2():
'''
'''
def wrong_print_9_2():
'''
'''
def wrong_comparison_9_6():
'''
'''
def wrong_conversion_10_2():
'''
'''
def wrong_filter_condition_10_3():
'''
'''
def wrong_and_filter_condition_10_4():
'''
'''
def wrong_nested_filter_condition_10_4():
'''
'''
def wrong_conversion_problem_10_5():
'''
'''
def wrong_filter_problem_atl1_10_5():
'''
'''
def wrong_filter_problem_atl2_10_5():
'''
'''
def wrong_append_problem_atl1_10_5():
'''
'''
def wrong_append_problem_atl2_10_5():
'''
'''
def wrong_debug_10_6():
'''
'''
def wrong_debug_10_7():
'''
'''
##instructor_histogram
def histogram_missing():
'''
'''
def plot_show_missing():
'''
'''
def histogram_argument_not_list():
'''
'''
def histogram_wrong_list():
'''
'''
##instructor_append
def find_append_in(node):
'''
'''
def missing_append_in_iteration():
'''
'''
def wrong_not_append_to_list():
'''
'''
def missing_append_list_initialization():
'''
'''
def wrong_append_list_initiatization():
'''
'''
def append_list_wrong_slot():
'''
'''
##instructor_iteration
def all_for_loops():
'''
'''
def wrong_target_is_list():
'''
'''
def wrong_list_repeated_in_for():
'''
'''
def missing_iterator_initialization():
'''
'''
def wrong_iterator_not_list():
'''
'''
def missing_target_slot_empty():
'''
'''
def list_not_initialized_on_run():
'''
'''
def list_initialization_misplaced():
'''
'''
def missing_for_slot_empty():
'''
'''