55from functools import partial
66import copy
77
8- def part_to_child (stu_part , sol_part , append_message , state ):
8+ def part_to_child (stu_part , sol_part , append_message , state , node_name = None ):
99 # stu_part and sol_part will be accessible on all templates
1010 append_message ['kwargs' ].update ({'stu_part' : stu_part , 'sol_part' : sol_part })
1111
@@ -15,13 +15,13 @@ def part_to_child(stu_part, sol_part, append_message, state):
1515 stu_part .get ('target_vars' ), sol_part .get ('target_vars' ),
1616 stu_part , sol_part ,
1717 highlight = stu_part .get ('highlight' ),
18- append_message = append_message )
18+ append_message = append_message , node_name = node_name )
1919
2020 # otherwise, assume they are just nodes
2121 return state .to_child_state (stu_part , sol_part , append_message = append_message )
2222
2323
24- def check_part (name , part_msg , state = None , missing_msg = "" , expand_msg = "" ):
24+ def check_part (name , part_msg , state = None , missing_msg = "Are you sure it's defined? " , expand_msg = "" ):
2525 """Return child state with name part as its ast tree"""
2626 rep = Reporter .active_reporter
2727
@@ -36,17 +36,19 @@ def check_part(name, part_msg, state=None, missing_msg="", expand_msg=""):
3636 return part_to_child (stu_part , sol_part , append_message , state )
3737
3838def check_part_index (name , index , part_msg ,
39- missing_msg = "Define more {part}. " ,
39+ missing_msg = "FMT:Are you sure it is defined? " ,
4040 state = None , expand_msg = "" ):
4141 """Return child state with indexed name part as its ast tree"""
4242
4343 rep = Reporter .active_reporter
4444
4545 # create message
4646 ordinal = "" if isinstance (index , str ) else get_ord (index + 1 )
47+ fmt_kwargs = {'index' : index , 'ordinal' : ordinal }
48+ fmt_kwargs ['part' ] = part_msg .format (** fmt_kwargs )
4749
4850 append_message = {'msg' : expand_msg ,
49- 'kwargs' : { 'part' : part_msg , 'index' : index , 'ordinal' : ordinal } }
51+ 'kwargs' : fmt_kwargs }
5052
5153 # check there are enough parts for index
5254 stu_parts = state .student_parts [name ]
@@ -62,16 +64,17 @@ def check_part_index(name, index, part_msg,
6264 # return child state from part
6365 return part_to_child (stu_part , sol_part , append_message , state )
6466
65- MSG_MISSING = "The system wants to check the {ordinal} {typestr} you defined but hasn't found it."
66- MSG_PREPEND = "Check your code in the {child[part]} of the {ordinal} { typestr}. "
67+ MSG_MISSING = "FMT: The system wants to check the {typestr} you defined but hasn't found it."
68+ MSG_PREPEND = "__JINJA__: Check your code in the {{ child[' part']+ ' of the' if child['part']}} {{ typestr} }. "
6769def check_node (name , index , typestr , missing_msg = MSG_MISSING , expand_msg = MSG_PREPEND , state = None ):
6870 rep = Reporter .active_reporter
6971 stu_out = getattr (state , 'student_' + name )
7072 sol_out = getattr (state , 'solution_' + name )
7173
7274 # check if there are enough nodes for index
7375 fmt_kwargs = {'ordinal' : get_ord (index + 1 ) if isinstance (index , int ) else "" ,
74- 'typestr' : typestr }
76+ 'index' : index }
77+ fmt_kwargs ['typestr' ] = typestr .format (** fmt_kwargs )
7578
7679 # test if node can be indexed succesfully
7780 try : stu_out [index ]
@@ -87,7 +90,10 @@ def check_node(name, index, typestr, missing_msg=MSG_MISSING, expand_msg=MSG_PRE
8790 'kwargs' : fmt_kwargs
8891 }
8992
90- return part_to_child (stu_part , sol_part , append_message , state )
93+ return part_to_child (stu_part , sol_part , append_message , state , node_name = name )
94+
95+
96+ # Part tests ------------------------------------------------------------------
9197
9298def has_part (name , msg , state = None , fmt_kwargs = None ):
9399 rep = Reporter .active_reporter
@@ -112,9 +118,8 @@ def has_equal_part(name, msg, state):
112118 'sol_part' : state .solution_parts ,
113119 'name' : name }
114120
115- if d ['stu_part' ][name ] != d ['sol_part' ][name ]:
116- _msg = state .build_message (msg , d )
117- rep .do_test (Test (Feedback (_msg , state .highlight )))
121+ _msg = state .build_message (msg , d )
122+ rep .do_test (EqualTest (d ['stu_part' ][name ], d ['sol_part' ][name ], Feedback (_msg , state .highlight )))
118123
119124 return state
120125
@@ -221,11 +226,11 @@ def set_context(*args, state=None, **kwargs):
221226 student_context = out_stu , solution_context = out_sol )
222227
223228
224- def check_arg (name , missing_msg = 'check the argument `{part}`, ' , state = None ):
229+ def check_args (name , missing_msg = 'FMT:Are you sure it is defined? ' , state = None ):
225230 if name in ['*args' , '**kwargs' ]:
226231 return check_part (name , name , state = state , missing_msg = missing_msg )
227232 else :
228- return check_part_index ('args' , name , name , state = state , missing_msg = missing_msg )
233+ return check_part_index ('args' , name , "argument `%s`" % name , state = state , missing_msg = missing_msg )
229234
230235
231236# CALL CHECK ==================================================================
@@ -281,14 +286,14 @@ def run_call(args, node, process, get_func, **kwargs):
281286 return get_func (process = process , tree = func_expr , call = fmt_args , ** kwargs )
282287
283288
284- MSG_CALL_INCORRECT = "Calling it should result in {str_sol}, instead got {str_sol}"
285- MSG_CALL_ERROR = "Calling it should result in {str_sol}, instead got an error"
289+ MSG_CALL_INCORRECT = "FMT: Calling it should result in {str_sol}, instead got {str_sol}"
290+ MSG_CALL_ERROR = "FMT: Calling it should result in {str_sol}, instead got an error"
286291def call (args ,
287292 test = 'value' ,
288293 incorrect_msg = MSG_CALL_INCORRECT ,
289294 error_msg = MSG_CALL_ERROR ,
290- # TODO hardcoded lambda description for now
291- argstr = 'the Xth lambda function ' ,
295+ # TODO kept for backwards compatibility in test_function_definition/lambda
296+ argstr = '' ,
292297 state = None , ** kwargs ):
293298 rep = Reporter .active_reporter
294299 test_type = ('value' , 'output' , 'error' )
@@ -299,11 +304,14 @@ def call(args,
299304 eval_sol , str_sol = run_call (args , state .solution_parts ['node' ], state .solution_process , get_func , ** kwargs )
300305
301306 if (test == 'error' ) ^ isinstance (str_sol , Exception ):
302- _msg_prefix = "Calling %s for arguments %s " % (argstr , args )
303- raise ValueError (_msg_prefix + call_warnings [test ])
307+ _msg = state .build_message ("FMT:Calling for arguments {args} resulted in an error (or not an error if testing for one). Error message: {str_sol}" ,
308+ dict (args = args , str_sol = str_sol ))
309+ raise ValueError (_msg )
304310
305311 if isinstance (eval_sol , ReprFail ):
306- raise ValueError ("Can't get the result of calling %s for arguments %s: %s" % (argstr , args , eval_sol .info ))
312+ _msg = state .build_message ("FMT:Can't get the result of calling it for arguments {args}: {eval_sol.info}" ,
313+ dict (args = args , eval_sol = eval_sol ))
314+ raise ValueError (_msg )
307315
308316 # Run for Submission ------------------------------------------------------
309317 eval_stu , str_stu = run_call (args , state .student_parts ['node' ], state .student_process , get_func , ** kwargs )
@@ -323,11 +331,10 @@ def call(args,
323331
324332# Expression tests ------------------------------------------------------------
325333from pythonwhat .tasks import ReprFail , UndefinedValue
326- from pythonwhat .Test import EqualTest
327334from pythonwhat import utils
328- def has_expr (incorrect_msg ,
335+ def has_expr (incorrect_msg = "FMT:Unexpected expression {test}: expected `{sol_eval}`, got `{stu_eval}` with values{extra_env}." ,
329336 error_msg = "Running an expression in the student process caused an issue" ,
330- undefined_msg = "Have you defined `{name}` without errors?" ,
337+ undefined_msg = "FMT: Have you defined `{name}` without errors?" ,
331338 extra_env = None ,
332339 context_vals = None ,
333340 expr_code = None ,
@@ -359,7 +366,8 @@ def has_expr(incorrect_msg,
359366 context = state .solution_context )
360367
361368 if (test == 'error' ) ^ isinstance (str_sol , Exception ):
362- raise ValueError ("evaluating expression raised error in solution process" )
369+ raise ValueError ("evaluating expression raised error in solution process (or not an error if testing for one). "
370+ "Error message: %s" % str_sol )
363371 if isinstance (eval_sol , ReprFail ):
364372 raise ValueError ("Couldn't figure out the value of a default argument: " + eval_sol .info )
365373
@@ -368,7 +376,9 @@ def has_expr(incorrect_msg,
368376 context = state .student_context )
369377
370378 # kwargs ---
371- fmt_kwargs = {'stu_part' : state .student_parts , 'sol_part' : state .solution_parts , 'name' : name }
379+ fmt_kwargs = {'stu_part' : state .student_parts , 'sol_part' : state .solution_parts ,
380+ 'name' : name , 'test' : test ,
381+ 'extra_env' : " " + str (extra_env or "" ), 'context_vals' : context_vals }
372382 fmt_kwargs ['stu_eval' ] = utils .shorten_str (str (eval_stu ))
373383 fmt_kwargs ['sol_eval' ] = utils .shorten_str (str (eval_sol ))
374384
0 commit comments