instructor_api_documentation.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. '''
  2. This file documents the Python Instructor API.
  3. The format is Google Style Python Docstrings, based on
  4. `http://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html`
  5. '''
  6. ## Core functions
  7. def compliment(message):
  8. '''
  9. Adds message to the list of complimentary feedback.
  10. Args:
  11. message (str): The text string to display to the user in the Feedback
  12. panel as complimentary feedback. This will appear as
  13. a tooltip.
  14. '''
  15. def set_success():
  16. '''
  17. Marks the problem as completely correct. This immediately ends execution
  18. by throwing a GracefulExit exception.
  19. '''
  20. def explain(message, priority="medium", line=None):
  21. '''
  22. Gives student the message as corrective feedback (and higlights line
  23. number). If this function is called multiple times, the student is given the
  24. first feedback of the highest priority found. By default, this overrides
  25. the analyzer and runtime errors that are given - if you want to avoid overriding
  26. those, then use `gently`.
  27. Args:
  28. message (str): The HTML string to display to the user in the Feedback
  29. panel as corrective feedback.
  30. priority (str): Either "low", "medium", or "high", indicating the
  31. ordering of the feedback if multiple occur. Also "student"
  32. is an option to put it after the analyzer and runtime
  33. errors, and "verifier" will put it before syntax errors.
  34. line (int): The specific line number to highlight for the user.
  35. '''
  36. def gently(message):
  37. '''
  38. Gives students the message as corrective feedback, at a priority level
  39. below the analyzer and runtime errors - that way, it can be used to
  40. more "gently" guide the student.
  41. '''
  42. def suppress(type, subtype):
  43. '''
  44. Suppresses feedback of the given "type". When also given a subtype, it
  45. instead suppresses the subtype (which is typically a specific error message
  46. or type of exception). Multiple subtypes can be specified with multiple
  47. calls, each call suppressing additional feedback.
  48. Args:
  49. type (str): Either 'verifier', 'parser', 'analyzer', or 'student',
  50. corresponding to the phase of execution feedback that will
  51. be suppressed.
  52. subtype (str): A specific type of exception (if 'student') or
  53. specific type of issue (if 'analyzer'). Available issues:
  54. - 'Unconnected blocks'
  55. - 'Iteration variable is iteration list'
  56. - "Undefined variables"
  57. - "Possibly undefined variables"
  58. - "Unread variables"
  59. - "Overwritten variables"
  60. - "Empty iterations"
  61. - "Non-list iterations"
  62. - "Incompatible types"
  63. '''
  64. def run_student():
  65. '''
  66. Reruns the students' submitted code ("__main__") as a function call.
  67. This allows the instructor to run the student code under new conditions.
  68. After a call to run_student(), the instructor will most likely want
  69. to use get_output() or some other function to access the new state.
  70. If the students' code failed to parse, then the body is instead replaced
  71. with "pass" to prevent compilation errors.
  72. If the students' code raises an exception, that exception is caught and
  73. returned by the function. This is necessary to avoid the Instructor feedback
  74. hijacking the runtime exceptions that students would see.
  75. Returns:
  76. Exception or None: If the students' code fails for some reason, the
  77. raised exception is returned. Otherwise, the None
  78. value is returned.
  79. '''
  80. def get_output():
  81. '''
  82. Returns a List containing the students' output from the last run. Note that
  83. multiple invocations of run_student(), without calls to reset_output(), will
  84. possibly result in duplicate output.
  85. Different types of student output are represented differently:
  86. - Print: Represented as strings WITHOUT the trailing newline.
  87. - Plot: Represented as Lists of "Plot Dictionaries". Each call to
  88. plot(), hist(), and scatter() adds another "Plot Dictionary" to
  89. the list, and the list is only "snipped off" when show() is
  90. called. Each "Plot Dictionary" contains two fields:
  91. "type": One of "hist", "line", or "scatter".
  92. "data": A 1- or 2- dimensional list of numeric data.
  93. Returns:
  94. list: The output that the students code generated on its last execution.
  95. '''
  96. def queue_input(input):
  97. '''
  98. Add a new string to be set as input when the "input" function is called.
  99. You can repeatedly queue_input to satisfy loops, too.
  100. If there are no queued inputs, a blank string will be returned.
  101. Args:
  102. input (str): The string to queue for input.
  103. '''
  104. def reset_output():
  105. '''
  106. Removes any output generated on a previous run of the student code. This is
  107. typically used between executions of the `run_student` function.
  108. '''
  109. def log(message):
  110. '''
  111. Print the given message to the JS console in the browser. This is useful
  112. for debugging purposes.
  113. Args:
  114. message: The logging message. Any kind of data is allowable, and will
  115. be printed as JavaScript (using remapToJs).)
  116. '''
  117. def log_ast():
  118. '''
  119. Prints the AST to the JS console in the browser. This is useful for
  120. debugging purposes.
  121. '''
  122. def log_variables():
  123. '''
  124. Prints a mapping between the names of the programs' variables and their
  125. estimated type (according to the abstract interpreter) to the JS console
  126. in the browser. This is useful for debugging purposes.
  127. '''
  128. def log_behavior():
  129. '''
  130. Prints a list of each variables' read/write behavior to the JS console
  131. in the browser. This is useful for debugging purposes.
  132. '''
  133. def log_trace():
  134. '''
  135. Prints a list of each step of the programs' execution to the JS console
  136. in the browser. This is useful for debugging purposes.
  137. '''
  138. def log_issues():
  139. '''
  140. Prints a list of all found Abstract Interperter issues to the JS console
  141. in the browser. This is useful for debugging purposes.
  142. '''
  143. class StudentData():
  144. '''
  145. A class that wraps a dictionary of data created after the execution of
  146. the students' code.
  147. A common use case for this is to require students to declare a particular
  148. function, and then to access that function via the singleton instance of
  149. this class.
  150. Attributes:
  151. data (dict): A dictionary containing all the students' data. In other
  152. words, if they declare a variable named "alpha", you can
  153. access that variable's final value via
  154. student['alpha']
  155. Because this is a dictionary, you can also test membership,
  156. access element dynamically, and anything else you may want.
  157. '''
  158. def get_values_by_type(type):
  159. '''
  160. Returns a list of values from the students' data where each value
  161. will have the given type.
  162. Args:
  163. type (type): A python type (e.g., int or str) that will be used
  164. in the comparison. Does not respect inheritance!
  165. Returns:
  166. list: A list of the values with that type.
  167. '''
  168. def get_names_by_type(type):
  169. '''
  170. Returns a list of the variable names from the students' data where each
  171. variable's final value will have the given type.
  172. Args:
  173. type (type): A python type (e.g., int or str) that will be used
  174. in the comparison. Does not respect inheritance!
  175. Returns:
  176. list of str: A list of the variables with that type.
  177. '''
  178. student = StudentData()
  179. '''
  180. A top-level variable that holds all of the students' created data.
  181. Acts as a convenient singleton for the code.
  182. '''
  183. def analyze_program():
  184. '''
  185. Triggers the analyzer to run so that type information can be available
  186. on the AST nodes.
  187. '''
  188. def parse_program():
  189. '''
  190. Returns the root node of the AST of the student code as an AstNode.
  191. See the AstNode class for more information.
  192. Returns:
  193. AstNode: The root node of the AST
  194. '''
  195. def get_program():
  196. '''
  197. Returns the students' code as a string.
  198. Returns:
  199. String: The string representation of the student code.
  200. '''
  201. def def_use_error(node):
  202. '''
  203. Determines if the given AstNode (with the astname "Name"), and if so,
  204. if the name associated with that node has not been initialized according
  205. to the Analyzer.
  206. Args:
  207. node (AstNode): The Name node to analyze.
  208. Returns:
  209. bool: Returns whether the associated name has been initialized.
  210. '''
  211. class AstNode():
  212. '''
  213. A representation of the students' Abstract Syntax Tree. Can be traversed
  214. and analyzed in order to make assertions about the students' code.
  215. The fields of the AstNode, in addition to the two listed below, are the
  216. fields listed in the Green Tree Snakes documentation.
  217. https://greentreesnakes.readthedocs.io/en/latest/nodes.html
  218. Attributes:
  219. ast_name (str): The type of AST Node of this node (e.g. "Name", "For",
  220. "Assign", etc.). For a complete list, see the Green Tree
  221. Snakes API, which closely mimics our own.
  222. Some notable exceptions are:
  223. - Assign: Currently, Assign.targets returns a single
  224. AstNode instead of a list.
  225. - Op Nodes: For nodes with an "ops" field, this
  226. only returns the FIRST operator, so no
  227. comparison operator chaining.
  228. data_type
  229. if this node is a Name node, returns the first data type that this variable has taken on
  230. next_tree
  231. For the AST node to which this node belongs, returns the next node in that AST that is NOT
  232. in this node's subtree.
  233. '''
  234. def __init__(self, id):
  235. '''
  236. This should NOT be used by an instructor, this is strictly used
  237. internally to match up with the already parsed Skulpt AST.
  238. Args:
  239. id (int): The index for the AST node when doing an in-order
  240. traversal of the tree.
  241. '''
  242. def __eq__(self, other):
  243. '''
  244. If other is an AstNode, checks whether they are the same AstNode. If
  245. both nodes originate from the same AST, then a return true will indicate
  246. it is the same node, and false will indicate it's a different node. If
  247. it's not an AstNode, this will crash the program
  248. Args:
  249. other (AstNode): The other AstNode to compare to.
  250. Returns:
  251. bool: A boolean indicating if they are equal.
  252. '''
  253. def has(self, astNode):
  254. '''
  255. Returns whether the given astNode is a Name astNode (or a number) AND
  256. if the name of the variable associated with astNode node is in the
  257. subtree of this node.
  258. Args:
  259. astNode (AstNode or int): The potential child node to find.
  260. Returns:
  261. bool: Whether the node is a descendent.
  262. '''
  263. def find_all(self, type):
  264. '''
  265. Returns all AstNodes in this node's subtrees that are an AstNode of the
  266. given type.
  267. Args:
  268. type (str): The ast name to search for ("For", "Assign", "BinOp",
  269. etc.). A complete list of options can be found in the
  270. Green Tree Snakes documentation.
  271. Returns:
  272. list of AstNode: The AstNodes descended from this one.
  273. '''
  274. def numeric_logic_check(self, mag, expr):
  275. '''
  276. Returns whether the numerical logical expression represented by the AST self likely
  277. equivalent to the numerical logical expression represented by expr. Likely is determined by
  278. testing boundary conditions
  279. Args:
  280. mag (number): a tolerance value with which to check against. This is used for checking
  281. boundary conditions
  282. expr (string): A string written in JAVASCRIPT syntax that is equivalent to the logic
  283. that you want self to be equivalent to. Eval is run on this string
  284. Returns:
  285. None: This means that either there was more than one variable in expr or self,
  286. that self wasn't a Compare or BoolOp node, or that
  287. True: This means that self matched all detectable edge cases testing between expr and self within
  288. the specified tolerance mag.
  289. False: This means that self and expr didn't return the same thing for one of the automatically
  290. generated test inputs.
  291. '''
  292. ## instructor_utility.py
  293. def ensure_literal(*literals):
  294. '''
  295. Raises an Explanation if the literal values (strings, ints, floats) are not
  296. in the source code.
  297. Args:
  298. *literals (int, float or str): Any literal value.
  299. Returns: False if the literals were all in the code, otherwise
  300. returns the first missing literal value.
  301. '''
  302. def prevent_literal(*literals):
  303. '''
  304. Raises an Explanation if the literal values (strings, ints, floats) are
  305. in the source code.
  306. Args:
  307. *literals (int, float or str): Any literal value.
  308. Returns: False if the literals were not in the code, otherwise
  309. returns the first present literal value.
  310. '''
  311. def ensure_operation(op_name, root=None):
  312. '''
  313. Gently rebukes if the given operator is not found in the source code.
  314. Args:
  315. op_name (str): The name of the operator, as it is written in Python
  316. (e.g., "==" and not "Eq"). Works for BoolOps, BinOps,
  317. UnaryOps, and Compares.
  318. Returns: False if the operator was not in the code, otherwise returns the
  319. first AST node apperance of the operator.
  320. '''
  321. def prevent_operation(op_name, root=None):
  322. '''
  323. Gently rebukes if the given operator is found in the source code.
  324. Args:
  325. op_name (str): The name of the operator, as it is written in Python
  326. (e.g., "==" and not "Eq"). Works for BoolOps, BinOps,
  327. UnaryOps, and Compares.
  328. Returns: False if the operator was not in the code, otherwise returns the
  329. first AST node apperance of the operator.
  330. '''
  331. def function_is_called(name):
  332. '''
  333. Returns whether the given function or method has been called from within
  334. the students' code.
  335. Args:
  336. name (str): The name of the function or method (e.g., "sum").
  337. Returns:
  338. bool: Whether the function or method is called.
  339. '''
  340. def only_printing_variables():
  341. '''
  342. Returns whether the students' code is only printing variables, as opposed
  343. to the anything else (e.g., literal values).
  344. Returns:
  345. bool: Whether any print function calls print non-variables.
  346. '''
  347. def find_prior_initializations(node):
  348. '''
  349. Given a name ast node at a specific location in code, returns a list of
  350. all previous assignments that have written to that name
  351. Returns:
  352. None if node is not a Name node, otherwise returns a (possibly empty)
  353. list
  354. '''
  355. def prevent_builtin_usage(names):
  356. '''
  357. Checks that a given list of function names are not being called, and
  358. explains a warning if they are. Also prevents against simply redeclaring
  359. the function_names.
  360. Args:
  361. names (list of str): A list of the function names to check for.
  362. Returns:
  363. None: No usages occurred
  364. str: The name of the first function that was used.
  365. '''
  366. def prevent_advanced_iteration():
  367. '''
  368. Checks that the program does not use While loops or any of the built-in
  369. functions for processing lists (e.g., sum or len).
  370. '''
  371. ##instructor_filter
  372. def missing_if_in_for():
  373. '''
  374. '''
  375. def append_not_in_if():
  376. '''
  377. '''
  378. ##iteration_context
  379. def list_length_3_or_more():
  380. '''
  381. '''
  382. def missing_list_initialization_8_2():
  383. '''
  384. '''
  385. def wrong_list_initialization_placement_8_3():
  386. '''
  387. '''
  388. def wrong_accumulator_initialization_placement_8_3():
  389. '''
  390. '''
  391. def wrong_iteration_body_8_3():
  392. '''
  393. '''
  394. def wrong_print_8_3():
  395. '''
  396. '''
  397. def missing_target_slot_empty_8_4():
  398. '''
  399. '''
  400. def missing_addition_slot_empty_8_4():
  401. '''
  402. '''
  403. def wrong_names_not_agree_8_4():
  404. '''
  405. '''
  406. def wrong_should_be_counting():
  407. '''
  408. '''
  409. def wrong_should_be_summing():
  410. '''
  411. '''
  412. def wrong_cannot_sum_list():
  413. '''
  414. '''
  415. def missing_no_print():
  416. '''
  417. '''
  418. def missing_counting_list():
  419. '''
  420. '''
  421. def missing_summing_list():
  422. '''
  423. '''
  424. def missing_zero_initialization():
  425. '''
  426. '''
  427. def missing_average():
  428. '''
  429. '''
  430. def warning_average_in_iteration():
  431. '''
  432. '''
  433. def wrong_average_demoninator():
  434. '''
  435. '''
  436. def wrong_average_numerator():
  437. '''
  438. '''
  439. def wrong_compare_list():
  440. '''
  441. '''
  442. def wrong_for_inside_if():
  443. '''
  444. '''
  445. def wrong_list_initialization_9_1():
  446. '''
  447. '''
  448. def wrong_accumulator_initialization_9_1():
  449. '''
  450. '''
  451. def wrong_accumulation_9_1():
  452. '''
  453. '''
  454. def wrong_list_initialization_placement_9_1():
  455. '''
  456. '''
  457. def wrong_accumulator_initialization_placement_9_1():
  458. '''
  459. '''
  460. def wrong_iteration_body_9_1():
  461. '''
  462. '''
  463. def wrong_print_9_1():
  464. '''
  465. '''
  466. def wrong_list_initialization_9_2():
  467. '''
  468. '''
  469. def wrong_accumulator_initialization_9_2():
  470. '''
  471. '''
  472. def wrong_accumulation_9_2():
  473. '''
  474. '''
  475. def wrong_list_initialization_placement_9_2():
  476. '''
  477. '''
  478. def wrong_accumulator_initialization_placement_9_2():
  479. '''
  480. '''
  481. def wrong_iteration_body_9_2():
  482. '''
  483. '''
  484. def wrong_decision_body_9_2():
  485. '''
  486. '''
  487. def wrong_print_9_2():
  488. '''
  489. '''
  490. def wrong_comparison_9_6():
  491. '''
  492. '''
  493. def wrong_conversion_10_2():
  494. '''
  495. '''
  496. def wrong_filter_condition_10_3():
  497. '''
  498. '''
  499. def wrong_and_filter_condition_10_4():
  500. '''
  501. '''
  502. def wrong_nested_filter_condition_10_4():
  503. '''
  504. '''
  505. def wrong_conversion_problem_10_5():
  506. '''
  507. '''
  508. def wrong_filter_problem_atl1_10_5():
  509. '''
  510. '''
  511. def wrong_filter_problem_atl2_10_5():
  512. '''
  513. '''
  514. def wrong_append_problem_atl1_10_5():
  515. '''
  516. '''
  517. def wrong_append_problem_atl2_10_5():
  518. '''
  519. '''
  520. def wrong_debug_10_6():
  521. '''
  522. '''
  523. def wrong_debug_10_7():
  524. '''
  525. '''
  526. ##instructor_histogram
  527. def histogram_missing():
  528. '''
  529. '''
  530. def plot_show_missing():
  531. '''
  532. '''
  533. def histogram_argument_not_list():
  534. '''
  535. '''
  536. def histogram_wrong_list():
  537. '''
  538. '''
  539. ##instructor_append
  540. def find_append_in(node):
  541. '''
  542. '''
  543. def missing_append_in_iteration():
  544. '''
  545. '''
  546. def wrong_not_append_to_list():
  547. '''
  548. '''
  549. def missing_append_list_initialization():
  550. '''
  551. '''
  552. def wrong_append_list_initiatization():
  553. '''
  554. '''
  555. def append_list_wrong_slot():
  556. '''
  557. '''
  558. ##instructor_iteration
  559. def all_for_loops():
  560. '''
  561. '''
  562. def wrong_target_is_list():
  563. '''
  564. '''
  565. def wrong_list_repeated_in_for():
  566. '''
  567. '''
  568. def missing_iterator_initialization():
  569. '''
  570. '''
  571. def wrong_iterator_not_list():
  572. '''
  573. '''
  574. def missing_target_slot_empty():
  575. '''
  576. '''
  577. def list_not_initialized_on_run():
  578. '''
  579. '''
  580. def list_initialization_misplaced():
  581. '''
  582. '''
  583. def missing_for_slot_empty():
  584. '''
  585. '''