Skip to content

Commit 181d471

Browse files
authored
Merge pull request #202 from datacamp/feature-ast-in
expand has_equal_ast to see if student code contains solution, manual…
2 parents cd345a6 + 686a96e commit 181d471

2 files changed

Lines changed: 74 additions & 4 deletions

File tree

pythonwhat/check_funcs.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -397,19 +397,42 @@ def call(args,
397397
from pythonwhat.tasks import ReprFail, UndefinedValue
398398
from pythonwhat import utils
399399

400-
def has_equal_ast(incorrect_msg="FMT: Your code does not seem to match the solution.", state=None):
400+
def has_equal_ast(incorrect_msg="FMT: Your code does not seem to match the solution.", code=None, exact=True, state=None):
401401
"""Test whether abstract syntax trees match between the student and solution code.
402402
403403
Args:
404404
incorrect_msg: message displayed when ASTs mismatch.
405+
code: optional code to use instead of the solution AST
406+
exact: whether the representations must match exactly. If false, the solution AST
407+
only needs to be contained within the student AST (similar to using test student typed).
408+
409+
:Example:
410+
411+
Student and Solution Code::
412+
413+
dict(a = 'value').keys()
414+
415+
SCT::
416+
417+
# all pass
418+
Ex().has_equal_ast()
419+
Ex().has_equal_ast(code = "dict(a = 'value').keys()")
420+
Ex().has_equal_ast(code = "dict(a = 'value')", exact = False)
421+
405422
"""
406423
rep = Reporter.active_reporter
407424

408-
stu_rep = ast.dump(state.student_tree)
409-
sol_rep = ast.dump(state.solution_tree)
425+
parse_tree = lambda n: ast.dump(n.body[0] if isinstance(n, ast.Module) and len(n.body) == 1 else n)
426+
427+
stu_rep = parse_tree(state.student_tree)
428+
sol_rep = parse_tree(state.solution_tree if not code else ast.parse(code))
410429

411430
_msg = state.build_message(incorrect_msg)
412-
rep.do_test(EqualTest(stu_rep, sol_rep, Feedback(_msg, state.highlight)))
431+
432+
if exact:
433+
rep.do_test(EqualTest(stu_rep, sol_rep, Feedback(_msg, state.highlight)))
434+
elif not sol_rep in stu_rep:
435+
rep.do_test(Test(Feedback(_msg, state.highlight)))
413436

414437
return state
415438

tests/test_spec.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,53 @@ def test_fail(self):
172172
sct_payload = helper.run(self.data)
173173
self.assertFalse(sct_payload['correct'])
174174

175+
class TestHasEqualAst(unittest.TestCase):
176+
def setUp(self):
177+
self.data = {
178+
"DC_SOLUTION": """dict(a = "a").keys()""",
179+
"DC_CODE": """dict(a = 'a') .keys()"""}
180+
181+
def failing_submission(self):
182+
self.data["DC_CODE"] = "dict(A = 'a').keys(somearg = 2)"""
183+
sct_payload = helper.run(self.data)
184+
self.assertFalse(sct_payload['correct'])
185+
186+
def test_simple_pass(self):
187+
self.data["DC_SCT"] = "Ex().has_equal_ast()"
188+
sct_payload = helper.run(self.data)
189+
self.assertTrue(sct_payload['correct'])
190+
191+
def test_simple_fail(self):
192+
self.data["DC_SCT"] = "Ex().has_equal_ast()"
193+
self.failing_submission()
194+
195+
def test_function_pass(self):
196+
self.data["DC_SCT"] = "Ex().check_function('dict', 0, signature=False).has_equal_ast()"
197+
sct_payload = helper.run(self.data)
198+
self.assertTrue(sct_payload['correct'])
199+
200+
def test_function_fail(self):
201+
self.data["DC_SCT"] = "Ex().check_function('dict', 0, signature=False).has_equal_ast()"
202+
self.failing_submission()
203+
204+
def test_function_code_pass(self):
205+
self.data["DC_SCT"] = """Ex().has_equal_ast(code = 'dict(a = "a").keys()')"""
206+
sct_payload = helper.run(self.data)
207+
self.assertTrue(sct_payload['correct'])
208+
209+
def test_function_code_fail(self):
210+
self.data["DC_SCT"] = """Ex().has_equal_ast(code = 'dict(a = "a").keys()')"""
211+
self.failing_submission()
212+
213+
def test_exact_false_pass(self):
214+
self.data["DC_CODE"] = """dict(a = 'a').keys()\nprint('extra')"""
215+
self.data["DC_SCT"] = "Ex().has_equal_ast(exact=False)"
216+
sct_payload = helper.run(self.data)
217+
self.assertTrue(sct_payload['correct'])
218+
219+
def test_exact_false_fail(self):
220+
self.data["DC_SCT"] = "Ex().has_equal_ast(exact=False)"
221+
self.failing_submission()
175222

176223
class TestOverride(unittest.TestCase):
177224
"""

0 commit comments

Comments
 (0)