Skip to content

Commit 40e995a

Browse files
committed
refactor test object and friends to use check funcs
added unit test for issue #150 should complete issue #124
1 parent 5445912 commit 40e995a

7 files changed

Lines changed: 76 additions & 88 deletions

File tree

pythonwhat/parsing.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -426,9 +426,10 @@ def __init__(self):
426426
def visit_Name(self, node):
427427
if self.active_assignment is not None:
428428
if node.id not in self.out:
429-
self.out[node.id] = [self.active_assignment]
429+
self.out[node.id] = self.get_part(node, self.active_assignment)
430430
else:
431-
self.out[node.id].append(self.active_assignment)
431+
self.out[node.id]['highlight'] = None
432+
self.out[node.id]['assignments'].append(self.active_assignment)
432433
self.active_assignment = None
433434

434435
def visit_Attribute(self, node):
@@ -465,6 +466,20 @@ def visit_TryFinally(self, node):
465466
self.visit_each(node.body)
466467
self.visit_each(node.finalbody)
467468

469+
@staticmethod
470+
def get_part(name_node, ass_node=None):
471+
# either name node or simply str or name itself
472+
name = getattr(name_node, 'id', name_node)
473+
load_name = ast.Name(id=name, ctx=ast.Load())
474+
ast.fix_missing_locations(load_name)
475+
#
476+
return {'name': name,
477+
'node': load_name,
478+
'highlight': ass_node or name_node,
479+
'assignments': [] if not ass_node else [ass_node]
480+
}
481+
482+
468483
class IfParser(Parser):
469484
"""Find if structures.
470485
Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,31 @@
1-
from pythonwhat.State import State
21
from pythonwhat.Reporter import Reporter
3-
from pythonwhat.Test import DefinedProcessTest, InstanceProcessTest, DefinedCollProcessTest, EqualValueProcessTest
4-
from pythonwhat.Feedback import Feedback
5-
from pythonwhat.tasks import isDefinedInProcess, isInstanceInProcess, getColumnsInProcess, getValueInProcess, ReprFail
2+
from pythonwhat.tasks import getColumnsInProcess
63
from .test_object import check_object
74
from .test_dictionary import is_instance, test_key, has_key
85

96
import pandas as pd
107

11-
MSG_UNDEFINED = "Are you sure you defined the pandas DataFrame: `{name}`?"
12-
MSG_NOT_INSTANCE = "`{name}` is not a pandas DataFrame."
13-
MSG_KEY_MISSING = "There is no column `{key}` inside `{name}`."
14-
MSG_INCORRECT_VAL = "Column `{key}` of your pandas DataFrame, `{name}`, is not correct."
8+
MSG_UNDEFINED = "Are you sure you defined the pandas DataFrame: `{parent[sol_part][name]}`?"
9+
MSG_NOT_INSTANCE = "`{parent[sol_part][name]}` is not a pandas DataFrame."
10+
MSG_KEY_MISSING = "There is no column `{key}` inside `{parent[sol_part][name]}`."
11+
MSG_INCORRECT_VAL = "Column `{key}` of your pandas DataFrame, `{parent[sol_part][name]}`, is not correct."
1512

1613
def test_data_frame(name,
1714
columns=None,
18-
undefined_msg=MSG_UNDEFINED,
19-
not_data_frame_msg=MSG_NOT_INSTANCE,
20-
undefined_cols_msg=MSG_KEY_MISSING,
21-
incorrect_msg=MSG_INCORRECT_VAL,
15+
undefined_msg=None,
16+
not_data_frame_msg=None,
17+
undefined_cols_msg=None,
18+
incorrect_msg=None,
2219
state=None):
2320
"""Test a pandas dataframe.
2421
"""
2522

2623
rep = Reporter.active_reporter
2724
rep.set_tag("fun", "test_data_frame")
2825

29-
check_df(name, undefined_msg, not_data_frame_msg, state=state)
26+
child = check_df(name, undefined_msg or MSG_UNDEFINED, not_data_frame_msg or MSG_NOT_INSTANCE, state=state)
3027

31-
sol_cols = getColumnsInProcess(name, state.solution_process)
28+
sol_cols = getColumnsInProcess(name, child.solution_process)
3229
if sol_cols is None:
3330
raise ValueError("Something went wrong in figuring out the columns for %s in the solution process" % name)
3431

@@ -37,19 +34,13 @@ def test_data_frame(name,
3734

3835
for col in columns:
3936
# check if column available
40-
test_key(name, col, incorrect_msg, undefined_cols_msg, state=state)
37+
test_key(name, col, incorrect_msg or MSG_INCORRECT_VAL, undefined_cols_msg or MSG_KEY_MISSING, state=child)
4138

4239
# Check functions -------------------------------------------------------------
4340

44-
def check_df(name, undefined_msg, not_instance_msg, state=None):
45-
rep = Reporter.active_reporter
46-
47-
# Check if defined
48-
undefined_msg = undefined_msg.format(name=name)
49-
50-
# check but don't get solution object representation
51-
state = check_object(name, undefined_msg, state=state)
41+
def check_df(name, undefined_msg=MSG_UNDEFINED, not_instance_msg=MSG_NOT_INSTANCE, state=None):
5242

53-
is_instance(name, pd.DataFrame, not_instance_msg, state=state)
43+
child = check_object(name, undefined_msg, state=state) # test defined
44+
is_instance(name, pd.DataFrame, not_instance_msg, state=child) # test instance
5445

55-
return state
46+
return child
Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
from pythonwhat.State import State
21
from pythonwhat.Reporter import Reporter
3-
from pythonwhat.Test import DefinedProcessTest, InstanceProcessTest, DefinedCollProcessTest, EqualValueProcessTest
2+
from pythonwhat.Test import InstanceProcessTest, DefinedCollProcessTest, EqualValueProcessTest
43
from pythonwhat.Feedback import Feedback
5-
from pythonwhat.tasks import isDefinedInProcess, isInstanceInProcess, getKeysInProcess, getValueInProcess, isDefinedCollInProcess, ReprFail
4+
from pythonwhat.tasks import isInstanceInProcess, getKeysInProcess, getValueInProcess, isDefinedCollInProcess, ReprFail
65
from .test_object import check_object
76

8-
MSG_UNDEFINED = "Are you sure you defined the dictionary `{name}`?"
9-
MSG_NOT_INSTANCE = "`{name}` is not a dictionary."
10-
MSG_KEY_MISSING = "Have you specified a key `{key}` inside `{name}`?"
11-
MSG_INCORRECT_VAL = "Have you specified the correct value for the key `{key}` inside `{name}`?"
7+
MSG_UNDEFINED = "Are you sure you defined the dictionary `{parent[sol_part][name]}`?"
8+
MSG_NOT_INSTANCE = "`{parent[sol_part][name]}` is not a dictionary."
9+
MSG_KEY_MISSING = "Have you specified a key `{key}` inside `{parent[sol_part][name]}`?"
10+
MSG_INCORRECT_VAL = "Have you specified the correct value for the key `{key}` inside `{parent[sol_part][name]}`?"
1211

1312
def test_dictionary(name,
1413
keys=None,
@@ -23,39 +22,33 @@ def test_dictionary(name,
2322
rep = Reporter.active_reporter
2423
rep.set_tag("fun", "test_dictionary")
2524

26-
check_dict(name, undefined_msg, not_dictionary_msg, state=state)
25+
child = check_dict(name, undefined_msg or MSG_UNDEFINED, not_dictionary_msg or MSG_NOT_INSTANCE, state=state)
2726

2827
# set keys or check if manual keys are valid
2928
if not keys:
3029
keys = getKeysInProcess(name, state.solution_process)
3130

3231
for key in keys:
3332
# check if key in dictionary
34-
test_key(name, key, incorrect_value_msg, key_missing_msg, state=state)
33+
test_key(name, key, incorrect_value_msg or MSG_INCORRECT_VAL, key_missing_msg or MSG_KEY_MISSING, state=child)
3534

3635
# Check functions -------------------------------------------------------------
3736

38-
def check_dict(name, undefined_msg, not_instance_msg, state=None):
39-
rep = Reporter.active_reporter
40-
41-
# Check if defined
42-
undefined_msg = undefined_msg.format(name=name)
43-
_msg = state.build_message(undefined_msg)
44-
45-
# check but don't get solution object representation
46-
state = check_object(name, _msg, state=state)
37+
def check_dict(name, undefined_msg=MSG_UNDEFINED, not_instance_msg=MSG_NOT_INSTANCE, state=None):
4738

48-
is_instance(name, dict, not_instance_msg, state=state)
39+
child = check_object(name, undefined_msg, state=state) # test defined
40+
is_instance(name, dict, not_instance_msg, state=child) # test instance
4941

50-
return state
42+
return child
5143

5244
def is_instance(name, inst, not_instance_msg, state=None):
5345
rep = Reporter.active_reporter
5446

5547
if not isInstanceInProcess(name, inst, state.solution_process):
5648
raise ValueError("%r is not a %s in the solution environment" % (name, type(inst)))
5749

58-
feedback = Feedback(not_instance_msg.format(name=name))
50+
_msg = state.build_message(not_instance_msg)
51+
feedback = Feedback(_msg, state.highlight)
5952
rep.do_test(InstanceProcessTest(name, inst, state.student_process, feedback))
6053

6154
def has_key(name, key, key_missing_msg, state=None):
@@ -65,8 +58,9 @@ def has_key(name, key, key_missing_msg, state=None):
6558
raise NameError("Not all keys you specified are actually keys in %s in the solution process" % name)
6659

6760
# check if key available
68-
msg = key_missing_msg.format(key=key, name=name)
69-
rep.do_test(DefinedCollProcessTest(name, key, state.student_process, Feedback(msg)))
61+
_msg = state.build_message(key_missing_msg, {'key': key})
62+
rep.do_test(DefinedCollProcessTest(name, key, state.student_process,
63+
Feedback(_msg, state.highlight)))
7064

7165
def test_key(name, key, incorrect_value_msg, key_missing_msg, state=None):
7266
rep = Reporter.active_reporter
@@ -78,6 +72,5 @@ def test_key(name, key, incorrect_value_msg, key_missing_msg, state=None):
7872
raise NameError("Value from %r can't be fetched from the solution process: %s" % c(name, sol_value.info))
7973

8074
# check if value ok
81-
msg = incorrect_value_msg.format(key=key, name=name)
82-
_msg = state.build_message(msg)
83-
rep.do_test(EqualValueProcessTest(name, key, state.student_process, sol_value, Feedback(_msg)))
75+
_msg = state.build_message(incorrect_value_msg, {'key': key})
76+
rep.do_test(EqualValueProcessTest(name, key, state.student_process, sol_value, Feedback(_msg, state.highlight)))

pythonwhat/test_funcs/test_object.py

Lines changed: 21 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
import ast
2+
from pythonwhat.parsing import ObjectAssignmentParser
13
from pythonwhat.Test import DefinedProcessTest, EqualProcessTest
2-
from pythonwhat.State import State
34
from pythonwhat.Reporter import Reporter
45
from pythonwhat.Feedback import Feedback
5-
from pythonwhat.tasks import isDefinedInProcess, getRepresentation, ReprFail
6+
from pythonwhat.tasks import isDefinedInProcess, getRepresentation
7+
from pythonwhat.check_funcs import part_to_child, has_equal_value
8+
9+
MSG_UNDEFINED = "Have you defined `{parent[sol_part][name]}`?"
10+
MSG_INCORRECT = "The contents of `{parent[sol_part][name]}` aren't correct."
611

712
def test_object(name,
813
eq_condition="equal",
@@ -44,10 +49,11 @@ def test_object(name,
4449
rep = Reporter.active_reporter
4550
rep.set_tag("fun", "test_object")
4651

47-
state = check_object(name, undefined_msg, do_eval=do_eval, state=state)
52+
child = check_object(name, undefined_msg or MSG_UNDEFINED, state=state)
4853

4954
if do_eval:
50-
is_equal(name, incorrect_msg, state)
55+
56+
has_equal_value(incorrect_msg or MSG_INCORRECT, state=child)
5157

5258
def get_assignment_node(obj_ass, name):
5359
nodes = obj_ass[name] if name in obj_ass else None
@@ -58,39 +64,21 @@ def get_assignment_node(obj_ass, name):
5864

5965
# Check functions -------------------------------------------------------------
6066

61-
MSG_UNDEFINED = "Have you defined `{name}`?"
62-
MSG_INCORRECT = "The contents of `{name}` aren't correct."
63-
64-
def check_object(name, undefined_msg, do_eval=True, state=None):
67+
def check_object(name, undefined_msg=MSG_UNDEFINED, state=None):
6568
rep = Reporter.active_reporter
66-
if not undefined_msg:
67-
undefined_msg = MSG_UNDEFINED.format(name=name)
6869

6970
if not isDefinedInProcess(name, state.solution_process):
7071
raise NameError("%r not in solution environment " % name)
7172

72-
_msg = state.build_message(undefined_msg)
73-
rep.do_test(DefinedProcessTest(name, state.student_process, Feedback(_msg)))
74-
75-
if do_eval:
76-
sol_obj = getRepresentation(name, state.solution_process)
77-
if isinstance(sol_obj, ReprFail):
78-
raise NameError(sol_obj.info)
79-
80-
state.solution_object = sol_obj
81-
82-
return state
83-
84-
def is_equal(name, incorrect_msg, state=None):
85-
rep = Reporter.active_reporter
86-
if not incorrect_msg:
87-
incorrect_msg = MSG_INCORRECT.format(name=name)
73+
# create child state, using either parser output, or create part from name
74+
fallback = lambda: ObjectAssignmentParser.get_part(name)
75+
stu_part = state.student_object_assignments.get(name, fallback())
76+
sol_part = state.solution_object_assignments.get(name, fallback())
77+
78+
child = part_to_child(stu_part, sol_part, {'msg': '', 'kwargs': {}}, state)
8879

89-
ass_node = get_assignment_node(state.student_object_assignments, name)
90-
_msg = state.build_message(incorrect_msg)
91-
rep.do_test(EqualProcessTest(name,
92-
state.student_process,
93-
state.solution_object,
94-
Feedback(_msg, ass_node)))
95-
return state
80+
# test object exists
81+
_msg = child.build_message(undefined_msg)
82+
rep.do_test(DefinedProcessTest(name, child.student_process, Feedback(_msg)))
9683

84+
return child

pythonwhat/test_funcs/test_object_after_expression.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def test_object_after_expression(name,
7373
if not incorrect_msg:
7474
incorrect_msg = "Are you sure you assigned the correct value to `%s`?" % name
7575

76-
ass_node = get_assignment_node(state.student_object_assignments, name)
76+
ass_node = state.student_object_assignments.get(name, {}).get('highlight')
7777

7878
has_equal_value(
7979
incorrect_msg = incorrect_msg,

tests/test_instructor_warnings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ def test_converter_err(self):
88
"DC_SCT": """set_converter('builtins.dict', lambda: abc); test_object('d') """
99
}
1010
data['DC_CODE'] = data['DC_SOLUTION']
11-
self.assertRaises(NameError, lambda: helper.run(data))
11+
self.assertRaises(ValueError, lambda: helper.run(data))
1212

1313
if __name__ == "__main__":
1414
unittest.main()

tests/test_test_data_frame.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ def test_Fail2(self):
9090
sct_payload = helper.run(self.data)
9191
self.assertFalse(sct_payload['correct'])
9292
self.assertEqual(sct_payload['message'], 'Column <code>b</code> of your pandas DataFrame, <code>df</code>, is not correct.')
93+
helper.test_lines(self, sct_payload, 2, 6, 1, 2)
9394

9495
def test_Fail3(self):
9596
self.data["DC_CODE"] = '''

0 commit comments

Comments
 (0)