Skip to content

Commit ce575e2

Browse files
committed
prefix formatted messages with FMT
1 parent e7760d8 commit ce575e2

15 files changed

Lines changed: 96 additions & 77 deletions

pythonwhat/State.py

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,36 @@ def __len__(self):
3232
return len(self._items)
3333

3434

35-
class MsgFormatter(string.Formatter):
36-
def __init__(self, default='{{{0}}}'):
37-
self.default=default
38-
39-
def get_value(self, key, args, kwds):
40-
if isinstance(key, str):
41-
return kwds.get(key, self.default.format(key))
42-
else:
43-
Formatter.get_value(key, args, kwds)
44-
35+
#class MsgFormatter(string.Formatter):
36+
# def vformat(self, format_string, args, kwargs):
37+
# """Restricted vformat, which does not format entries with converters or format specs"""
38+
# used_args = set()
39+
# result = []
40+
# for chunk in string._string.formatter_parser(format_string):
41+
# orig = self._orig_from_chunk(*chunk)
42+
# # return original string if there are converters or format specs,
43+
# # otherwise, parse as normal
44+
# if chunk[1] and any(chunk[2:]):
45+
# result.append(orig)
46+
# elif chunk[0] and not any(chunk[1:]):
47+
# result.append(chunk[0])
48+
# else:
49+
# res, _ = self._vformat(orig, args, kwargs, used_args, 1)
50+
# result.append(res)
51+
# return "".join(result)
52+
#
53+
# def get_field(self, field_name, args, kwargs):
54+
# try:
55+
# return super().get_field(field_name, args, kwargs)
56+
# except (KeyError, AttributeError):
57+
# return "{"+field_name+"}", "NA"
58+
#
59+
# @staticmethod
60+
# def _orig_from_chunk(literal_text, field_name, format_spec, conversion):
61+
# # of form, literal_str {var_name!conversion:format_spec}
62+
# conversion = '!' + conversion if conversion else ""
63+
# format_spec = ":" + format_spec if format_spec else ""
64+
# return "%s{%s%s%s}"%(literal_text, field_name, conversion, format_spec)
4565

4666
class State(object):
4767
"""State of the SCT environment.
@@ -55,7 +75,6 @@ def __init__(self,
5575
student_context=None, solution_context=None,
5676
student_parts=None, solution_parts=None,
5777
highlight = None, messages=None,
58-
msg_formatter = None,
5978
**kwargs):
6079

6180
# Set basic fields from kwargs
@@ -83,8 +102,6 @@ def __init__(self,
83102

84103
self.highlight = self.student_tree if highlight is None else highlight
85104

86-
self.msg_formatter = MsgFormatter() if msg_formatter is None else msg_formatter
87-
88105
self.converters = get_manual_converters() # accessed only from root state
89106

90107
self.fun_usage = {}
@@ -123,11 +140,14 @@ def build_message(self, tail="", fmt_kwargs=None):
123140
msgs = self.messages[:] + [{'msg': tail or "", 'kwargs':fmt_kwargs}]
124141
# format messages in list, by iterating over previous, current, and next message
125142
for prev_d, d, next_d in zip([{}, *msgs[:-1]], msgs, [*msgs[1:], {}]):
126-
out = self.msg_formatter.format(d['msg'],
127-
parent = prev_d.get('kwargs'),
128-
child = next_d.get('kwargs'),
129-
this = d['kwargs'],
130-
**d['kwargs'])
143+
if d['msg'].startswith('FMT:'):
144+
out = d['msg'].replace('FMT:', "").format(
145+
parent = prev_d.get('kwargs'),
146+
child = next_d.get('kwargs'),
147+
this = d['kwargs'],
148+
**d['kwargs'])
149+
else:
150+
out = d['msg']
131151
out_list.append(out)
132152

133153
return "".join(out_list)
@@ -179,7 +199,6 @@ def to_child_state(self, student_subtree, solution_subtree,
179199
solution_process = self.solution_process,
180200
raw_student_output = self.raw_student_output,
181201
pre_exercise_tree = self.pre_exercise_tree,
182-
msg_formatter = self.msg_formatter,
183202
student_tree = student_subtree,
184203
solution_tree = solution_subtree,
185204
student_parts = student_parts,

pythonwhat/check_funcs.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ def check_part_index(name, index, part_msg,
6262
# return child state from part
6363
return part_to_child(stu_part, sol_part, append_message, state)
6464

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}. "
65+
MSG_MISSING = "FMT:The system wants to check the {ordinal} {typestr} you defined but hasn't found it."
66+
MSG_PREPEND = "FMT:Check your code in the {child[part]} of the {ordinal} {typestr}. "
6767
def check_node(name, index, typestr, missing_msg=MSG_MISSING, expand_msg=MSG_PREPEND, state=None):
6868
rep = Reporter.active_reporter
6969
stu_out = getattr(state, 'student_'+name)
@@ -281,8 +281,8 @@ def run_call(args, node, process, get_func, **kwargs):
281281
return get_func(process = process, tree=func_expr, call = fmt_args, **kwargs)
282282

283283

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"
284+
MSG_CALL_INCORRECT = "FMT:Calling it should result in {str_sol}, instead got {str_sol}"
285+
MSG_CALL_ERROR = "FMT:Calling it should result in {str_sol}, instead got an error"
286286
def call(args,
287287
test='value',
288288
incorrect_msg=MSG_CALL_INCORRECT,
@@ -327,7 +327,7 @@ def call(args,
327327
from pythonwhat import utils
328328
def has_expr(incorrect_msg,
329329
error_msg="Running an expression in the student process caused an issue",
330-
undefined_msg="Have you defined `{name}` without errors?",
330+
undefined_msg="FMT:Have you defined `{name}` without errors?",
331331
extra_env=None,
332332
context_vals=None,
333333
expr_code=None,

pythonwhat/test_funcs/test_comp.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
from pythonwhat.utils import get_ord
55
from pythonwhat.check_funcs import check_node, check_part, check_part_index, multi, has_equal_part_len
66

7-
MSG_NOT_CALLED = "The system wants to check the {ordinal} {typestr} you defined but hasn't found it."
8-
MSG_PREPEND = "Check your code in the {child[part]} of the {ordinal} {typestr}. "
7+
MSG_NOT_CALLED = "FMT:The system wants to check the {ordinal} {typestr} you defined but hasn't found it."
8+
MSG_PREPEND = "FMT:Check your code in the {child[part]} of the {ordinal} {typestr}. "
99

10-
MSG_INCORRECT_ITER_VARS = "Have you used the correct iterator variables in the {parent[ordinal]} {parent[typestr]}? Make sure you use the correct names!"
11-
MSG_INCORRECT_NUM_ITER_VARS = "Have you used {num_vars} iterator variables in the {parent[ordinal]} {parent[typestr]}?"
12-
MSG_INSUFFICIENT_IFS = "Have you used {sol_len} ifs inside the {parent[ordinal]} {parent[typestr]}?"
10+
MSG_INCORRECT_ITER_VARS = "FMT:Have you used the correct iterator variables in the {parent[ordinal]} {parent[typestr]}? Make sure you use the correct names!"
11+
MSG_INCORRECT_NUM_ITER_VARS = "FMT:Have you used {num_vars} iterator variables in the {parent[ordinal]} {parent[typestr]}?"
12+
MSG_INSUFFICIENT_IFS = "FMT:Have you used {sol_len} ifs inside the {parent[ordinal]} {parent[typestr]}?"
1313

1414
def test_list_comp(index=1,
1515
not_called_msg=None,

pythonwhat/test_funcs/test_data_frame.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55

66
import pandas as pd
77

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."
8+
MSG_UNDEFINED = "FMT:Are you sure you defined the pandas DataFrame: `{parent[sol_part][name]}`?"
9+
MSG_NOT_INSTANCE = "FMT:`{parent[sol_part][name]}` is not a pandas DataFrame."
10+
MSG_KEY_MISSING = "FMT:There is no column `{key}` inside `{parent[sol_part][name]}`."
11+
MSG_INCORRECT_VAL = "FMT:Column `{key}` of your pandas DataFrame, `{parent[sol_part][name]}`, is not correct."
1212

1313
def test_data_frame(name,
1414
columns=None,

pythonwhat/test_funcs/test_dictionary.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
from pythonwhat.tasks import isInstanceInProcess, getKeysInProcess, getValueInProcess, isDefinedCollInProcess, ReprFail
55
from .test_object import check_object
66

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]}`?"
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]}`?"
1111

1212
def test_dictionary(name,
1313
keys=None,

pythonwhat/test_funcs/test_expression_output.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ def test_expression_output(extra_env=None,
6666
feedback_msg = incorrect_msg
6767
else:
6868
if expr_code is not None:
69-
prestring = "When running %s e" % expr_code
69+
prestring = "FMT:When running %s e" % expr_code
7070
else:
71-
prestring = "E"
71+
prestring = "FMT:E"
7272
feedback_msg = "%sxpected output `{sol_eval}`, instead got `{stu_eval}`" % (prestring)
7373
if extra_env:
7474
# need double brackets to not screw up string formatting

pythonwhat/test_funcs/test_expression_result.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def test_expression_result(extra_env=None,
8080
else:
8181
# need to double bracket extra_env, so doesn't mess up str templating
8282
feedback_msg = (
83-
"Unexpected expression: expected `{sol_eval}`, got `{stu_eval}` with values"
83+
"FMT:Unexpected expression: expected `{sol_eval}`, got `{stu_eval}` with values"
8484
" " + str(extra_env).replace('{', '{{').replace('}','}}') if extra_env else "."
8585
)
8686

pythonwhat/test_funcs/test_for_loop.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
from functools import partial
55

6-
MSG_MISSING = "Define more {typestr}."
7-
MSG_PREPEND = "Check your code in the {child[part]} of the {ordinal} for loop. "
6+
MSG_MISSING = "FMT:Define more {typestr}."
7+
MSG_PREPEND = "FMT:Check your code in the {child[part]} of the {ordinal} for loop. "
88

99
def test_for_loop(index=1,
1010
for_iter=None,

pythonwhat/test_funcs/test_function_definition.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,28 @@
33

44
from functools import partial
55

6-
MSG_MISSING = "You didn't define the following function: {typestr}."
7-
MSG_PREPEND = "Check your definition of {typestr}. "
6+
MSG_MISSING = "FMT:You didn't define the following function: {typestr}."
7+
MSG_PREPEND = "FMT:Check your definition of {typestr}. "
88

9-
MSG_NUM_ARGS = "You should define {parent[typestr]} with {sol_len} arguments, instead got {stu_len}."
9+
MSG_NUM_ARGS = "FMT:You should define {parent[typestr]} with {sol_len} arguments, instead got {stu_len}."
1010

11-
MSG_PREPEND_ARG = "In your definition of {typestr}, "
12-
MSG_BAD_ARG_NAME = "the {parent[ordinal]} {parent[part]} should be called `{sol_part[name]}`, instead got `{stu_part[name]}`."
13-
MSG_BAD_DEFAULT = "the {parent[part]} `{stu_part[name]}` should have no default."
14-
MSG_INC_DEFAULT = "the {parent[part]} `{stu_part[name]}` does not have the correct default."
11+
MSG_PREPEND_ARG = "FMT:In your definition of {typestr}, "
12+
MSG_BAD_ARG_NAME = "FMT:the {parent[ordinal]} {parent[part]} should be called `{sol_part[name]}`, instead got `{stu_part[name]}`."
13+
MSG_BAD_DEFAULT = "FMT:the {parent[part]} `{stu_part[name]}` should have no default."
14+
MSG_INC_DEFAULT = "FMT:the {parent[part]} `{stu_part[name]}` does not have the correct default."
1515

16-
MSG_NO_VARARG = "have you specified an argument to take a `*` argument and named it `{sol_part[*args][name]}`?"
17-
MSG_NO_KWARGS = "have you specified an argument to take a `**` argument and named it `{sol_part[**kwargs][name]}`?"
18-
MSG_VARARG_NAME = "have you specified an argument to take a `*` argument and named it `{sol_part[name]}`?"
19-
MSG_KWARG_NAME = "have you specified an argument to take a `**` argument and named it `{sol_part[name]}`?"
16+
MSG_NO_VARARG = "FMT:have you specified an argument to take a `*` argument and named it `{sol_part[*args][name]}`?"
17+
MSG_NO_KWARGS = "FMT:have you specified an argument to take a `**` argument and named it `{sol_part[**kwargs][name]}`?"
18+
MSG_VARARG_NAME = "FMT:have you specified an argument to take a `*` argument and named it `{sol_part[name]}`?"
19+
MSG_KWARG_NAME = "FMT:have you specified an argument to take a `**` argument and named it `{sol_part[name]}`?"
2020

2121
# TODO some need to reference the eval rather than str
22-
MSG_RES_ERROR = "Calling `{argstr}` should result in `{str_sol}`, instead got an error."
23-
MSG_RES_INCORRECT = "Calling `{argstr}` should result in `{str_sol}`, instead got `{str_stu}`."
24-
MSG_ERR_NONE = "Calling `{argstr}` doesn't result in an error, but it should!"
25-
MSG_ERR_INCORRECT = "Calling `{argstr}` should result in a `{str_sol.__class__.__name__}`, instead got a `{str_stu.__class__.__name__}`."
26-
MSG_OUT_ERROR = "Calling `{argstr}` should output {str_sol}, instead got an error."
27-
MSG_OUT_INCORRECT = "Calling `{argstr}` should output `{str_sol}`, instead got {str_stu}."
22+
MSG_RES_ERROR = "FMT:Calling `{argstr}` should result in `{str_sol}`, instead got an error."
23+
MSG_RES_INCORRECT = "FMT:Calling `{argstr}` should result in `{str_sol}`, instead got `{str_stu}`."
24+
MSG_ERR_NONE = "FMT:Calling `{argstr}` doesn't result in an error, but it should!"
25+
MSG_ERR_INCORRECT = "FMT:Calling `{argstr}` should result in a `{str_sol.__class__.__name__}`, instead got a `{str_stu.__class__.__name__}`."
26+
MSG_OUT_ERROR = "FMT:Calling `{argstr}` should output {str_sol}, instead got an error."
27+
MSG_OUT_INCORRECT = "FMT:Calling `{argstr}` should output `{str_sol}`, instead got {str_stu}."
2828

2929
def test_function_definition(name,
3030
arg_names=True,

pythonwhat/test_funcs/test_if_else.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
from functools import partial, update_wrapper
55

6-
MSG_MISSING = "The system wants to check the {ordinal} {typestr}, but it hasn't found it. Have another look at your code."
7-
MSG_PREPEND = "Check your code in the {child[part]} of the {ordinal} `if` statement. "
6+
MSG_MISSING = "FMT:The system wants to check the {ordinal} {typestr}, but it hasn't found it. Have another look at your code."
7+
MSG_PREPEND = "FMT:Check your code in the {child[part]} of the {ordinal} `if` statement. "
88

99
def test_if_else(index=1,
1010
test=None,

0 commit comments

Comments
 (0)