Skip to content

Commit 67895c1

Browse files
author
Filip Schouwenaars
authored
Merge pull request #165 from datacamp/spec-expose-check-object
move check_funcs all into test_object, expose to SCT creators
2 parents c1bd981 + ee99fb5 commit 67895c1

9 files changed

Lines changed: 208 additions & 99 deletions

pythonwhat/check_object.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
from pythonwhat.parsing import ObjectAssignmentParser
2+
from pythonwhat.Test import DefinedProcessTest, InstanceProcessTest, DefinedCollProcessTest, EqualValueProcessTest
3+
from pythonwhat.Reporter import Reporter
4+
from pythonwhat.Feedback import Feedback
5+
from pythonwhat.tasks import isDefinedInProcess, isInstanceInProcess, getValueInProcess, isDefinedCollInProcess, ReprFail
6+
from pythonwhat.check_funcs import part_to_child, has_equal_value
7+
8+
9+
MSG_PREPEND = "FMT:Check the variable `{index}`. "
10+
MSG_UNDEFINED = "FMT:Are you sure you defined the {typestr}, `{index}`?"
11+
MSG_INCORRECT_VAL = """FMT: Have you specified the correct value for "{key}" inside `{parent[sol_part][name]}`?"""
12+
MSG_KEY_MISSING = "__JINJA__:There is no {{ 'column' if 'DataFrame' in parent.typestr else 'key' }} inside {{parent.index}}."
13+
14+
def check_object(index, missing_msg=MSG_UNDEFINED, expand_msg=MSG_PREPEND, state=None, typestr="variable"):
15+
rep = Reporter.active_reporter
16+
17+
if not isDefinedInProcess(index, state.solution_process):
18+
raise NameError("%r not in solution environment " % index)
19+
20+
append_message = {'msg': expand_msg, 'kwargs': {'index': index, 'typestr': typestr}}
21+
22+
# create child state, using either parser output, or create part from name
23+
fallback = lambda: ObjectAssignmentParser.get_part(index)
24+
stu_part = state.student_object_assignments.get(index, fallback())
25+
sol_part = state.solution_object_assignments.get(index, fallback())
26+
27+
# test object exists
28+
_msg = state.build_message(missing_msg, append_message['kwargs'])
29+
rep.do_test(DefinedProcessTest(index, state.student_process, Feedback(_msg)))
30+
31+
child = part_to_child(stu_part, sol_part, append_message, state)
32+
33+
return child
34+
35+
def is_instance(inst, not_instance_msg="FMT:Is it a {inst.__name__}?", name=None, state=None):
36+
rep = Reporter.active_reporter
37+
38+
sol_name = name or state.solution_parts.get('name')
39+
stu_name = name or state.student_parts.get('name')
40+
41+
if not isInstanceInProcess(sol_name, inst, state.solution_process):
42+
raise ValueError("%r is not a %s in the solution environment" % (sol_name, type(inst)))
43+
44+
_msg = state.build_message(not_instance_msg, {'inst': inst})
45+
feedback = Feedback(_msg, state.highlight)
46+
rep.do_test(InstanceProcessTest(stu_name, inst, state.student_process, feedback))
47+
48+
return state
49+
50+
def has_key(key, key_missing_msg=MSG_KEY_MISSING, name = None, state=None):
51+
rep = Reporter.active_reporter
52+
53+
sol_name = name or state.solution_parts.get('name')
54+
stu_name = name or state.student_parts.get('name')
55+
56+
if not isDefinedCollInProcess(sol_name, key, state.solution_process):
57+
raise NameError("Not all keys you specified are actually keys in %s in the solution process" % sol_name)
58+
59+
# check if key available
60+
_msg = state.build_message(key_missing_msg, {'key': key})
61+
rep.do_test(DefinedCollProcessTest(stu_name, key, state.student_process,
62+
Feedback(_msg, state.highlight)))
63+
64+
return state
65+
66+
def has_equal_key(key, incorrect_value_msg=MSG_INCORRECT_VAL, key_missing_msg=MSG_KEY_MISSING, name=None, state=None):
67+
rep = Reporter.active_reporter
68+
69+
sol_name = name or state.solution_parts.get('name')
70+
stu_name = name or state.student_parts.get('name')
71+
72+
has_key(key, key_missing_msg, state=state)
73+
74+
sol_value, sol_str = getValueInProcess(sol_name, key, state.solution_process)
75+
if isinstance(sol_value, ReprFail):
76+
raise NameError("Value from %r can't be fetched from the solution process: %s" % c(sol_name, sol_value.info))
77+
78+
# check if value ok
79+
_msg = state.build_message(incorrect_value_msg, {'key': key})
80+
rep.do_test(EqualValueProcessTest(stu_name, key, state.student_process, sol_value, Feedback(_msg, state.highlight)))
81+
82+
return state

pythonwhat/check_wrappers.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
from pythonwhat.check_funcs import check_part, check_part_index, check_node, has_equal_part
2-
from pythonwhat import check_funcs
2+
from pythonwhat import check_funcs, check_object
3+
from pythonwhat.test_funcs.test_data_frame import check_df
4+
from pythonwhat.test_funcs.test_dictionary import check_dict
5+
from pythonwhat import test_funcs
36
from functools import partial
47
import inspect
58
#from jinja2 import Template
@@ -40,6 +43,7 @@
4043
scts['has_equal_name'] = partial(has_equal_part, 'name', msg='Make sure to use the correct {name}, was expecting {sol_part[name]}, instead got {stu_part[name]}.')
4144
scts['is_default'] = partial(has_equal_part, 'is_default', msg="__JINJA__:Make sure it {{ 'has' if sol_part.is_default else 'does not have'}} a default argument.")
4245

46+
# include rest of wrappers
4347
for k, v in __PART_WRAPPERS__.items():
4448
scts['check_'+k] = partial(check_part, k, v)
4549

@@ -57,3 +61,12 @@
5761
'check_args',
5862
'has_equal_part']:
5963
scts[k] = getattr(check_funcs, k)
64+
65+
# include check_object and friends ------
66+
for k in ['check_object', 'is_instance', 'has_equal_key', 'has_key']:
67+
scts[k] = getattr(check_object, k)
68+
69+
scts['check_df'] = check_df
70+
scts['check_dict'] = check_dict
71+
72+
Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
from pythonwhat.Reporter import Reporter
22
from pythonwhat.tasks import getColumnsInProcess
3-
from .test_object import check_object
4-
from .test_dictionary import is_instance, test_key, has_key
3+
from pythonwhat.check_object import check_object, is_instance, has_equal_key, has_key
54

6-
import pandas as pd
7-
8-
MSG_UNDEFINED = "FMT:Are you sure you defined the pandas DataFrame: `{parent[sol_part][name]}`?"
5+
MSG_UNDEFINED = "FMT:Are you sure you defined the pandas DataFrame: `{index}`?"
96
MSG_NOT_INSTANCE = "FMT:`{parent[sol_part][name]}` is not a pandas DataFrame."
107
MSG_KEY_MISSING = "FMT:There is no column `{key}` inside `{parent[sol_part][name]}`."
118
MSG_INCORRECT_VAL = "FMT:Column `{key}` of your pandas DataFrame, `{parent[sol_part][name]}`, is not correct."
129

10+
import pandas as pd
11+
1312
def test_data_frame(name,
1413
columns=None,
1514
undefined_msg=None,
@@ -23,7 +22,8 @@ def test_data_frame(name,
2322
rep = Reporter.active_reporter
2423
rep.set_tag("fun", "test_data_frame")
2524

26-
child = check_df(name, undefined_msg or MSG_UNDEFINED, not_data_frame_msg or MSG_NOT_INSTANCE, state=state)
25+
child = check_object(name, undefined_msg or MSG_UNDEFINED, expand_msg="", state=state, typestr="pandas DataFrame")
26+
is_instance(pd.DataFrame, not_data_frame_msg or MSG_NOT_INSTANCE, state=child) # test instance
2727

2828
sol_cols = getColumnsInProcess(name, child.solution_process)
2929
if sol_cols is None:
@@ -34,13 +34,14 @@ def test_data_frame(name,
3434

3535
for col in columns:
3636
# check if column available
37-
test_key(name, col, incorrect_msg or MSG_INCORRECT_VAL, undefined_cols_msg or MSG_KEY_MISSING, state=child)
37+
has_equal_key(col, incorrect_msg or MSG_INCORRECT_VAL, undefined_cols_msg or MSG_KEY_MISSING, state=child)
3838

39-
# Check functions -------------------------------------------------------------
4039

4140
def check_df(name, undefined_msg=MSG_UNDEFINED, not_instance_msg=MSG_NOT_INSTANCE, state=None):
4241

43-
child = check_object(name, undefined_msg, state=state) # test defined
44-
is_instance(name, pd.DataFrame, not_instance_msg, state=child) # test instance
42+
# test defined
43+
child = check_object(name, undefined_msg, state=state, typestr="pandas DataFrame")
44+
is_instance(pd.DataFrame, not_instance_msg, state=child) # test instance
4545

4646
return child
47+
Lines changed: 17 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,42 @@
11
from pythonwhat.Reporter import Reporter
2-
from pythonwhat.Test import InstanceProcessTest, DefinedCollProcessTest, EqualValueProcessTest
3-
from pythonwhat.Feedback import Feedback
4-
from pythonwhat.tasks import isInstanceInProcess, getKeysInProcess, getValueInProcess, isDefinedCollInProcess, ReprFail
5-
from .test_object import check_object
2+
from pythonwhat.tasks import getKeysInProcess
3+
from pythonwhat.check_object import check_object, is_instance, has_equal_key, has_key
64

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

1210
def test_dictionary(name,
1311
keys=None,
14-
undefined_msg=MSG_UNDEFINED,
15-
not_dictionary_msg=MSG_NOT_INSTANCE,
16-
key_missing_msg=MSG_KEY_MISSING,
17-
incorrect_value_msg=MSG_INCORRECT_VAL,
12+
undefined_msg=None,
13+
not_dictionary_msg=None,
14+
key_missing_msg=None,
15+
incorrect_value_msg=None,
1816
state=None):
1917
"""Test the contents of a dictionary.
2018
"""
2119

2220
rep = Reporter.active_reporter
2321
rep.set_tag("fun", "test_dictionary")
2422

25-
child = check_dict(name, undefined_msg or MSG_UNDEFINED, not_dictionary_msg or MSG_NOT_INSTANCE, state=state)
23+
child = check_object(name, undefined_msg or MSG_UNDEFINED, expand_msg = "", state=state, typestr="dictionary")
24+
is_instance(dict, not_dictionary_msg or MSG_NOT_INSTANCE, state=child) # test instance
2625

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

3130
for key in keys:
3231
# check if key in dictionary
33-
test_key(name, key, incorrect_value_msg or MSG_INCORRECT_VAL, key_missing_msg or MSG_KEY_MISSING, state=child)
32+
has_equal_key(key, incorrect_value_msg or MSG_INCORRECT_VAL, key_missing_msg or MSG_KEY_MISSING, state=child)
3433

35-
# Check functions -------------------------------------------------------------
3634

37-
def check_dict(name, undefined_msg=MSG_UNDEFINED, not_instance_msg=MSG_NOT_INSTANCE, state=None):
35+
def check_dict(name, undefined_msg=MSG_UNDEFINED, not_instance_msg=MSG_NOT_INSTANCE, expand_msg="", state=None):
3836

39-
child = check_object(name, undefined_msg, state=state) # test defined
40-
is_instance(name, dict, not_instance_msg, state=child) # test instance
37+
# test defined
38+
child = check_object(name, undefined_msg, state=state, typestr="dictionary")
39+
is_instance(dict, not_instance_msg, state=child) # test instance
4140

4241
return child
4342

44-
def is_instance(name, inst, not_instance_msg, state=None):
45-
rep = Reporter.active_reporter
46-
47-
if not isInstanceInProcess(name, inst, state.solution_process):
48-
raise ValueError("%r is not a %s in the solution environment" % (name, type(inst)))
49-
50-
_msg = state.build_message(not_instance_msg)
51-
feedback = Feedback(_msg, state.highlight)
52-
rep.do_test(InstanceProcessTest(name, inst, state.student_process, feedback))
53-
54-
def has_key(name, key, key_missing_msg, state=None):
55-
rep = Reporter.active_reporter
56-
57-
if not isDefinedCollInProcess(name, key, state.solution_process):
58-
raise NameError("Not all keys you specified are actually keys in %s in the solution process" % name)
59-
60-
# check if key available
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)))
64-
65-
def test_key(name, key, incorrect_value_msg, key_missing_msg, state=None):
66-
rep = Reporter.active_reporter
67-
68-
has_key(name, key, key_missing_msg, state=state)
69-
70-
sol_value, sol_str = getValueInProcess(name, key, state.solution_process)
71-
if isinstance(sol_value, ReprFail):
72-
raise NameError("Value from %r can't be fetched from the solution process: %s" % c(name, sol_value.info))
73-
74-
# check if value ok
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: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
1-
import ast
2-
from pythonwhat.parsing import ObjectAssignmentParser
3-
from pythonwhat.Test import DefinedProcessTest, EqualProcessTest
41
from pythonwhat.Reporter import Reporter
5-
from pythonwhat.Feedback import Feedback
6-
from pythonwhat.tasks import isDefinedInProcess, getRepresentation
72
from pythonwhat.check_funcs import part_to_child, has_equal_value
3+
from pythonwhat.check_object import check_object, MSG_UNDEFINED
84

9-
MSG_UNDEFINED = "FMT:Have you defined `{parent[sol_part][name]}`?"
10-
MSG_INCORRECT = "FMT:The contents of `{parent[sol_part][name]}` aren't correct."
5+
MSG_UNDEFINED = "FMT:Have you defined `{index}`?"
6+
MSG_INCORRECT = "FMT:The contents of `{parent[index]}` aren't correct."
117

128
def test_object(name,
139
eq_condition="equal",
@@ -57,36 +53,8 @@ def test_object(name,
5753
rep = Reporter.active_reporter
5854
rep.set_tag("fun", "test_object")
5955

60-
child = check_object(name, undefined_msg or MSG_UNDEFINED, state=state)
56+
child = check_object(name, undefined_msg or MSG_UNDEFINED, expand_msg = "", state=state)
6157

6258
if do_eval:
6359

6460
has_equal_value(incorrect_msg or MSG_INCORRECT, state=child)
65-
66-
def get_assignment_node(obj_ass, name):
67-
nodes = obj_ass[name] if name in obj_ass else None
68-
69-
# found a single case of assigning name
70-
if nodes and len(nodes) == 1:
71-
return nodes[0]
72-
73-
# Check functions -------------------------------------------------------------
74-
75-
def check_object(name, undefined_msg=MSG_UNDEFINED, state=None):
76-
rep = Reporter.active_reporter
77-
78-
if not isDefinedInProcess(name, state.solution_process):
79-
raise NameError("%r not in solution environment " % name)
80-
81-
# create child state, using either parser output, or create part from name
82-
fallback = lambda: ObjectAssignmentParser.get_part(name)
83-
stu_part = state.student_object_assignments.get(name, fallback())
84-
sol_part = state.solution_object_assignments.get(name, fallback())
85-
86-
child = part_to_child(stu_part, sol_part, {'msg': '', 'kwargs': {}}, state)
87-
88-
# test object exists
89-
_msg = child.build_message(undefined_msg)
90-
rep.do_test(DefinedProcessTest(name, child.student_process, Feedback(_msg)))
91-
92-
return child

pythonwhat/test_funcs/test_object_after_expression.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from pythonwhat.check_funcs import has_equal_value, check_part
2-
from .test_object import get_assignment_node
32

43
def test_object_after_expression(name,
54
extra_env=None,

tests/test_test_data_frame.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ def test_Pass1(self):
3030
sct_payload = helper.run(self.data)
3131
self.assertTrue(sct_payload['correct'])
3232

33+
def test_Pass1_spec2(self):
34+
self.data["DC_SCT"] = "Ex().check_df('df').has_equal_value()"
35+
self.test_Pass1()
36+
37+
def test_Pass1_spec2_manual(self):
38+
self.data["DC_SCT"] = "Ex().check_df('df').multi([has_equal_key(i) for i in ['a', 'b', 'c']])"
39+
self.test_Pass1()
40+
3341
def test_Pass2(self):
3442
self.data["DC_SCT"] = "test_data_frame('df', columns=['b','c'])"
3543
self.data["DC_CODE"] = '''
@@ -79,6 +87,30 @@ def test_Fail1(self):
7987
self.assertFalse(sct_payload['correct'])
8088
self.assertEqual(sct_payload['message'], 'Column <code>a</code> of your pandas DataFrame, <code>df</code>, is not correct.')
8189

90+
def test_Fail1_spec2_manual(self):
91+
self.data["DC_CODE"] = '''
92+
df = pd.DataFrame({
93+
'a': [1, 2, 2],
94+
'b': ['x', 'y', 'z'],
95+
'c': [True, False, True]
96+
})
97+
'''
98+
self.data["DC_SCT"] = "Ex().check_df('df').has_equal_key('a')"
99+
sct_payload = helper.run(self.data)
100+
self.assertFalse(sct_payload['correct'])
101+
102+
def test_Fail1_spec2_auto(self):
103+
self.data["DC_CODE"] = '''
104+
df = pd.DataFrame({
105+
'a': [1, 2, 2],
106+
'b': ['x', 'y', 'z'],
107+
'c': [True, False, True]
108+
})
109+
'''
110+
self.data["DC_SCT"] = "Ex().check_df('df').has_equal_value()"
111+
sct_payload = helper.run(self.data)
112+
self.assertFalse(sct_payload['correct'])
113+
82114
def test_Fail2(self):
83115
self.data["DC_CODE"] = '''
84116
df = pd.DataFrame({

0 commit comments

Comments
 (0)