forked from datacamp/pythonwhat
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcheck_object.py
More file actions
186 lines (128 loc) · 7.09 KB
/
Copy pathcheck_object.py
File metadata and controls
186 lines (128 loc) · 7.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
from pythonwhat.parsing import ObjectAssignmentParser
from pythonwhat.Test import DefinedProcessTest, InstanceProcessTest, DefinedCollProcessTest, EqualValueProcessTest
from pythonwhat.Reporter import Reporter
from pythonwhat.Feedback import Feedback
from pythonwhat.tasks import isDefinedInProcess, isInstanceInProcess, getValueInProcess, isDefinedCollInProcess, ReprFail
from pythonwhat.check_funcs import part_to_child, has_equal_value
MSG_PREPEND = "__JINJA__:Did you correctly define the variable `{{index}}`? "
MSG_UNDEFINED = "__JINJA__:Did you define the {{typestr}} `{{index}}` without errors?"
MSG_INCORRECT_VAL = """FMT: Have you specified the correct value for `{key}` inside `{parent[sol_part][name]}`?"""
MSG_KEY_MISSING = "__JINJA__:There is no {{ 'column' if 'DataFrame' in parent.typestr else 'key' }} inside {{parent.index}}."
def check_object(index, missing_msg=MSG_UNDEFINED, expand_msg=MSG_PREPEND, state=None, typestr="variable"):
"""Check object existence (and equality)
Check whether an object is defined in the student's environment, and zoom in on its value in both
student and solution environment to inspect quality (with has_equal_value().
Args:
index (str): the name of the object which value has to be checked.
missing_msg (str): feedback message when the object is not defined in the student's environment.
expect_msg (str): prepending message to put in front.
:Example:
Student code::
b = 1
c = 3
Solution code::
a = 1
b = 2
c = 3
SCT::
Ex().check_object("a") # fail
Ex().check_object("b") # pass
Ex().check_object("b").has_equal_value() # fail
Ex().check_object("c").has_equal_value() # pass
"""
rep = Reporter.active_reporter
if not isDefinedInProcess(index, state.solution_process):
raise NameError("%r not in solution environment " % index)
append_message = {'msg': expand_msg, 'kwargs': {'index': index, 'typestr': typestr}}
# create child state, using either parser output, or create part from name
fallback = lambda: ObjectAssignmentParser.get_part(index)
stu_part = state.student_object_assignments.get(index, fallback())
sol_part = state.solution_object_assignments.get(index, fallback())
# test object exists
_msg = state.build_message(missing_msg, append_message['kwargs'])
rep.do_test(DefinedProcessTest(index, state.student_process, Feedback(_msg)))
child = part_to_child(stu_part, sol_part, append_message, state)
return child
def is_instance(inst, not_instance_msg="__JINJA__:Is it a {{inst.__name__}}?", state=None):
"""Check whether an object is an instance of a certain class.
``is_instance()`` can currently only be used when chained from ``check_object()``, the function that is
used to 'zoom in' on the object of interest.
Args:
inst (class): The class that the object should have.
not_instance_msg (str): When specified, this overrides the automatically generated message in case
the object does not have the expected class.
state (State): The state that is passed in through the SCT chain (don't specify this).
:Example:
Student code and solution code::
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
SCT::
# Verify the class of arr
import numpy
Ex().check_object('arr').is_instance(numpy.ndarray)
"""
rep = Reporter.active_reporter
sol_name = state.solution_parts.get('name')
stu_name = state.student_parts.get('name')
if not isInstanceInProcess(sol_name, inst, state.solution_process):
raise ValueError("%r is not a %s in the solution environment" % (sol_name, type(inst)))
_msg = state.build_message(not_instance_msg, {'inst': inst})
feedback = Feedback(_msg, state)
rep.do_test(InstanceProcessTest(stu_name, inst, state.student_process, feedback))
return state
def has_key(key, key_missing_msg=MSG_KEY_MISSING, state=None):
"""Check whether an object (dict, DataFrame, etc) has a key.
``has_key()`` can currently only be used when chained from ``check_object()``, the function that is
used to 'zoom in' on the object of interest.
Args:
key (str): Name of the key that the object should have.
key_missing_msg (str): When specified, this overrides the automatically generated
message in case the key does not exist.
state (State): The state that is passed in through the SCT chain (don't specify this).
:Example:
Student code and solution code::
x = {'a': 2}
SCT::
# Verify that x contains a key a
Ex().check_object('x').has_key('a')
"""
rep = Reporter.active_reporter
sol_name = state.solution_parts.get('name')
stu_name = state.student_parts.get('name')
if not isDefinedCollInProcess(sol_name, key, state.solution_process):
raise NameError("Not all keys you specified are actually keys in %s in the solution process" % sol_name)
# check if key available
_msg = state.build_message(key_missing_msg, {'key': key})
rep.do_test(DefinedCollProcessTest(stu_name, key, state.student_process,
Feedback(_msg, state)))
return state
def has_equal_key(key, incorrect_value_msg=MSG_INCORRECT_VAL, key_missing_msg=MSG_KEY_MISSING, state=None):
"""Check whether an object (dict, DataFrame, etc) has a key, and whether this
key is correct when comparing to the solution code.
``has_equal_key()`` can currently only be used when chained from ``check_object()``, the function that is
used to 'zoom in' on the object of interest.
Args:
key (str): Name of the key that the object should have.
incorrect_value_msg (str): When specified, this overrides the automatically generated
message in case the key does not correspond to the value of the key in the solution process.
key_missing_msg (str): When specified, this overrides the automatically generated
message in case the key does not exist.
state (State): The state that is passed in through the SCT chain (don't specify this).
:Example:
Student code and solution code::
x = {'a': 2}
SCT::
# Verify that x contains a key a and whether it is correct
Ex().check_object('x').has_equal_key('a')
"""
rep = Reporter.active_reporter
sol_name = state.solution_parts.get('name')
stu_name = state.student_parts.get('name')
has_key(key, key_missing_msg, state=state)
sol_value, sol_str = getValueInProcess(sol_name, key, state.solution_process)
if isinstance(sol_value, ReprFail):
raise NameError("Value from %r can't be fetched from the solution process: %s" % c(sol_name, sol_value.info))
# check if value ok
_msg = state.build_message(incorrect_value_msg, {'key': key})
rep.do_test(EqualValueProcessTest(stu_name, key, state.student_process, sol_value, Feedback(_msg, state)))
return state