diff --git a/.python-version b/.python-version index 87ce492..fa8da20 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.5.2 +3.5.8 diff --git a/README.md b/README.md index 4482fd7..2f86a3b 100644 --- a/README.md +++ b/README.md @@ -68,14 +68,6 @@ Ex(state).check_result() ## Running unit tests ```bash -pytest -m "not backend" -``` - -If you also want to run the backend tests, you need to set a `GITHUB_TOKEN` environment variable with access to the (private) `sqlbackend` repository. -After this, you can: - -```bash -make install pytest ``` diff --git a/requirements.txt b/requirements.txt index 3378078..8be4ff7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ # pkg deps -protowhat~=1.13.0 +protowhat~=2.0.1 antlr-plsql~=0.9.1 antlr-tsql~=0.12.6 diff --git a/sqlwhat/checks/__init__.py b/sqlwhat/checks/__init__.py index e9c944d..8b2c1ac 100644 --- a/sqlwhat/checks/__init__.py +++ b/sqlwhat/checks/__init__.py @@ -25,4 +25,4 @@ ) from protowhat.checks.check_logic import fail, multi, check_not, check_or, check_correct from protowhat.checks.check_simple import has_chosen, success_msg, allow_errors -from protowhat.utils import _debug +from protowhat.failure import _debug diff --git a/sqlwhat/checks/check_funcs.py b/sqlwhat/checks/check_funcs.py index 28495ae..8bcb8f0 100644 --- a/sqlwhat/checks/check_funcs.py +++ b/sqlwhat/checks/check_funcs.py @@ -1,4 +1,5 @@ -from protowhat.Feedback import Feedback +from protowhat.Feedback import Feedback, FeedbackComponent +from protowhat.failure import debugger from protowhat.sct_syntax import link_to_state from protowhat.checks.check_simple import allow_errors @@ -72,11 +73,10 @@ def check_row(state, index, missing_msg=None, expand_msg=None): ) if index >= n_stu: - _msg = state.build_message(missing_msg, fmt_kwargs=msg_kwargs) - state.report(_msg) + state.report(missing_msg, msg_kwargs) return state.to_child( - append_message={"msg": expand_msg, "kwargs": msg_kwargs}, + append_message=FeedbackComponent(expand_msg, msg_kwargs), student_result={k: [v[index]] for k, v in stu_res.items()}, solution_result={k: [v[index]] for k, v in sol_res.items()}, ) @@ -130,11 +130,10 @@ def check_column(state, name, missing_msg=None, expand_msg=None): raise BaseException("name %s not in solution column names" % name) if name not in stu_res: - _msg = state.build_message(missing_msg, fmt_kwargs=msg_kwargs) - state.report(_msg) + state.report(missing_msg, msg_kwargs) return state.to_child( - append_message={"msg": expand_msg, "kwargs": msg_kwargs}, + append_message=FeedbackComponent(expand_msg, msg_kwargs), student_result={name: stu_res[name]}, solution_result={name: sol_res[name]}, ) @@ -199,14 +198,13 @@ def check_all_columns(state, allow_extra=True, too_many_cols_msg=None, expand_ms set(state.student_result.keys()) - set(child_stu_result.keys()) ) if not allow_extra and len(cols_not_in_sol) > 0: - _msg = state.build_message( + state.report( "Your query result contains the column `{{col}}` but shouldn't.", - fmt_kwargs={"col": cols_not_in_sol[0]}, + {"col": cols_not_in_sol[0]}, ) - state.report(_msg) return state.to_child( - append_message={"msg": expand_msg, "kwargs": {}}, + append_message=FeedbackComponent(expand_msg), student_result=child_stu_result, solution_result=child_sol_result, ) @@ -298,8 +296,6 @@ def check_query(state, query, error_msg=None, expand_msg=None): # make sure that it didn't generate any errors has_no_error(state) - _msg = state.build_message(error_msg, fmt_kwargs=msg_kwargs) - # sqlbackend makes sure all queries are run in transactions. # Rerun the solution code first, after which we run the provided query with dbconn(state.solution_conn) as conn: @@ -307,7 +303,8 @@ def check_query(state, query, error_msg=None, expand_msg=None): sol_res = runQuery(conn, query) if sol_res is None: - raise ValueError("Solution failed: " + _msg) + with debugger(state): + state.report("Solution failed: " + error_msg) # sqlbackend makes sure all queries are run in transactions. # Rerun the student code first, after wich we run the provided query @@ -316,10 +313,10 @@ def check_query(state, query, error_msg=None, expand_msg=None): stu_res = runQuery(conn, query) if stu_res is None: - state.report(_msg) + state.report(error_msg, msg_kwargs) return state.to_child( - append_message={"msg": expand_msg, "kwargs": msg_kwargs}, + append_message=FeedbackComponent(expand_msg, msg_kwargs), student_result=stu_res, solution_result=sol_res, ) diff --git a/sqlwhat/checks/has_funcs.py b/sqlwhat/checks/has_funcs.py index 31611c1..c0c5d54 100644 --- a/sqlwhat/checks/has_funcs.py +++ b/sqlwhat/checks/has_funcs.py @@ -63,10 +63,7 @@ def has_nrows( n_sol = len(next(iter(state.solution_result.values()))) if n_stu != n_sol: - _msg = state.build_message( - incorrect_msg, fmt_kwargs={"n_stu": n_stu, "n_sol": n_sol} - ) - state.report(_msg) + state.report(incorrect_msg, {"n_stu": n_stu, "n_sol": n_sol}) return state @@ -109,10 +106,7 @@ def has_ncols( n_sol = len(state.solution_result) if n_stu != n_sol: - _msg = state.build_message( - incorrect_msg, fmt_kwargs={"n_stu": n_stu, "n_sol": n_sol} - ) - state.report(_msg) + state.report(incorrect_msg, {"n_stu": n_stu, "n_sol": n_sol}) return state @@ -182,10 +176,7 @@ def has_equal_value(state, ordered=False, ndigits=None, incorrect_msg=None): pass if sol_col_vals != stu_col_vals: - _msg = state.build_message( - incorrect_msg, fmt_kwargs={"col": sol_col_name, "ordered": ordered} - ) - state.report(_msg) + state.report(incorrect_msg, {"col": sol_col_name, "ordered": ordered}) return state diff --git a/sqlwhat/sct_context.py b/sqlwhat/sct_context.py new file mode 100644 index 0000000..7752611 --- /dev/null +++ b/sqlwhat/sct_context.py @@ -0,0 +1,9 @@ +from protowhat.sct_context import create_sct_context, get_checks_dict +from sqlwhat import checks + +SCT_DICT = get_checks_dict(checks) +SCT_CTX = create_sct_context(SCT_DICT) + +# put on module for easy * importing +globals().update(SCT_CTX) +__all__ = list(SCT_CTX.keys()) diff --git a/sqlwhat/sct_syntax.py b/sqlwhat/sct_syntax.py deleted file mode 100644 index 33b51a1..0000000 --- a/sqlwhat/sct_syntax.py +++ /dev/null @@ -1,15 +0,0 @@ -# Wrap SCT checks ------------------------------------------------------------- - -from sqlwhat.State import State -from sqlwhat import checks -from protowhat.sct_syntax import get_checks_dict, create_sct_context - -# used in Chain and F, to know what methods are available -sct_dict = get_checks_dict(checks) -SCT_CTX = create_sct_context(State, sct_dict) - -# used in test_exercise, so that scts without Ex() don't run immediately -globals().update(SCT_CTX) - -# put on module for easy importing -__all__ = list(SCT_CTX.keys()) diff --git a/sqlwhat/test_exercise.py b/sqlwhat/test_exercise.py index 32e9951..a40bd8f 100644 --- a/sqlwhat/test_exercise.py +++ b/sqlwhat/test_exercise.py @@ -1,7 +1,9 @@ -from sqlwhat.State import State -from protowhat.Test import TestFail +from protowhat.failure import Failure, InstructorError from protowhat.Reporter import Reporter -from sqlwhat.sct_syntax import SCT_CTX +from protowhat.sct_context import create_sct_context, get_checks_dict + +from sqlwhat import checks +from sqlwhat.State import State def test_exercise( @@ -21,23 +23,33 @@ def test_exercise( """ """ - state = State( - student_code=student_code, - solution_code=solution_code, - pre_exercise_code=pre_exercise_code, - student_conn=student_conn, - solution_conn=solution_conn, - student_result=student_result, - solution_result=solution_result, - reporter=Reporter(errors=error), - force_diagnose=force_diagnose, - ) - - SCT_CTX["Ex"].root_state = state + reporter = Reporter(errors=error) try: - exec(sct, SCT_CTX) - except TestFail as tf: - return tf.payload + state = State( + student_code=student_code, + solution_code=solution_code, + pre_exercise_code=pre_exercise_code, + student_conn=student_conn, + solution_conn=solution_conn, + student_result=student_result, + solution_result=solution_result, + reporter=reporter, + force_diagnose=force_diagnose, + ) + + # the available SCT methods + sct_dict = get_checks_dict(checks) + + # the available global variables + sct_context = create_sct_context(sct_dict, state) + + exec(sct, sct_context) + + except Failure as e: + if isinstance(e, InstructorError): + # TODO: decide based on context + raise e + return reporter.build_failed_payload(e.feedback) - return state.reporter.build_final_payload() + return reporter.build_final_payload() diff --git a/tests/test_check_funcs.py b/tests/test_check_funcs.py index 2ea24fe..d914709 100644 --- a/tests/test_check_funcs.py +++ b/tests/test_check_funcs.py @@ -1,6 +1,6 @@ import pytest -from protowhat.Test import TestFail as TF +from protowhat.failure import TestFail as TF from tests.helper import ( prepare_state, passes, diff --git a/tests/test_has_funcs.py b/tests/test_has_funcs.py index 68f20a4..8a1d29e 100644 --- a/tests/test_has_funcs.py +++ b/tests/test_has_funcs.py @@ -1,5 +1,5 @@ import pytest -from protowhat.Test import TestFail as TF +from protowhat.failure import TestFail as TF from sqlwhat.checks.has_funcs import sort_rows from tests.helper import ( diff --git a/tests/test_line_info.py b/tests/test_line_info.py index 95e200b..18180f4 100644 --- a/tests/test_line_info.py +++ b/tests/test_line_info.py @@ -1,4 +1,5 @@ -from protowhat.Test import Feedback, TestFail as TF +from protowhat.failure import TestFail as TF +from protowhat.Feedback import Feedback from protowhat.Reporter import Reporter from protowhat.selectors import Dispatcher from sqlwhat.State import State, PARSER_MODULES @@ -35,4 +36,4 @@ def test_line_info(sql_cmd, start, pos): state.report("failure message") except TF as tf: for ii, k in enumerate(pos_names): - assert tf.payload[k] == pos[ii] + assert tf.feedback.get_highlight()[k] == pos[ii] diff --git a/tests/test_messaging.py b/tests/test_messaging.py index d2ebcae..ba6ae79 100644 --- a/tests/test_messaging.py +++ b/tests/test_messaging.py @@ -4,7 +4,7 @@ from protowhat.selectors import Dispatcher from sqlwhat.State import State, PARSER_MODULES from protowhat.Reporter import Reporter -from protowhat.Test import TestFail as TF +from protowhat.failure import TestFail as TF from tests.helper import ( Connection, has_result, diff --git a/tests/test_protowhat_check_funcs.py b/tests/test_protowhat_check_funcs.py index 2ed9220..e02ed5b 100644 --- a/tests/test_protowhat_check_funcs.py +++ b/tests/test_protowhat_check_funcs.py @@ -11,7 +11,7 @@ from protowhat.selectors import Dispatcher from sqlwhat.State import State, PARSER_MODULES from protowhat.Reporter import Reporter -from protowhat.Test import TestFail as TF +from protowhat.failure import TestFail as TF @pytest.fixture diff --git a/tests/test_protowhat_messaging.py b/tests/test_protowhat_messaging.py index bed3ce1..564ba84 100644 --- a/tests/test_protowhat_messaging.py +++ b/tests/test_protowhat_messaging.py @@ -12,7 +12,7 @@ from protowhat.selectors import Dispatcher from sqlwhat.State import State, PARSER_MODULES from protowhat.Reporter import Reporter -from protowhat.Test import TestFail as TF +from protowhat.failure import TestFail as TF check_node = link_to_state(check_node) @@ -148,7 +148,7 @@ def test_get_ast_path(query, path, target_desc): [check_edge, ["left"]], [has_equal_ast, []], ], - "Check the Script. Could not find the first `SELECT` statement.", + "Could not find the first `SELECT` statement.", ), ( "SELECT a FROM c", diff --git a/tests/test_selectors.py b/tests/test_selectors.py index d9277c8..3885f06 100644 --- a/tests/test_selectors.py +++ b/tests/test_selectors.py @@ -2,7 +2,7 @@ from sqlwhat.State import State, PARSER_MODULES import importlib from protowhat.Reporter import Reporter -from protowhat.Test import TestFail as TF +from protowhat.failure import TestFail as TF import pytest @@ -12,8 +12,8 @@ def ast(): @pytest.fixture -def dispatcher(): - return Dispatcher.from_module(ast()) +def dispatcher(ast): + return Dispatcher.from_module(ast) def test_selector_standalone(): diff --git a/tests/test_state.py b/tests/test_state.py index d0d5619..ee16fe3 100644 --- a/tests/test_state.py +++ b/tests/test_state.py @@ -1,9 +1,9 @@ from unittest.mock import MagicMock -from sqlwhat.sct_syntax import Ex +from sqlwhat.sct_context import Ex from sqlwhat.State import State from protowhat.Reporter import Reporter -from protowhat.Test import TestFail as TF +from protowhat.failure import TestFail as TF from tests.helper import Connection import pytest