def test_runtime_error_inputs(self): student_code = dedent(''' def x(): value = input("Gimme a number") return 7 % value x ''') student = Sandbox() set_source(student_code) student.run() result = student.call("x", inputs=["0"]) print([[c.kind, c.inputs] for c in student._context]) self.assertEqual( """A TypeError occurred: Unsupported operand type(s) for %: 'int' and 'str' I ran the code: x() And I entered as input: 0 The traceback was: Line 4 of file answer.py in x return 7 % value Type errors occur when you use an operator or function on the wrong type of value. For example, using `+` to add to a list (instead of `.append`), or dividing a string by a number. Suggestion: To fix a type error, you should trace through your code. Make sure each expression has the type you expect it to have. """.strip(), student.feedback.message)
def test_error_context(self): student_code = dedent(''' def get_input(): return int(input("Gimme the number")) ''') set_source(student_code, "student.py") student = Sandbox() student.run() result = student.call("get_input", inputs="Banana") print("--", [c.inputs for c in student._context]) self.assertEqual(3, student.feedback.location.line) self.assertEqual( dedent("""A ValueError occurred: Invalid literal for int() with base 10: 'banana' I ran the code: get_input() And I entered as input: Banana The traceback was: Line 3 of file student.py in get_input return int(input("Gimme the number")) A ValueError occurs when you pass the wrong type of value to a function. For example, you try to convert a string without numbers to an integer (like `int('Five')`). Suggestion: Read the error message to see which function had the issue. Check what type the function expects. Then trace your code to make sure you pass in that type.""" ).strip(), student.feedback.message)
def test_improved_exceptions(self): TEST_FILENAME = here + '_sandbox_test_student.py' with open(TEST_FILENAME) as student_file: student_code = student_file.read() student = Sandbox() student.run(student_code, filename=TEST_FILENAME) self.assertIsNotNone(student.exception) self.assertIsInstance(student.exception, TypeError) self.assertEqual(1, student.feedback.location.line) self.assertEqual( dedent("""A TypeError occurred: Unsupported operand type(s) for +: 'int' and 'str' I ran the file _sandbox_test_student.py. The traceback was: Line 1 of file _sandbox_test_student.py 1+'0' Type errors occur when you use an operator or function on the wrong type of value. For example, using `+` to add to a list (instead of `.append`), or dividing a string by a number. Suggestion: To fix a type error, you should trace through your code. Make sure each expression has the type you expect it to have. """.format(filename=TEST_FILENAME)).strip(), student.feedback.message)
def test_oo(self): # Load the "bank.py" code student_code = dedent(''' class Bank: def __init__(self, balance): self.balance = balance def save(self, amount): self.balance += amount return self.balance > 0 def take(self, amount): self.balance -= amount return self.balance > 0''') student = Sandbox() student.run(student_code, filename='bank.py') # Check that we created the class self.assertIn('Bank', student.data) # Now let's try making an instance student.call('Bank', 50, target='bank') self.assertIsInstance(student.data['bank'], student.data['Bank']) # Can we save money? student.call('bank.save', 32) self.assertTrue(student.result) # What about extracting money? student.data['bank'].balance += 100 student.call('bank.take', 100) self.assertTrue(student.result)
def test_normal_run(self): student = Sandbox() student.run('a=0\nprint(a)', filename='student.py') self.assertIn('a', student.data) self.assertEqual(student.data['a'], 0) self.assertEqual(len(student.output), 1) self.assertIn('0', student.output[0])
def test_improved_exceptions(self): student_code = '0+"1"' student = Sandbox() student.run(student_code, _as_filename='student.py') self.assertIsNotNone(student.exception) self.assertIsInstance(student.exception, TypeError) self.assertEqual(student.exception_position, {'line': 1})
def test_sandboxing_open(self): student_code = dedent(''' print(open("test_sandbox.py").read()) ''') set_source(student_code) student = Sandbox() student.run(student_code, filename='student.py') self.assertEqual(str(student.exception), "The filename you passed to 'open' is restricted.")
def test_weird_module_behavior(self): student_code = dedent(''' import pprint pprint.pprint(5) ''') set_source(student_code) student = Sandbox() student.run(student_code, filename='student.py') self.assertIsNone(student.exception)
def test_sandboxing_pedal(self): student_code = dedent(''' from pedal.report import MAIN_REPORT print(MAIN_REPORT) ''') set_source(student_code) student = Sandbox() student.run(student_code, filename='student.py') self.assertEqual(str(student.exception), "You cannot import pedal!")
def test_sandboxing_devious_open1(self): student_code = dedent(''' __builtins__['globals']()['open']("test_sandbox.py") # Fine to import anything else ''') set_source(student_code) student = Sandbox() student.run(student_code, filename='student.py') self.assertEqual(str(student.exception), "You are not allowed to call 'globals'.")
def test_coverage(self): test_filename = here + '_sandbox_test_coverage.py' with open(test_filename) as student_file: student_code = student_file.read() contextualize_report(student_code) student = Sandbox() student.tracer_style = 'coverage' student.run(student_code, filename=test_filename) self.assertIsNone(student.exception) self.assertEqual(student.trace.pc_covered, 80.0) self.assertEqual(student.trace.lines, {1, 2, 4, 7, 10, 11, 12, 13})
def test_matplotlib(self): student_code = dedent(''' import matplotlib.pyplot as plt plt.plot([1,2,3]) plt.title("My line plot") plt.show() ''') student = Sandbox() student.run(student_code, _as_filename='student.py') self.assertIn('matplotlib.pyplot', student.modules) plt = student.modules['matplotlib.pyplot'] self.assertEqual(len(plt.plots), 1)
def test_mock_turtle(self): student_code = dedent(''' import turtle turtle.forward(100) turtle.mainloop() ''') set_source(student_code) student = Sandbox() student.run(student_code, filename='student.py') self.assertIsNone(student.exception) self.assertEqual(student.modules.turtles.calls[0][0], 'forward') self.assertEqual(student.modules.turtles.calls[0][1][0], 100)
def test_calls(self): student_code = dedent(''' def x(n): if n > 0: return x(n-1) return 0 x(5) ''') student = Sandbox() student.tracer_style = 'calls' student.run(student_code, filename='student.py') self.assertEqual(len(student.trace.calls['x']), 6)
def test_coverage(self): test_filename = here + '_sandbox_test_coverage.py' with open(test_filename) as student_file: student_code = student_file.read() contextualize_report(student_code) student = Sandbox() student.tracer_style = 'native' student.run(student_code, filename=test_filename) self.assertIsNone(student.exception) #self.assertEqual(round(student.trace.pc_covered), 85) self.assertEqual(student.trace.lines, [1, 2, 3, 4, 6, 9, 12, 16, 14, 15, 17])
def test_sandboxing_sys_modules(self): clear_report() student_code = dedent(''' import sys # Might try to bypass us del sys.modules['pedal'] from pedal.report import MAIN_REPORT print(MAIN_REPORT) ''') set_source(student_code) student = Sandbox() student.run(student_code, filename='student.py') self.assertEqual(str(student.exception), "You cannot import pedal!")
def test_get_by_types(self): student_code = dedent(''' my_int = 0 my_other_int = 5 my_str = "Hello there!" response_str = "General Kenobi!" a_list = [1,2,3] another_list = [4,5,6] ''') student = Sandbox() student.run(student_code, filename='student.py') # ints ints = student.get_names_by_type(int) self.assertEqual(len(ints), 2) self.assertIn('my_int', ints) self.assertIn('my_other_int', ints) # lists lists = student.get_names_by_type(list) self.assertEqual(len(lists), 2) self.assertIn('a_list', lists) self.assertIn('another_list', lists) # strs strs = student.get_values_by_type(str) self.assertEqual(len(strs), 2) self.assertIn('Hello there!', strs) self.assertIn('General Kenobi!', strs)
def test_coverage_native(self): test_filename = here + '_sandbox_test_coverage.py' with open(test_filename) as student_file: student_code = student_file.read() contextualize_report(student_code) student = Sandbox() student.tracer_style = 'native' student.run(student_code, filename=test_filename) self.assertIsNone(student.exception) self.assertEqual(student.trace.lines, [1, 2, 3, 4, 6, 9, 12, 16, 14, 15, 17]) self.assertEqual(student.trace.calls, { 'z': [{ 'args': (0, ), 'banana': 2, 'kwargs': { 'apple': 8 }, 'p': 3 }] })
def test_call(self): student_code = "def average(a,b):\n return (a+b)/2" student = Sandbox() student.run(student_code, filename='student.py') student.call('average', 10, 12)
def test_sandboxing_open(self): student_code = dedent(''' print(open("test_sandbox.py").read()) ''') set_source(student_code) student = Sandbox() student.run(student_code, filename='student.py') self.assertEqual(str(student.exception), "The filename you passed to 'open' is restricted.") # Then can we turn it back on? student.allow_function('open') student.run(student_code, filename='student.py') self.assertIsNone(student.exception) # And off again student.block_function('open') student.run(student_code, filename='student.py') self.assertEqual(str(student.exception), "You are not allowed to call 'open'.")
def test_input(self): student = Sandbox() student.run('b = input("Give me something:")\nprint(b)', inputs=['Hello World!']) self.assertIn('b', student.data) self.assertEqual(student.data['b'], 'Hello World!')
def test_unittest(self): student_code = dedent(''' x = 0 ''') student = Sandbox() student.run(student_code) student.call('x') self.assertIsNotNone(student.exception) student_code = dedent(''' class Fruit: def __init__(self, name, weight=0): self.name = name self.weight = weight def do_math(a, b): return a + b - 5 def weigh_fruits(fruits): return sum(fruit.weight for fruit in fruits) ''') student.report.contextualize(Submission(main_code=student_code)) student = Sandbox() student.run() result = student.call('do_math', 15, 20) self.assertEqual(result, 30) self.assertEqual(['do_math(15, 20)'], [context.code for context in student.get_context()]) banana = student.call('Fruit', "Banana") self.assertIsInstance(banana, student.data['Fruit']) self.assertEqual(["Fruit('Banana')"], [context.code for context in student.get_context()]) student.start_grouping_context() student.run() orange = student.call("Fruit", "Orange", 30, target="orange") self.assertIsInstance(orange, student.data['Fruit']) student.call("Fruit", "Pineapple", 60, target="pineapple") student.run("fruits = [orange, pineapple]") total_weight = student.call('weigh_fruits', args_locals=["fruits"]) self.assertEqual(total_weight, 90) self.assertEqual([ student_code, "orange = Fruit('Orange', 30)", "pineapple = Fruit('Pineapple', 60)", "fruits = [orange, pineapple]", "weigh_fruits(fruits)" ], [context.code for context in student.get_context()])
import sys from pprint import pprint from pedal.sandbox import Sandbox student = Sandbox() student.run(""" def add_together(a, b): return -7 print(add_together) """, as_filename='student.py') #pprint(student.data) print(student.data) print(student.output) from pedal.assertions import assertEqual, phase #assertEqual(student.data['a'], 2) result = student.call("add_together", 2, 2) #as_int = str(result) #print("Result was:", as_int) assertEqual(result, 4) @phase('input_tryit') def try_it(): print("Executed") from pedal.resolvers import simple