Skip to content

Commit b2bf6df

Browse files
authored
Merge pull request #383 from datacamp/jh/allow-errors
Expose allow_errors SCT + fix using _debug
2 parents 42be017 + f0578a4 commit b2bf6df

9 files changed

Lines changed: 90 additions & 50 deletions

File tree

docs/reference.rst

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ has_equal_x
4444
Combining SCTs
4545
--------------
4646

47-
.. autofunction:: pythonwhat.checks.check_logic.multi
48-
.. autofunction:: pythonwhat.checks.check_logic.check_correct
49-
.. autofunction:: pythonwhat.checks.check_logic.check_or
50-
.. autofunction:: pythonwhat.checks.check_logic.check_not
47+
.. autofunction:: protowhat.checks.check_logic.multi
48+
.. autofunction:: protowhat.checks.check_logic.check_correct
49+
.. autofunction:: protowhat.checks.check_logic.check_or
50+
.. autofunction:: protowhat.checks.check_logic.check_not
5151

5252
Function/Class/Lambda definitions
5353
---------------------------------
@@ -95,4 +95,5 @@ Electives
9595

9696
.. autofunction:: pythonwhat.checks.has_funcs.has_chosen
9797
.. autofunction:: pythonwhat.test_exercise.success_msg
98-
.. autofunction:: pythonwhat.checks.check_logic.fail
98+
.. autofunction:: protowhat.checks.check_simple.allow_errors
99+
.. autofunction:: protowhat.checks.check_logic.fail

pythonwhat/State.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ def to_child(self, append_message="", node_name="", **kwargs):
113113
student tree and solution tree. This is necessary when testing if statements or
114114
for loops for example.
115115
"""
116+
bad_pars = set(kwargs) - set(self.params)
117+
if bad_pars:
118+
raise ValueError("Invalid init params for State: %s" % ", ".join(bad_pars))
119+
116120
base_kwargs = {
117121
attr: getattr(self, attr)
118122
for attr in self.params
@@ -121,7 +125,6 @@ def to_child(self, append_message="", node_name="", **kwargs):
121125

122126
if not isinstance(append_message, dict):
123127
append_message = {"msg": append_message, "kwargs": {}}
124-
125128
kwargs["messages"] = [*self.messages, append_message]
126129

127130
def update_kwarg(name, func):
@@ -156,7 +159,16 @@ def update_context(name):
156159
kwargs.pop(context)
157160

158161
klass = self.SUBCLASSES[node_name] if node_name else State
159-
child = klass(**{**base_kwargs, **kwargs})
162+
init_kwargs = {**base_kwargs, **kwargs}
163+
child = klass(**init_kwargs)
164+
165+
extra_attrs = set(vars(self)) - set(self.params)
166+
for attr in extra_attrs:
167+
# don't copy attrs set on new instances in init
168+
# the cached manual_sigs is passed
169+
if attr not in {"params", "ast_dispatcher", "converters"}:
170+
setattr(child, attr, getattr(self, attr))
171+
160172
return child
161173

162174
def has_different_processes(self):

pythonwhat/checks/check_wrappers.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from protowhat.utils import _debug
2+
from protowhat.checks.check_simple import allow_errors
23
from protowhat.checks.check_files import check_file, has_dir
34
from pythonwhat.checks.check_funcs import check_part, check_part_index, check_node
45
from pythonwhat.checks.has_funcs import has_equal_part
5-
from pythonwhat.checks import check_object, check_logic, check_funcs, has_funcs
66
from pythonwhat.checks.check_function import check_function
77
from pythonwhat.checks.check_has_context import has_context
8+
from pythonwhat.checks import check_object, check_logic, check_funcs, has_funcs
89
from pythonwhat.local import run
910

1011
from inspect import signature, Parameter
@@ -771,9 +772,14 @@ def rename_function(func, name):
771772

772773
scts["has_context"] = has_context
773774
scts["check_function"] = check_function
775+
774776
scts["run"] = run
777+
775778
scts["check_file"] = check_file
776779
scts["has_dir"] = has_dir
780+
781+
scts["allow_errors"] = allow_errors
782+
777783
scts["_debug"] = _debug
778784

779785
locals().update(scts)

pythonwhat/test_exercise.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def test_exercise(
6666
return state.reporter.build_final_payload()
6767

6868

69-
# TODO: consistent success_msg and allow_errors
69+
# TODO: consistent success_msg
7070
def success_msg(message):
7171
"""
7272
Set the succes message of the sct. This message will be the feedback if all tests pass.
@@ -76,6 +76,7 @@ def success_msg(message):
7676
State.root_state.reporter.success_msg = message
7777

7878

79+
# deprecated
7980
def allow_errors():
8081
State.root_state.reporter.errors_allowed = True
8182

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# pythonwhat deps
2-
protowhat~=1.8.2
2+
protowhat~=1.9.0
33
asttokens~=1.1.10
44
dill~=0.2.7.1
55
markdown2~=2.3.7

tests/helper.py

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ def wrapper(*args, **kwargs):
4343
test_exercise = capture_test_data(test_exercise)
4444

4545

46+
@contextmanager
47+
def in_temp_dir():
48+
with tempfile.TemporaryDirectory() as d:
49+
with ChDir(d):
50+
yield
51+
52+
4653
def run(data, run_code=True):
4754

4855
pec = data.get("DC_PEC", "")
@@ -51,30 +58,29 @@ def run(data, run_code=True):
5158
sct = data.get("DC_SCT", "")
5259
force_diagnose = data.get("DC_FORCE_DIAGNOSE", False)
5360

54-
with tempfile.TemporaryDirectory() as d:
55-
with ChDir(d):
56-
if run_code:
57-
sol_process, stu_process, raw_stu_output, error = run_exercise(
58-
pec, sol_code, stu_code
59-
)
60-
else:
61-
raw_stu_output = ""
62-
stu_process = StubProcess() # WorkerProcess()
63-
sol_process = StubProcess() # WorkerProcess()
64-
error = None
65-
66-
res = test_exercise(
67-
sct=sct,
68-
student_code=stu_code,
69-
solution_code=sol_code,
70-
pre_exercise_code=pec,
71-
student_process=stu_process,
72-
solution_process=sol_process,
73-
raw_student_output=raw_stu_output,
74-
ex_type="NormalExercise",
75-
force_diagnose=force_diagnose,
76-
error=error,
61+
with in_temp_dir():
62+
if run_code:
63+
sol_process, stu_process, raw_stu_output, error = run_exercise(
64+
pec, sol_code, stu_code
7765
)
66+
else:
67+
raw_stu_output = ""
68+
stu_process = StubProcess() # WorkerProcess()
69+
sol_process = StubProcess() # WorkerProcess()
70+
error = None
71+
72+
res = test_exercise(
73+
sct=sct,
74+
student_code=stu_code,
75+
solution_code=sol_code,
76+
pre_exercise_code=pec,
77+
student_process=stu_process,
78+
solution_process=sol_process,
79+
raw_student_output=raw_stu_output,
80+
ex_type="NormalExercise",
81+
force_diagnose=force_diagnose,
82+
error=error,
83+
)
7884

7985
return res
8086

tests/test_check_files.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,9 @@ def test_file_content(temp_file):
8787
expected_content = cf.get_file_content(temp_file.name)
8888
chain = setup_state("", "", pec="")
8989

90-
chain.check_file(temp_file.name, parse=False).has_code(expected_content.split("\n")[0])
90+
chain.check_file(temp_file.name, parse=False).has_code(
91+
expected_content.split("\n")[0]
92+
)
9193
chain.check_file(
9294
temp_file.name, parse=False, solution_code=expected_content
9395
).has_code(expected_content.split("\n")[0])
@@ -161,6 +163,6 @@ def test_running_file_with_root_check(temp_py_file):
161163

162164
with tempfile.TemporaryDirectory() as d:
163165
with ChDir(d):
164-
chain.check_file(
165-
temp_py_file.name, solution_code=content
166-
).run().has_output("Hi")
166+
chain.check_file(temp_py_file.name, solution_code=content).run().has_output(
167+
"Hi"
168+
)

tests/test_debug.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22
import tests.helper as helper
33

44

5+
def test_debug_on_error():
6+
data = {
7+
"DC_PEC": "",
8+
"DC_CODE": "x = 123",
9+
"DC_SOLUTION": "x = 122",
10+
"DC_SCT": "Ex()._debug(on_error=True).check_object('x').has_equal_value()",
11+
}
12+
output = helper.run(data)
13+
assert not output["correct"]
14+
assert "SCT" in output["message"]
15+
16+
517
def build_data(course_id, chapter_id, ex_number, printout=False):
618
url = "https://www.datacamp.com/api/courses/{course_id}/chapters/{chapter_id}/exercises.json".format(
719
course_id=course_id, chapter_id=chapter_id

tests/test_local.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import pytest
22

3-
from protowhat.Test import TestFail as TF
43
from pythonwhat.test_exercise import setup_state
5-
from tests.helper import verify_sct
6-
4+
from tests.helper import verify_sct, in_temp_dir
75

86
modify_sys = (
97
"""
@@ -58,13 +56,14 @@ def test_running_code_isolation_run(sol_code, stu_code):
5856
def test_urlretrieve_in_process(sol_code, stu_code):
5957
# on mac an env var needs to be set:
6058
# OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
61-
chain = setup_state("", "", pec="")
59+
with in_temp_dir():
60+
chain = setup_state("", "", pec="")
6261

63-
chain._state.solution_code = sol_code
64-
chain._state.student_code = stu_code
62+
chain._state.solution_code = sol_code
63+
chain._state.student_code = stu_code
6564

66-
with verify_sct(True):
67-
chain.run()
65+
with verify_sct(True):
66+
chain.run()
6867

6968

7069
@pytest.mark.parametrize(
@@ -82,10 +81,11 @@ def test_urlretrieve_in_process(sol_code, stu_code):
8281
],
8382
)
8483
def test_urlopen_in_process(sol_code, stu_code):
85-
chain = setup_state("", "", pec="")
84+
with in_temp_dir():
85+
chain = setup_state("", "", pec="")
8686

87-
chain._state.solution_code = sol_code
88-
chain._state.student_code = stu_code
87+
chain._state.solution_code = sol_code
88+
chain._state.student_code = stu_code
8989

90-
with verify_sct(True):
91-
chain.run()
90+
with verify_sct(True):
91+
chain.run()

0 commit comments

Comments
 (0)