forked from datacamp/pythonwhat
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.py
More file actions
142 lines (117 loc) · 4.55 KB
/
utils.py
File metadata and controls
142 lines (117 loc) · 4.55 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
import ast
from protowhat.Feedback import InstructorError
from pythonwhat.Feedback import Feedback
from pythonwhat.Test import EqualTest
from pythonwhat.checks.check_funcs import StubState
from pythonwhat.checks.has_funcs import evalCalls
from pythonwhat.tasks import ReprFail
def fix_format(arguments):
if isinstance(arguments, str):
arguments = (arguments,)
if isinstance(arguments, tuple):
arguments = list(arguments)
if isinstance(arguments, list):
arguments = {"args": arguments, "kwargs": {}}
if (
not isinstance(arguments, dict)
or "args" not in arguments
or "kwargs" not in arguments
):
raise ValueError(
"Wrong format of arguments in 'results', 'outputs' or 'errors'; either a list, or a dictionary with names args (a list) and kwargs (a dict)"
)
return arguments
def stringify(arguments):
vararg = str(arguments["args"])[1:-1]
kwarg = ", ".join(
["%s = %s" % (key, value) for key, value in arguments["kwargs"].items()]
)
if len(vararg) == 0:
if len(kwarg) == 0:
return "()"
else:
return "(" + kwarg + ")"
else:
if len(kwarg) == 0:
return "(" + vararg + ")"
else:
return "(" + ", ".join([vararg, kwarg]) + ")"
# TODO: test string syntax with check_function_def
# test argument syntax with check_lambda_function
def run_call(args, node, process, get_func, **kwargs):
# Get function expression
if isinstance(node, ast.FunctionDef): # function name
func_expr = ast.Name(id=node.name, ctx=ast.Load())
elif isinstance(node, ast.Lambda): # lambda body expr
func_expr = node
else:
raise InstructorError("Only function definition or lambda may be called")
ast.fix_missing_locations(func_expr)
return get_func(process=process, tree=func_expr, call=args, **kwargs)
MSG_CALL_INCORRECT = "Calling {{argstr}} should {{action}} `{{str_sol}}`, instead got {{str_stu if str_stu == 'no printouts' else '`' + str_stu + '`'}}."
MSG_CALL_ERROR = "Calling {{argstr}} should {{action}} `{{str_sol}}`, instead it errored out: `{{str_stu}}`."
MSG_CALL_ERROR_INV = (
"Calling {{argstr}} should {{action}} `{{str_sol}}`, instead got `{{str_stu}}`."
)
def call(
state,
args,
test="value",
incorrect_msg=None,
error_msg=None,
argstr=None,
func=None,
**kwargs
):
"""Use ``check_call()`` in combination with ``has_equal_x()`` instead.
"""
if incorrect_msg is None:
incorrect_msg = MSG_CALL_INCORRECT
if error_msg is None:
error_msg = MSG_CALL_ERROR_INV if test == "error" else MSG_CALL_ERROR
assert test in ("value", "output", "error")
get_func = evalCalls[test]
# Run for Solution --------------------------------------------------------
eval_sol, str_sol = run_call(
args, state.solution_parts["node"], state.solution_process, get_func, **kwargs
)
if (test == "error") ^ isinstance(eval_sol, Exception):
_msg = (
state.build_message(
"Calling {{argstr}} resulted in an error (or not an error if testing for one). Error message: {{type_err}} {{str_sol}}",
dict(type_err=type(eval_sol), str_sol=str_sol, argstr=argstr),
),
)
raise InstructorError(_msg)
if isinstance(eval_sol, ReprFail):
_msg = state.build_message(
"Can't get the result of calling {{argstr}}: {{eval_sol.info}}",
dict(argstr=argstr, eval_sol=eval_sol),
)
raise InstructorError(_msg)
# Run for Submission ------------------------------------------------------
eval_stu, str_stu = run_call(
args, state.student_parts["node"], state.student_process, get_func, **kwargs
)
action_strs = {
"value": "return",
"output": "print out",
"error": "error out with the message",
}
fmt_kwargs = {
"part": argstr,
"argstr": argstr,
"str_sol": str_sol,
"str_stu": str_stu,
"action": action_strs[test],
}
# either error test and no error, or vice-versa
stu_node = state.student_parts["node"]
stu_state = StubState(stu_node, state.highlighting_disabled)
if (test == "error") ^ isinstance(eval_stu, Exception):
_msg = state.build_message(error_msg, fmt_kwargs)
state.report(Feedback(_msg, stu_state))
# incorrect result
_msg = state.build_message(incorrect_msg, fmt_kwargs)
state.do_test(EqualTest(eval_sol, eval_stu, Feedback(_msg, stu_state), func))
return state