diff --git a/pythonwhat/.coveragerc b/pythonwhat/.coveragerc
new file mode 100644
index 00000000..c2c30563
--- /dev/null
+++ b/pythonwhat/.coveragerc
@@ -0,0 +1,2 @@
+[run]
+source=pythonwhat
\ No newline at end of file
diff --git a/pythonwhat/.gitignore b/pythonwhat/.gitignore
new file mode 100644
index 00000000..90e761a1
--- /dev/null
+++ b/pythonwhat/.gitignore
@@ -0,0 +1,79 @@
+# Vim swapfiles
+.*.swp
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+.hypothesis/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Ipython Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# Mac stuff
+.DS_Store
+tests/.DS_Store
+
+# pytest
+.pytest_cache/
+
+# datasets
+*.csv
diff --git a/pythonwhat/.travis.yml b/pythonwhat/.travis.yml
new file mode 100644
index 00000000..a9840af6
--- /dev/null
+++ b/pythonwhat/.travis.yml
@@ -0,0 +1,19 @@
+sudo: false
+language: python
+python:
+- '3.5'
+before_install:
+- pip install -r requirements.txt
+- pip install -e .
+script: pytest -m "not compiled" --cov=pythonwhat
+after_success: codecov --token=$CODECOV_TOKEN
+deploy:
+ provider: pypi
+ user: datacamp
+ password:
+ secure: g0lu0u/tDLNxPa0VsmPQhRfPUzA+3EavKljX9goinCbp0o6RWY7LSBEwCqmMcqIlVJ7gL2yMkBC21HsEGkIvkJhoTpWFXVWtbZEUcmyrfviu1HCrOUqsLvy9WOd+h8ZVelsPT4PEuEKtXD8K9qMjRjtiWH9xuoG+LyMPWvO2WGuIxk+z7DNIrPNm48kuC97yKKr2NWXoj9MjhfqPNAizqAiolYdLBwQX/biXQqsGNErSWp1rvojeA27PrtROLFHZO3cni7b3QjmpmaDkAYERfSbfUfom1gcAKQNeM3OfecBVxNsFc8kjhriktQyp9pWGZ44Hn9dmeYVeGfmVOEmIlCppLlQYcRm07QBtOpgN/QqOftAgZBKbd0AXftrxu64G//l9fLXGaW7i9vsqUMc7ttD4FKF5c7Du4+v3i5ouRXnnaMf2bER/FwezPn5tBrAJ2jW96CDYCnUzQzve/NRmjMUSJbNM4YAB5apXq1PyeacLUEPWphZBcurZe0/Gy3ERTDFTMpHC1fS6ciIDnzMLl69HDuCwOfT7Spvj0hjUgDPR650l66rbEG5UHDmymK2r+qsNrmSicahmKOUT8NJ478qP68G1eNemqe2iT6m8dTvMGD5vSqSgPLwrbfX/ECLk4Gx39Xn+Guj1o/TYF8MTMy/CxXIdXXx9P2Z9/4X5Xso=
+ on:
+ tags: true
+ distributions: sdist bdist_wheel
+ repo: datacamp/pythonwhat
+ skip_upload_docs: true
diff --git a/CHANGELOG.md b/pythonwhat/CHANGELOG.md
similarity index 99%
rename from CHANGELOG.md
rename to pythonwhat/CHANGELOG.md
index f06bd833..c754f8e8 100644
--- a/CHANGELOG.md
+++ b/pythonwhat/CHANGELOG.md
@@ -2,11 +2,6 @@
All notable changes to the pythonwhat project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
-## 2.20.0
-
-- Expose `_debug` function
-- Update protowhat
-
## 2.19.0
- `state` is now the first argument to SCT functions (instead of a keyword argument)
diff --git a/LICENSE b/pythonwhat/LICENSE
similarity index 100%
rename from LICENSE
rename to pythonwhat/LICENSE
diff --git a/MANIFEST.in b/pythonwhat/MANIFEST.in
similarity index 100%
rename from MANIFEST.in
rename to pythonwhat/MANIFEST.in
diff --git a/Makefile b/pythonwhat/Makefile
similarity index 100%
rename from Makefile
rename to pythonwhat/Makefile
diff --git a/README.md b/pythonwhat/README.md
similarity index 100%
rename from README.md
rename to pythonwhat/README.md
diff --git a/docs/Makefile b/pythonwhat/docs/Makefile
similarity index 100%
rename from docs/Makefile
rename to pythonwhat/docs/Makefile
diff --git a/docs/articles/checking_compound_statements.rst b/pythonwhat/docs/articles/checking_compound_statements.rst
similarity index 100%
rename from docs/articles/checking_compound_statements.rst
rename to pythonwhat/docs/articles/checking_compound_statements.rst
diff --git a/docs/articles/checking_function_calls.rst b/pythonwhat/docs/articles/checking_function_calls.rst
similarity index 100%
rename from docs/articles/checking_function_calls.rst
rename to pythonwhat/docs/articles/checking_function_calls.rst
diff --git a/docs/articles/checking_through_string_matching.rst b/pythonwhat/docs/articles/checking_through_string_matching.rst
similarity index 100%
rename from docs/articles/checking_through_string_matching.rst
rename to pythonwhat/docs/articles/checking_through_string_matching.rst
diff --git a/docs/articles/electives.rst b/pythonwhat/docs/articles/electives.rst
similarity index 100%
rename from docs/articles/electives.rst
rename to pythonwhat/docs/articles/electives.rst
diff --git a/docs/articles/expression_tests.rst b/pythonwhat/docs/articles/expression_tests.rst
similarity index 100%
rename from docs/articles/expression_tests.rst
rename to pythonwhat/docs/articles/expression_tests.rst
diff --git a/docs/articles/make_your_sct_robust.rst b/pythonwhat/docs/articles/make_your_sct_robust.rst
similarity index 100%
rename from docs/articles/make_your_sct_robust.rst
rename to pythonwhat/docs/articles/make_your_sct_robust.rst
diff --git a/docs/articles/processes.rst b/pythonwhat/docs/articles/processes.rst
similarity index 100%
rename from docs/articles/processes.rst
rename to pythonwhat/docs/articles/processes.rst
diff --git a/docs/articles/single_process_exercise.rst b/pythonwhat/docs/articles/single_process_exercise.rst
similarity index 100%
rename from docs/articles/single_process_exercise.rst
rename to pythonwhat/docs/articles/single_process_exercise.rst
diff --git a/docs/articles/test_to_check.rst b/pythonwhat/docs/articles/test_to_check.rst
similarity index 100%
rename from docs/articles/test_to_check.rst
rename to pythonwhat/docs/articles/test_to_check.rst
diff --git a/docs/articles/tutorial.rst b/pythonwhat/docs/articles/tutorial.rst
similarity index 100%
rename from docs/articles/tutorial.rst
rename to pythonwhat/docs/articles/tutorial.rst
diff --git a/docs/conf.py b/pythonwhat/docs/conf.py
similarity index 97%
rename from docs/conf.py
rename to pythonwhat/docs/conf.py
index 46ddd4cd..3b6117ef 100644
--- a/docs/conf.py
+++ b/pythonwhat/docs/conf.py
@@ -49,12 +49,7 @@
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
-extensions = [
- "sphinx.ext.autodoc",
- "sphinx.ext.napoleon",
- "sphinxcontrib.jinja",
- "sphinxprettysearchresults",
-]
+extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon", "sphinxcontrib.jinja", 'sphinxprettysearchresults']
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
diff --git a/docs/data_to_doc.py b/pythonwhat/docs/data_to_doc.py
similarity index 100%
rename from docs/data_to_doc.py
rename to pythonwhat/docs/data_to_doc.py
diff --git a/docs/glossary.rst b/pythonwhat/docs/glossary.rst
similarity index 100%
rename from docs/glossary.rst
rename to pythonwhat/docs/glossary.rst
diff --git a/docs/index.rst b/pythonwhat/docs/index.rst
similarity index 100%
rename from docs/index.rst
rename to pythonwhat/docs/index.rst
diff --git a/docs/old/test_object_accessed.md b/pythonwhat/docs/old/test_object_accessed.md
similarity index 100%
rename from docs/old/test_object_accessed.md
rename to pythonwhat/docs/old/test_object_accessed.md
diff --git a/docs/reference.rst b/pythonwhat/docs/reference.rst
similarity index 100%
rename from docs/reference.rst
rename to pythonwhat/docs/reference.rst
diff --git a/docs/tests.rst b/pythonwhat/docs/tests.rst
similarity index 100%
rename from docs/tests.rst
rename to pythonwhat/docs/tests.rst
diff --git a/pytest.ini b/pythonwhat/pytest.ini
similarity index 100%
rename from pytest.ini
rename to pythonwhat/pytest.ini
diff --git a/pythonwhat/pythonwhat/Feedback.py b/pythonwhat/pythonwhat/Feedback.py
new file mode 100644
index 00000000..00fe9985
--- /dev/null
+++ b/pythonwhat/pythonwhat/Feedback.py
@@ -0,0 +1,21 @@
+from protowhat.Feedback import Feedback as ProtoFeedback
+
+
+class Feedback(ProtoFeedback):
+ def _line_info(self):
+ if hasattr(self.highlight, "first_token") and hasattr(
+ self.highlight, "last_token"
+ ):
+ return {
+ "line_start": self.highlight.first_token.start[0],
+ "column_start": self.highlight.first_token.start[1],
+ "line_end": self.highlight.last_token.end[0],
+ "column_end": self.highlight.last_token.end[1],
+ }
+
+ def get_formatted_line_info(self):
+ formatted_info = self.get_line_info()
+ for k in ["column_start"]:
+ if k in formatted_info:
+ formatted_info[k] += 1
+ return formatted_info
diff --git a/pythonwhat/State.py b/pythonwhat/pythonwhat/State.py
similarity index 92%
rename from pythonwhat/State.py
rename to pythonwhat/pythonwhat/State.py
index 90320efe..7e6dc830 100644
--- a/pythonwhat/State.py
+++ b/pythonwhat/pythonwhat/State.py
@@ -8,6 +8,7 @@
from protowhat.State import State as ProtoState
from protowhat.selectors import DispatcherInterface
from protowhat.Feedback import InstructorError
+from pythonwhat.Feedback import Feedback
from pythonwhat import signatures
from pythonwhat.converters import get_manual_converters
from collections.abc import Mapping
@@ -62,7 +63,7 @@ def __init__(
highlight=None,
highlighting_disabled=None,
messages=None,
- creator=None,
+ parent_state=None,
student_ast=None,
solution_ast=None,
student_ast_tokens=None,
@@ -78,12 +79,10 @@ def __init__(
self.params = list()
for k, v in args.items():
- if k != "self":
+ if k not in ["self", "args"]:
self.params.append(k)
setattr(self, k, v)
- self.messages = messages if messages else []
-
self.ast_dispatcher = self.get_dispatcher()
# parse code if didn't happen yet
@@ -92,9 +91,11 @@ def __init__(
if solution_ast is None:
self.solution_ast = self.parse(solution_code, test=False)
- if highlight is None: # todo: check parent_state? (move check to reporting?)
+ if highlight is None and parent_state:
self.highlight = self.student_ast
+ self.messages = messages if messages else []
+
self.converters = get_manual_converters() # accessed only from root state
self.manual_sigs = None
@@ -122,6 +123,7 @@ def to_child(self, append_message="", node_name="", **kwargs):
append_message = {"msg": append_message, "kwargs": {}}
kwargs["messages"] = [*self.messages, append_message]
+ kwargs["parent_state"] = self
for kwarg in ["solution_context", "student_context"]:
if kwarg in kwargs and not kwargs[kwarg]:
@@ -202,21 +204,26 @@ def parse_external(self, code):
e.filename = "script.py"
# no line info for now
self.report(
- "Your code could not be parsed due to an error in the indentation:
`%s.`"
- % str(e)
+ Feedback(
+ "Your code could not be parsed due to an error in the indentation:
`%s.`"
+ % str(e)
+ )
)
except SyntaxError as e:
e.filename = "script.py"
# no line info for now
self.report(
- "Your code can not be executed due to a syntax error:
`%s.`" % str(e)
+ Feedback(
+ "Your code can not be executed due to a syntax error:
`%s.`"
+ % str(e)
+ )
)
# Can happen, can't catch this earlier because we can't differentiate between
# TypeError in parsing or TypeError within code (at runtime).
except:
- self.report("Something went wrong while parsing your code.")
+ self.report(Feedback("Something went wrong while parsing your code."))
return res
@@ -257,12 +264,14 @@ def __init__(self, context_code=""):
self._parser_cache = dict()
context_ast = getattr(self._context_cache, context_code, None)
if context_ast is None:
- context_ast = self._context_cache[context_code] = self.parse(context_code)[
- 1
- ]
- self.context_mappings = self._getx(FunctionParser, "mappings", context_ast)
-
- def find(self, name, node, *args, **kwargs):
+ context_ast = self._context_cache[context_code] = self.parse(
+ context_code
+ )[1]
+ self.context_mappings = self._getx(
+ FunctionParser, "mappings", context_ast
+ )
+
+ def __call__(self, name, node, *args, **kwargs):
return getattr(self, name)(node)
def parse(self, code):
diff --git a/pythonwhat/Test.py b/pythonwhat/pythonwhat/Test.py
similarity index 96%
rename from pythonwhat/Test.py
rename to pythonwhat/pythonwhat/Test.py
index 50c20f5c..9f4596b7 100644
--- a/pythonwhat/Test.py
+++ b/pythonwhat/pythonwhat/Test.py
@@ -22,7 +22,7 @@ def __init__(self, name, process, feedback):
self.name = name
self.process = process
- def test(self):
+ def specific_test(self):
self.result = isDefinedInProcess(self.name, self.process)
@@ -42,7 +42,7 @@ def __init__(self, name, collection, feedback):
self.name = name
self.collection = collection
- def test(self):
+ def specific_test(self):
self.result = self.name in self.collection
@@ -53,7 +53,7 @@ def __init__(self, name, key, process, feedback):
self.key = key
self.process = process
- def test(self):
+ def specific_test(self):
self.result = isDefinedCollInProcess(self.name, self.key, self.process)
@@ -67,7 +67,7 @@ def __init__(self, name, klass, process, feedback):
self.klass = klass
self.process = process
- def test(self):
+ def specific_test(self):
self.result = isInstanceInProcess(self.name, self.klass, self.process)
@@ -92,11 +92,11 @@ def __init__(self, obj1, obj2, feedback, func=None):
self.obj2 = obj2
self.func = func if func is not None else is_equal
- def test(self):
+ def specific_test(self):
"""
Perform the actual test. result is set to False if the objects differ, True otherwise.
"""
- self.result = np.array(self.func(self.obj1, self.obj2)).all()
+ self.result = self.func(self.obj1, self.obj2)
# Helpers for testing equality
@@ -177,7 +177,7 @@ def __init__(self, string, search_string, pattern, feedback):
self.search_string = search_string
self.pattern = pattern
- def test(self):
+ def specific_test(self):
"""
Perform the actual test. result will be True if string is found (whether or not with a pattern),
False otherwise.
diff --git a/pythonwhat/__init__.py b/pythonwhat/pythonwhat/__init__.py
similarity index 70%
rename from pythonwhat/__init__.py
rename to pythonwhat/pythonwhat/__init__.py
index 7a4c712d..0ea5ba40 100644
--- a/pythonwhat/__init__.py
+++ b/pythonwhat/pythonwhat/__init__.py
@@ -1,3 +1,3 @@
-__version__ = "2.20.0"
+__version__ = "2.19.0"
from .test_exercise import test_exercise, allow_errors
diff --git a/pythonwhat/checks/__init__.py b/pythonwhat/pythonwhat/checks/__init__.py
similarity index 100%
rename from pythonwhat/checks/__init__.py
rename to pythonwhat/pythonwhat/checks/__init__.py
diff --git a/pythonwhat/checks/check_funcs.py b/pythonwhat/pythonwhat/checks/check_funcs.py
similarity index 91%
rename from pythonwhat/checks/check_funcs.py
rename to pythonwhat/pythonwhat/checks/check_funcs.py
index 9d5cc02e..1d6abb71 100644
--- a/pythonwhat/checks/check_funcs.py
+++ b/pythonwhat/pythonwhat/checks/check_funcs.py
@@ -1,6 +1,8 @@
from pythonwhat.checks.check_logic import multi
from pythonwhat.checks.has_funcs import has_part
+from protowhat.Test import Test
from protowhat.Feedback import InstructorError
+from pythonwhat.Feedback import Feedback
from pythonwhat.tasks import setUpNewEnvInProcess, breakDownNewEnvInProcess
from pythonwhat.utils import get_ord
from pythonwhat.utils_ast import assert_ast
@@ -12,6 +14,12 @@ def render(template, kwargs):
return Template(template).render(**kwargs)
+class StubState:
+ def __init__(self, highlight, highlighting_disabled):
+ self.highlight = highlight
+ self.highlighting_disabled = highlighting_disabled
+
+
def part_to_child(stu_part, sol_part, append_message, state, node_name=None):
# stu_part and sol_part will be accessible on all templates
append_message["kwargs"].update({"stu_part": stu_part, "sol_part": sol_part})
@@ -115,8 +123,8 @@ def check_node(
if expand_msg is None:
expand_msg = "Check the {{typestr}}. "
- stu_out = state.ast_dispatcher.find(name, state.student_ast)
- sol_out = state.ast_dispatcher.find(name, state.solution_ast)
+ stu_out = state.ast_dispatcher(name, state.student_ast)
+ sol_out = state.ast_dispatcher(name, state.solution_ast)
# check if there are enough nodes for index
fmt_kwargs = {
@@ -131,7 +139,7 @@ def check_node(
stu_out[index]
except (KeyError, IndexError): # TODO comment errors
_msg = state.build_message(missing_msg, fmt_kwargs)
- state.report(_msg)
+ state.report(Feedback(_msg, state))
# get node at index
stu_part = stu_out[index]
@@ -159,14 +167,20 @@ def with_context(state, *args, child=None):
process=state.student_process, context=state.student_parts["with_items"]
)
if isinstance(student_res, AttributeError):
- child.report(
- "In your `with` statement, you're not using a correct context manager."
+ state.report(
+ Feedback(
+ "In your `with` statement, you're not using a correct context manager.",
+ child.highlight, # TODO
+ )
)
if isinstance(student_res, (AssertionError, ValueError, TypeError)):
- child.report(
- "In your `with` statement, the number of values in your context manager "
- "doesn't correspond to the number of variables you're trying to assign it to."
+ state.report(
+ Feedback(
+ "In your `with` statement, the number of values in your context manager "
+ "doesn't correspond to the number of variables you're trying to assign it to.",
+ child.highlight,
+ )
)
# run subtests
@@ -186,8 +200,11 @@ def with_context(state, *args, child=None):
close_student_context = breakDownNewEnvInProcess(process=state.student_process)
if isinstance(close_student_context, Exception):
state.report(
- "Your `with` statement can not be closed off correctly, you're "
- "not using the context manager correctly."
+ Feedback(
+ "Your `with` statement can not be closed off correctly, you're "
+ + "not using the context manager correctly.",
+ state,
+ )
)
return state
diff --git a/pythonwhat/checks/check_function.py b/pythonwhat/pythonwhat/checks/check_function.py
similarity index 91%
rename from pythonwhat/checks/check_function.py
rename to pythonwhat/pythonwhat/checks/check_function.py
index 32ff5b8e..2ac2d99a 100644
--- a/pythonwhat/checks/check_function.py
+++ b/pythonwhat/pythonwhat/checks/check_function.py
@@ -1,7 +1,9 @@
-from pythonwhat.checks.check_funcs import part_to_child
+from pythonwhat.checks.check_funcs import part_to_child, StubState
from pythonwhat.tasks import getSignatureInProcess
from pythonwhat.utils import get_ord, get_times
+from protowhat.Test import Test
from protowhat.Feedback import InstructorError
+from pythonwhat.Feedback import Feedback
from pythonwhat.parsing import IndexedDict
from functools import partial
@@ -47,7 +49,7 @@ def check_function(
"""Check whether a particular function is called.
``check_function()`` is typically followed by:
-
+
- ``check_args()`` to check whether the arguments were specified.
In turn, ``check_args()`` can be followed by ``has_equal_value()`` or ``has_equal_ast()``
to assert that the arguments were correctly specified.
@@ -97,10 +99,10 @@ def check_function(
if params_not_matched_msg is None:
params_not_matched_msg = SIG_ISSUE_MSG
- stu_out = state.ast_dispatcher.find("function_calls", state.student_ast)
- sol_out = state.ast_dispatcher.find("function_calls", state.solution_ast)
+ stu_out = state.ast_dispatcher("function_calls", state.student_ast)
+ sol_out = state.ast_dispatcher("function_calls", state.solution_ast)
- student_mappings = state.ast_dispatcher.find("mappings", state.student_ast)
+ student_mappings = state.ast_dispatcher("mappings", state.student_ast)
fmt_kwargs = {
"times": get_times(index + 1),
@@ -129,7 +131,7 @@ def check_function(
stu_parts = {**stu_out[name][index]}
except (KeyError, IndexError):
_msg = state.build_message(missing_msg, fmt_kwargs, append=append_missing)
- state.report(_msg)
+ state.report(Feedback(_msg, state))
# Signatures -----
if signature:
@@ -161,7 +163,11 @@ def check_function(
_msg = state.build_message(
params_not_matched_msg, fmt_kwargs, append=append_params_not_matched
)
- state.to_child(highlight=stu_parts["node"]).report(_msg)
+ state.report(
+ Feedback(
+ _msg, StubState(stu_parts["node"], state.highlighting_disabled)
+ )
+ )
# three types of parts: pos_args, keywords, args (e.g. these are bound to sig)
append_message = {"msg": expand_msg, "kwargs": fmt_kwargs}
diff --git a/pythonwhat/checks/check_has_context.py b/pythonwhat/pythonwhat/checks/check_has_context.py
similarity index 97%
rename from pythonwhat/checks/check_has_context.py
rename to pythonwhat/pythonwhat/checks/check_has_context.py
index 05ed3676..14bf5361 100644
--- a/pythonwhat/checks/check_has_context.py
+++ b/pythonwhat/pythonwhat/checks/check_has_context.py
@@ -1,5 +1,6 @@
from pythonwhat.Test import EqualTest
-from protowhat.Feedback import Feedback, InstructorError
+from protowhat.Feedback import InstructorError
+from pythonwhat.Feedback import Feedback
from pythonwhat.State import State
from functools import singledispatch
from pythonwhat.checks.check_funcs import check_part_index
diff --git a/pythonwhat/checks/check_logic.py b/pythonwhat/pythonwhat/checks/check_logic.py
similarity index 95%
rename from pythonwhat/checks/check_logic.py
rename to pythonwhat/pythonwhat/checks/check_logic.py
index 26ada48a..587ac73e 100644
--- a/pythonwhat/checks/check_logic.py
+++ b/pythonwhat/pythonwhat/checks/check_logic.py
@@ -1,18 +1,9 @@
-from protowhat.checks.check_logic import (
- multi,
- check_not,
- check_or,
- check_correct,
- disable_highlighting,
- fail,
-)
+from protowhat.checks.check_logic import multi, check_not, check_or, check_correct, disable_highlighting, fail
from protowhat.Feedback import InstructorError
import ast
-multi.__doc__ = (
- str(multi.__doc__)
- + """
+multi.__doc__ = str(multi.__doc__) + """
:Example:
Suppose we want to verify the following function call: ::
@@ -27,12 +18,9 @@
check_args('ndigits').has_equal_value()
)
"""
-)
-check_not.__doc__ = (
- str(check_not.__doc__)
- + """
+check_not.__doc__ = str(check_not.__doc__) + """
:Example:
The SCT fails with feedback for a specific incorrect value, defined using an override: ::
@@ -64,12 +52,9 @@
- This function can be considered a direct counterpart of multi.
"""
-)
-check_or.__doc__ = (
- str(check_or.__doc__)
- + """
+check_or.__doc__ = str(check_or.__doc__) + """
:Example:
The SCT below tests that the student typed either 'mean' or 'median': ::
@@ -83,12 +68,9 @@
the first SCT, will be presented to the student.
"""
-)
-check_correct.__doc__ = (
- str(check_correct.__doc__)
- + """
+check_correct.__doc__ = str(check_correct.__doc__) + """
:Example:
The SCT below tests whether an object is correct. Only if the object is not correct, will
@@ -100,15 +82,12 @@
)
"""
-)
# utility functions -----------------------------------------------------------
-fail.__doc__ = (
- str(fail.__doc__)
- + """
+fail.__doc__ = str(fail.__doc__) + """
:Example:
As a trivial SCT example, ::
@@ -117,7 +96,6 @@
This can also be helpful for debugging SCTs, as it can be used to stop testing as a given point.
"""
-)
def override(state, solution):
@@ -293,9 +271,7 @@ def set_env(state, **kwargs):
)
-disable_highlighting.__doc__ = (
- str(disable_highlighting.__doc__)
- + """
+disable_highlighting.__doc__ = str(disable_highlighting.__doc__) + """
:Examples:
SCT that will mark the 'number' portion if it is incorrect::
@@ -308,4 +284,3 @@ def set_env(state, **kwargs):
Ex().check_function('round').disable_highlighting().check_args(0).has_equal_ast()
Ex().check_function('round').check_args(0).disable_highlighting().has_equal_ast()
"""
-)
diff --git a/pythonwhat/checks/check_object.py b/pythonwhat/pythonwhat/checks/check_object.py
similarity index 96%
rename from pythonwhat/checks/check_object.py
rename to pythonwhat/pythonwhat/checks/check_object.py
index a3890d02..bb6bfdd2 100644
--- a/pythonwhat/checks/check_object.py
+++ b/pythonwhat/pythonwhat/checks/check_object.py
@@ -4,7 +4,8 @@
InstanceProcessTest,
DefinedCollProcessTest,
)
-from protowhat.Feedback import Feedback, InstructorError
+from protowhat.Feedback import InstructorError
+from pythonwhat.Feedback import Feedback
from pythonwhat.tasks import (
isDefinedInProcess,
isInstanceInProcess,
@@ -16,7 +17,9 @@
import ast
-def check_object(state, index, missing_msg=None, expand_msg=None, typestr="variable"):
+def check_object(
+ state, index, missing_msg=None, expand_msg=None, typestr="variable"
+):
"""Check object existence (and equality)
Check whether an object is defined in the student's process, and zoom in on its value in both
@@ -32,7 +35,7 @@ def check_object(state, index, missing_msg=None, expand_msg=None, typestr="varia
expand_msg (str): If specified, this overrides any messages that are prepended by previous SCT chains.
:Example:
-
+
Suppose you want the student to create a variable ``x``, equal to 15: ::
x = 15
@@ -43,9 +46,9 @@ def check_object(state, index, missing_msg=None, expand_msg=None, typestr="varia
- ``check_object()`` will check if the variable ``x`` is defined in the student process.
- ``has_equal_value()`` will check whether the value of ``x`` in the solution process is the same as in the student process.
-
+
Note that ``has_equal_value()`` only looks at **end result** of a variable in the student process.
- In the example, how the object ``x`` came about in the student's submission, does not matter.
+ In the example, how the object ``x`` came about in the student's submission, does not matter.
This means that all of the following submission will also pass the above SCT: ::
x = 15
@@ -90,10 +93,10 @@ def check_object(state, index, missing_msg=None, expand_msg=None, typestr="varia
)
)
)
-
+
- ``check_correct()`` is used to robustly check whether ``my_list`` was built correctly.
- If ``my_list`` is not correct, **both** the initialization and the population code are checked.
-
+
:Example:
Because checking object correctness incorrectly is such a common misconception, we're adding another example: ::
@@ -101,7 +104,7 @@ def check_object(state, index, missing_msg=None, expand_msg=None, typestr="varia
import pandas as pd
df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})
df['c'] = [7, 8, 9]
-
+
The following SCT would be **wrong**, as it does not factor in the possibility that the 'add column ``c``' step could've been wrong: ::
Ex().check_correct(
@@ -147,7 +150,7 @@ def __init__(self, n):
# submissions that will pass this sct
x = Number(1)
x = Number(2 - 1)
-
+
The basic SCT like in the previous example will notwork here.
Notice how we used the ``expr_code`` argument to _override_ which value `has_equal_value()` is checking.
Instead of checking whether `x` corresponds between student and solution process, it's now executing the expression ``x.n``
@@ -179,12 +182,8 @@ def __init__(self, n):
# create child state, using either parser output, or create part from name
fallback = lambda: ObjectAssignmentParser.get_part(index)
- stu_part = state.ast_dispatcher.find("object_assignments", state.student_ast).get(
- index, fallback()
- )
- sol_part = state.ast_dispatcher.find("object_assignments", state.solution_ast).get(
- index, fallback()
- )
+ stu_part = state.ast_dispatcher("object_assignments", state.student_ast).get(index, fallback())
+ sol_part = state.ast_dispatcher("object_assignments", state.solution_ast).get(index, fallback())
# test object exists
_msg = state.build_message(missing_msg, append_message["kwargs"])
@@ -244,9 +243,11 @@ def is_instance(state, inst, not_instance_msg=None):
return state
-def check_df(state, index, missing_msg=None, not_instance_msg=None, expand_msg=None):
+def check_df(
+ state, index, missing_msg=None, not_instance_msg=None, expand_msg=None
+):
"""Check whether a DataFrame was defined and it is the right type
-
+
``check_df()`` is a combo of ``check_object()`` and ``is_instance()`` that checks whether the specified object exists
and whether the specified object is pandas DataFrame.
@@ -277,9 +278,9 @@ def check_df(state, index, missing_msg=None, not_instance_msg=None, expand_msg=N
- ``check_df()`` checks if ``my_df`` exists (``check_object()`` behind the scenes) and is a DataFrame (``is_instance()``)
- ``check_keys("a")`` zooms in on the column ``a`` of the data frame, and ``has_equal_value()`` checks if the columns correspond between student and solution process.
- ``check_keys("b")`` zooms in on hte column ``b`` of the data frame, but there's no 'equality checking' happening
-
+
The following submissions would pass the SCT above: ::
-
+
my_df = pd.DataFrame({"a": [1, 1 + 1, 3], "b": ["a", "l", "l"]})
my_df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
diff --git a/pythonwhat/checks/check_wrappers.py b/pythonwhat/pythonwhat/checks/check_wrappers.py
similarity index 95%
rename from pythonwhat/checks/check_wrappers.py
rename to pythonwhat/pythonwhat/checks/check_wrappers.py
index 6d4c0ca5..454f98cd 100644
--- a/pythonwhat/checks/check_wrappers.py
+++ b/pythonwhat/pythonwhat/checks/check_wrappers.py
@@ -1,12 +1,9 @@
-from protowhat.utils import _debug
-from protowhat.checks.check_files import check_file, has_dir
from pythonwhat.checks.check_funcs import check_part, check_part_index, check_node
from pythonwhat.checks.has_funcs import has_equal_part
from pythonwhat.checks import check_object, check_logic, check_funcs, has_funcs
from pythonwhat.checks.check_function import check_function
from pythonwhat.checks.check_has_context import has_context
-from inspect import signature, Parameter
from functools import partial, wraps
from jinja2 import Template
@@ -651,6 +648,25 @@ def __init__(self, i):
# make has_equal_part wrappers
+# todo: check @wraps implementation
+
+# def partial_with_state(func, *partial_args, **partial_kwargs):
+# @wraps(func)
+# def state_partial(state, *args, **kwargs):
+# func(state, *partial_args, *args, **{**partial_kwargs, **kwargs})
+# return state_partial
+
+
+# def right_args_partial(func, *last_args, **partial_kwargs):
+# kwargs_partial = partial(func, **partial_kwargs)
+#
+# @wraps(func)
+# def full_partial(*first_args, **kwargs):
+# kwargs_partial(*first_args, *last_args, **kwargs)
+#
+# return full_partial
+
+
def partial_with_offset(offset=1):
def bound_partial_with_offset(func, *partial_args, **partial_kwargs):
kwargs_partial = partial(func, **partial_kwargs)
@@ -660,35 +676,7 @@ def full_partial(*args, **kwargs):
full_args = args[:offset] + partial_args + args[offset:]
return kwargs_partial(*full_args, **kwargs)
- # set correct signature of returned partial
- # todo: pass arguments as keywords to partial, instead of this decorator?
- # (where args are always the same)
- func_sig = signature(full_partial)
- parameter_names = tuple(func_sig.parameters)
-
- partialed_positional_indices = []
- for kwarg in partial_kwargs:
- param = func_sig.parameters[kwarg]
- if param.default is param.empty:
- partialed_positional_indices.append(parameter_names.index(kwarg))
-
- partial_params = list(func_sig.parameters.values())
- for index in sorted(partialed_positional_indices, reverse=True):
- # appending isn't needed for functionality, but more similar to partial
- # and it shows that these arguments can still be updated as kwargs
- partial_params.append(
- partial_params[index].replace(
- kind=Parameter.KEYWORD_ONLY,
- default=partial_kwargs[partial_params[index].name],
- )
- )
- del partial_params[index]
- del partial_params[offset : offset + len(partial_args)]
-
- full_partial.__signature__ = func_sig.replace(parameters=partial_params)
-
return full_partial
-
return bound_partial_with_offset
@@ -770,8 +758,5 @@ def rename_function(func, name):
scts["has_context"] = has_context
scts["check_function"] = check_function
-scts["check_file"] = check_file
-scts["has_dir"] = has_dir
-scts["_debug"] = _debug
locals().update(scts)
diff --git a/pythonwhat/checks/has_funcs.py b/pythonwhat/pythonwhat/checks/has_funcs.py
similarity index 97%
rename from pythonwhat/checks/has_funcs.py
rename to pythonwhat/pythonwhat/checks/has_funcs.py
index 48136b87..40d41086 100644
--- a/pythonwhat/checks/has_funcs.py
+++ b/pythonwhat/pythonwhat/checks/has_funcs.py
@@ -10,7 +10,8 @@
)
from protowhat.Test import Test
from pythonwhat.Test import EqualTest, DefinedCollTest
-from protowhat.Feedback import Feedback, InstructorError
+from protowhat.Feedback import InstructorError
+from pythonwhat.Feedback import Feedback
from pythonwhat import utils
from functools import partial
import re
@@ -52,7 +53,7 @@ def verify(part, index):
try:
verify(state.student_parts[name], index)
except (KeyError, IndexError):
- state.report(_msg)
+ state.report(Feedback(_msg, state))
return state
@@ -100,7 +101,7 @@ def shout(word):
if d["stu_len"] != d["sol_len"]:
_msg = state.build_message(unequal_msg, d)
- state.report(_msg)
+ state.report(Feedback(_msg, state))
return state
@@ -194,7 +195,7 @@ def parse_tree(tree):
if exact and not code:
state.do_test(EqualTest(stu_rep, sol_rep, Feedback(_msg, state)))
elif not sol_rep in stu_rep:
- state.report(_msg)
+ state.report(Feedback(_msg, state))
return state
@@ -238,7 +239,7 @@ def parse_tree(tree):
{0} of an object after executing the body of e.g. a ``for`` loop.
copy (bool): whether to try to deep copy objects in the environment, such as lists, that could
accidentally be mutated. Disable to speed up SCTs. Disabling may lead to cryptic mutation issues.
- func (function): custom binary function of form f(stu_result, sol_result), for equality testing.
+ func: custom binary function of form f(stu_result, sol_result), for equality testing.
override: If specified, this avoids the execution of the targeted code in the solution process. Instead, it
will compare the {0} of the expression in the student process with the value specified in ``override``.
Typically used in a ``SingleProcessExercise`` or if you want to allow for different solutions other than
@@ -346,12 +347,13 @@ def has_expr(
if (test == "error") ^ isinstance(eval_stu, Exception):
fmt_kwargs["stu_str"] = str_stu
_msg = state.build_message(error_msg, fmt_kwargs, append=append)
- state.report(_msg)
+ feedback = Feedback(_msg, state)
+ state.report(feedback)
# name is undefined after running expression
if isinstance(eval_stu, UndefinedValue):
_msg = state.build_message(undefined_msg, fmt_kwargs, append=append)
- state.report(_msg)
+ state.report(Feedback(_msg, state))
# test equality of results
_msg = state.build_message(incorrect_msg, fmt_kwargs, append=append)
@@ -511,8 +513,8 @@ def has_import(
import matplotlib.pyplot as pltttt
"""
- student_imports = state.ast_dispatcher.find("imports", state.student_ast)
- solution_imports = state.ast_dispatcher.find("imports", state.solution_ast)
+ student_imports = state.ast_dispatcher("imports", state.student_ast)
+ solution_imports = state.ast_dispatcher("imports", state.solution_ast)
if name not in solution_imports:
raise InstructorError(
@@ -660,7 +662,7 @@ def has_printout(
)
try:
- sol_call_ast = state.ast_dispatcher.find("function_calls", state.solution_ast)[
+ sol_call_ast = state.ast_dispatcher("function_calls", state.solution_ast)[
"print"
][index]["node"]
except (KeyError, IndexError):
@@ -753,7 +755,7 @@ def has_no_error(
_msg = state.build_message(
incorrect_msg, {"error": str(state.reporter.errors[0])}
)
- state.report(_msg)
+ state.report(Feedback(_msg, state))
return state
diff --git a/pythonwhat/converters.py b/pythonwhat/pythonwhat/converters.py
similarity index 100%
rename from pythonwhat/converters.py
rename to pythonwhat/pythonwhat/converters.py
diff --git a/pythonwhat/local.py b/pythonwhat/pythonwhat/local.py
similarity index 96%
rename from pythonwhat/local.py
rename to pythonwhat/pythonwhat/local.py
index 18f5c669..de1528c8 100644
--- a/pythonwhat/local.py
+++ b/pythonwhat/pythonwhat/local.py
@@ -3,7 +3,7 @@
from pythonwhat.sct_syntax import Ex
from pythonwhat.State import State
-from pythonwhat.reporter import Reporter
+from protowhat.Reporter import Reporter
from contextlib import redirect_stdout
@@ -38,7 +38,7 @@ def setup_state(stu_code="", sol_code="", pec="", pid=None):
student_process=stu_process,
solution_process=sol_process,
raw_student_output=raw_stu_output,
- reporter=Reporter(),
+ reporter=Reporter()
)
State.root_state = state
diff --git a/pythonwhat/parsing.py b/pythonwhat/pythonwhat/parsing.py
similarity index 100%
rename from pythonwhat/parsing.py
rename to pythonwhat/pythonwhat/parsing.py
diff --git a/pythonwhat/probe.py b/pythonwhat/pythonwhat/probe.py
similarity index 95%
rename from pythonwhat/probe.py
rename to pythonwhat/pythonwhat/probe.py
index 9a041e4d..57f33392 100644
--- a/pythonwhat/probe.py
+++ b/pythonwhat/pythonwhat/probe.py
@@ -61,8 +61,7 @@ def str_branch(cls, node, str_func=lambda s: str(s)):
# dict(getattr(s.data.get("bound_args", {}), 'arguments', {}))
f = node.data.get("func")
this_node = (
- " " * node.depth
- + "("
+ " " * node.depth + "("
+ getattr(f, "__name__", node.name)
+ str_func(node)
+ ")\n"
@@ -113,9 +112,7 @@ def __call__(self, state=None):
return ba.arguments["state"]
def __str__(self):
- return pp.pformat(
- dict(getattr(self.data.get("bound_args", {}), "arguments", {}))
- )
+ return pp.pformat(dict(getattr(self.data.get("bound_args", {}), 'arguments', {})))
def __iter__(self):
for c in self.child_list:
@@ -190,7 +187,7 @@ def __call__(self, *args, **kwargs):
"""
if (len(args) > 0 and not isinstance(args[0], State)) or len(args) == 0:
# no state placeholder if a state is passed
- args = ["state_placeholder"] + list(args)
+ args = ['state_placeholder'] + list(args)
bound_args = inspect.signature(self.f).bind(*args, **kwargs)
@@ -203,9 +200,7 @@ def __call__(self, *args, **kwargs):
arguments = bound_args.arguments
for subtest in self.sub_tests: # TODO: auto sub test detection
if subtest in arguments and arguments[subtest]:
- self.build_sub_test_nodes(
- arguments[subtest], self.tree, this_node, subtest
- )
+ self.build_sub_test_nodes(arguments[subtest], self.tree, this_node, subtest)
# Second pass to build node and all its children into a subtest
for node in this_node.descend(include_me=True):
diff --git a/pythonwhat/sct_syntax.py b/pythonwhat/pythonwhat/sct_syntax.py
similarity index 100%
rename from pythonwhat/sct_syntax.py
rename to pythonwhat/pythonwhat/sct_syntax.py
diff --git a/pythonwhat/signatures.py b/pythonwhat/pythonwhat/signatures.py
similarity index 100%
rename from pythonwhat/signatures.py
rename to pythonwhat/pythonwhat/signatures.py
diff --git a/pythonwhat/tasks.py b/pythonwhat/pythonwhat/tasks.py
similarity index 100%
rename from pythonwhat/tasks.py
rename to pythonwhat/pythonwhat/tasks.py
diff --git a/pythonwhat/test_exercise.py b/pythonwhat/pythonwhat/test_exercise.py
similarity index 96%
rename from pythonwhat/test_exercise.py
rename to pythonwhat/pythonwhat/test_exercise.py
index 38288deb..7c103c60 100644
--- a/pythonwhat/test_exercise.py
+++ b/pythonwhat/pythonwhat/test_exercise.py
@@ -1,6 +1,6 @@
from pythonwhat.State import State
from pythonwhat.utils import check_str, check_process
-from pythonwhat.reporter import Reporter
+from protowhat.Reporter import Reporter
from protowhat.Test import TestFail
from pythonwhat.utils import include_v1
@@ -43,7 +43,7 @@ def test_exercise(
solution_process=check_process(solution_process),
raw_student_output=check_str(raw_student_output),
force_diagnose=force_diagnose,
- reporter=Reporter(errors=[error] if error else []),
+ reporter=Reporter([error] if error else [])
)
State.root_state = state
diff --git a/pythonwhat/test_funcs/__init__.py b/pythonwhat/pythonwhat/test_funcs/__init__.py
similarity index 100%
rename from pythonwhat/test_funcs/__init__.py
rename to pythonwhat/pythonwhat/test_funcs/__init__.py
diff --git a/pythonwhat/test_funcs/test_compound_statement.py b/pythonwhat/pythonwhat/test_funcs/test_compound_statement.py
similarity index 95%
rename from pythonwhat/test_funcs/test_compound_statement.py
rename to pythonwhat/pythonwhat/test_funcs/test_compound_statement.py
index a41d8f37..b555664b 100644
--- a/pythonwhat/test_funcs/test_compound_statement.py
+++ b/pythonwhat/pythonwhat/test_funcs/test_compound_statement.py
@@ -1,4 +1,3 @@
-from protowhat.sct_syntax import link_to_state
from pythonwhat.checks.check_funcs import (
check_node,
check_part,
@@ -17,12 +16,6 @@
from functools import partial
from pythonwhat import utils
-# this is done by the chain for v2
-# it's only needed when a new state is created and (possibly) used elsewhere
-check_node = link_to_state(check_node)
-check_part = link_to_state(check_part)
-check_part_index = link_to_state(check_part_index)
-
def test_if_else(state, index=1, test=None, body=None, orelse=None):
"""Test parts of the if statement.
@@ -282,17 +275,19 @@ def shout( word = 'help', times = 3 ):
test_function_definition('shout') # fail
test_function_definition('shout', arg_defaults = False) # pass
test_function_definition('shout', arg_defaults = False, # fail
- outputs = [('help')])
+ outputs = [('help')])
test_function_definition('shout', arg_defaults = False, # pass
- results = [('help', 2)])
+ results = [('help', 2)])
test_function_definition('shout', args_defaults = False # pass
body = test_function('print', args = []]))
"""
# what the function will be referred to as
- child = check_node(state, "function_defs", name, "definition of `{{index}}()`")
+ child = check_node(
+ state, "function_defs", name, "definition of `{{index}}()`"
+ )
test_args(
child, arg_names, arg_defaults, nb_args_msg, arg_names_msg, arg_defaults_msg
@@ -337,7 +332,7 @@ def shout( word = 'help', times = 3 ):
def test_args(
- state, arg_names, arg_defaults, nb_args_msg, arg_names_msg, arg_defaults_msg
+ state, arg_names, arg_defaults, nb_args_msg, arg_names_msg, arg_defaults_msg
):
MSG_NUM_ARGS = "You should define {{parent[typestr]}} with {{sol_len}} arguments, instead got {{stu_len}}."
@@ -444,11 +439,7 @@ def test_object_after_expression(
pre_code=None,
**kwargs
):
- state.highlight = (
- state.ast_dispatcher.find("object_assignments", state.student_ast)
- .get(name, {})
- .get("highlight")
- )
+ state.highlight = state.ast_dispatcher("object_assignments", state.student_ast).get(name, {}).get("highlight")
has_equal_value(
state,
incorrect_msg=incorrect_msg,
@@ -498,7 +489,9 @@ def test_with(
if context_vals:
# test context var names ----
has_context(
- child, incorrect_msg=context_vals_msg or MSG_CTXT_NAMES, exact_names=True
+ child,
+ incorrect_msg=context_vals_msg or MSG_CTXT_NAMES,
+ exact_names=True,
)
# test num context vars ----
@@ -511,8 +504,7 @@ def test_with(
for i, context_test in enumerate(context_tests or []):
# partial the substate check, because the function uses two prepended messages
def check_context(state):
- return check_part_index(
- state,
+ return check_part_index(state,
"context",
i,
"%s context" % utils.get_ord(i + 1),
@@ -544,8 +536,8 @@ def test_list_comp(
):
"""Test list comprehension."""
kwargs = locals().copy()
- kwargs["typestr"] = "{{ordinal}} list comprehension"
- kwargs["comptype"] = "list_comps"
+ kwargs['typestr'] = "{{ordinal}} list comprehension"
+ kwargs['comptype'] = "list_comps"
test_comp(**kwargs)
@@ -575,7 +567,9 @@ def test_comp(
insufficient_ifs_msg = MSG_INSUFFICIENT_IFS
# get comprehension
- child = check_node(state, comptype, index - 1, typestr, missing_msg=not_called_msg)
+ child = check_node(
+ state, comptype, index - 1, typestr, missing_msg=not_called_msg
+ )
# test comprehension iter and its variable names (or number of variables)
if comp_iter:
@@ -600,4 +594,7 @@ def test_comp(
# test that ifs are same length
has_equal_part_len(child, "ifs", insufficient_ifs_msg)
# test individual ifs
- multi(check_part_index(child, "ifs", i, utils.get_ord(i + 1) + " if"), if_test)
+ multi(
+ check_part_index(child, "ifs", i, utils.get_ord(i + 1) + " if"),
+ if_test,
+ )
diff --git a/pythonwhat/test_funcs/test_function.py b/pythonwhat/pythonwhat/test_funcs/test_function.py
similarity index 94%
rename from pythonwhat/test_funcs/test_function.py
rename to pythonwhat/pythonwhat/test_funcs/test_function.py
index 9ce4ba2f..16adf0fe 100644
--- a/pythonwhat/test_funcs/test_function.py
+++ b/pythonwhat/pythonwhat/test_funcs/test_function.py
@@ -1,16 +1,10 @@
from functools import partial
-
-from protowhat.sct_syntax import link_to_state
from pythonwhat.checks.check_function import check_function
from protowhat.Feedback import InstructorError
from protowhat.Test import TestFail
from pythonwhat.checks.check_funcs import check_args
from pythonwhat.checks.has_funcs import has_equal_value, has_equal_ast, has_printout
-# this is done by the chain for v2
-# it's only needed when a new state is created and (possibly) used elsewhere
-check_function = link_to_state(check_function)
-
def arg_test(state, name, do_eval, missing_msg, incorrect_msg):
arg_state = check_args(state, name=name, missing_msg=missing_msg)
@@ -135,7 +129,9 @@ def test_function_v2(
eligible = do_eval[0] if isinstance(do_eval, list) and len(do_eval) > 0 else do_eval
if name == "print" and state.parent_state is None and eligible:
try:
- return has_printout(state, index=index, not_printed_msg=incorrect_msg[0])
+ return has_printout(
+ state, index=index, not_printed_msg=incorrect_msg[0]
+ )
except TestFail:
# The test didn't pass; just continue with the more strict check_function test.
pass
diff --git a/pythonwhat/test_funcs/test_object.py b/pythonwhat/pythonwhat/test_funcs/test_object.py
similarity index 83%
rename from pythonwhat/test_funcs/test_object.py
rename to pythonwhat/pythonwhat/test_funcs/test_object.py
index c0b441d5..d19d44a7 100644
--- a/pythonwhat/test_funcs/test_object.py
+++ b/pythonwhat/pythonwhat/test_funcs/test_object.py
@@ -1,14 +1,7 @@
-from protowhat.sct_syntax import link_to_state
from pythonwhat.tasks import getColumnsInProcess
from pythonwhat.checks.check_object import check_object, check_df, check_keys
from pythonwhat.checks.has_funcs import has_equal_value
-# this is done by the chain for v2
-# it's only needed when a new state is created and (possibly) used elsewhere
-check_object = link_to_state(check_object)
-check_df = link_to_state(check_df)
-check_keys = link_to_state(check_keys)
-
def test_object(
state,
diff --git a/pythonwhat/test_funcs/test_object_accessed.py b/pythonwhat/pythonwhat/test_funcs/test_object_accessed.py
similarity index 90%
rename from pythonwhat/test_funcs/test_object_accessed.py
rename to pythonwhat/pythonwhat/test_funcs/test_object_accessed.py
index a9ad7474..3e32dab9 100644
--- a/pythonwhat/test_funcs/test_object_accessed.py
+++ b/pythonwhat/pythonwhat/test_funcs/test_object_accessed.py
@@ -34,10 +34,8 @@ def test_object_accessed(state, name, times=1, not_accessed_msg=None):
| ``test_object_accessed("arr.shape")``: pass.
| ``test_object_accessed("arr.dtype")``: fail.
"""
- student_object_accesses = state.ast_dispatcher.find(
- "object_accesses", state.student_ast
- )
- student_mappings = state.ast_dispatcher.find("oa_mappings", state.student_ast)
+ student_object_accesses = state.ast_dispatcher("object_accesses", state.student_ast)
+ student_mappings = state.ast_dispatcher("oa_mappings", state.student_ast)
if not not_accessed_msg:
stud_name = name
diff --git a/pythonwhat/test_funcs/utils.py b/pythonwhat/pythonwhat/test_funcs/utils.py
similarity index 94%
rename from pythonwhat/test_funcs/utils.py
rename to pythonwhat/pythonwhat/test_funcs/utils.py
index 5410f6e6..03fae8d9 100644
--- a/pythonwhat/test_funcs/utils.py
+++ b/pythonwhat/pythonwhat/test_funcs/utils.py
@@ -1,7 +1,9 @@
import ast
-from protowhat.Feedback import Feedback, InstructorError
+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
@@ -128,10 +130,10 @@ def call(
# either error test and no error, or vice-versa
stu_node = state.student_parts["node"]
- stu_state = state.to_child(highlight=stu_node)
+ stu_state = StubState(stu_node, state.highlighting_disabled)
if (test == "error") ^ isinstance(eval_stu, Exception):
_msg = state.build_message(error_msg, fmt_kwargs)
- stu_state.report(_msg)
+ state.report(Feedback(_msg, stu_state))
# incorrect result
_msg = state.build_message(incorrect_msg, fmt_kwargs)
diff --git a/pythonwhat/utils.py b/pythonwhat/pythonwhat/utils.py
similarity index 100%
rename from pythonwhat/utils.py
rename to pythonwhat/pythonwhat/utils.py
diff --git a/pythonwhat/utils_ast.py b/pythonwhat/pythonwhat/utils_ast.py
similarity index 100%
rename from pythonwhat/utils_ast.py
rename to pythonwhat/pythonwhat/utils_ast.py
diff --git a/pythonwhat/utils_env.py b/pythonwhat/pythonwhat/utils_env.py
similarity index 100%
rename from pythonwhat/utils_env.py
rename to pythonwhat/pythonwhat/utils_env.py
diff --git a/pythonwhat/reporter.py b/pythonwhat/reporter.py
deleted file mode 100644
index df68479b..00000000
--- a/pythonwhat/reporter.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from protowhat.Reporter import Reporter as BaseReporter
-
-
-class Reporter(BaseReporter):
- ast_highlight_offset = {"column_start": 1}
diff --git a/requirements.txt b/pythonwhat/requirements.txt
similarity index 92%
rename from requirements.txt
rename to pythonwhat/requirements.txt
index e85eb809..ad2c757b 100644
--- a/requirements.txt
+++ b/pythonwhat/requirements.txt
@@ -1,5 +1,5 @@
# pythonwhat deps
-protowhat~=1.8.0
+protowhat~=1.6.0
asttokens~=1.1.10
dill~=0.2.7.1
markdown2~=2.3.5
@@ -15,7 +15,7 @@ html5lib~=1.0.1
h5py~=2.7.1
requests~=2.20.0
seaborn~=0.8.1
-sqlalchemy~=1.3.0
+sqlalchemy~=1.2.6
xlrd~=1.1.0
# test-utils deps
diff --git a/setup.py b/pythonwhat/setup.py
similarity index 100%
rename from setup.py
rename to pythonwhat/setup.py
diff --git a/tests/__init__.py b/pythonwhat/tests/__init__.py
similarity index 100%
rename from tests/__init__.py
rename to pythonwhat/tests/__init__.py
diff --git a/tests/helper.py b/pythonwhat/tests/helper.py
similarity index 100%
rename from tests/helper.py
rename to pythonwhat/tests/helper.py
diff --git a/tests/test_ast_operations.py b/pythonwhat/tests/test_ast_operations.py
similarity index 100%
rename from tests/test_ast_operations.py
rename to pythonwhat/tests/test_ast_operations.py
diff --git a/tests/test_author_warnings.py b/pythonwhat/tests/test_author_warnings.py
similarity index 100%
rename from tests/test_author_warnings.py
rename to pythonwhat/tests/test_author_warnings.py
diff --git a/tests/test_check_class_def.py b/pythonwhat/tests/test_check_class_def.py
similarity index 100%
rename from tests/test_check_class_def.py
rename to pythonwhat/tests/test_check_class_def.py
diff --git a/tests/test_check_function.py b/pythonwhat/tests/test_check_function.py
similarity index 99%
rename from tests/test_check_function.py
rename to pythonwhat/tests/test_check_function.py
index c313a07d..1063ab2e 100644
--- a/tests/test_check_function.py
+++ b/pythonwhat/tests/test_check_function.py
@@ -86,9 +86,7 @@ def test_bind_args():
pec = "def my_fun(a, b, *args, **kwargs): pass"
s = setup_state(pec=pec, stu_code="my_fun(1, 2, 3, 4, c = 5)")
- args = s._state.ast_dispatcher.find("function_calls", s._state.student_ast)[
- "my_fun"
- ][0]["args"]
+ args = s._state.ast_dispatcher("function_calls", s._state.student_ast)["my_fun"][0]["args"]
sig = signature(s._state.student_process.shell.user_ns["my_fun"])
binded_args = bind_args(sig, args)
assert binded_args["a"]["node"].n == 1
diff --git a/tests/test_check_function_def.py b/pythonwhat/tests/test_check_function_def.py
similarity index 100%
rename from tests/test_check_function_def.py
rename to pythonwhat/tests/test_check_function_def.py
diff --git a/tests/test_check_if_else.py b/pythonwhat/tests/test_check_if_else.py
similarity index 100%
rename from tests/test_check_if_else.py
rename to pythonwhat/tests/test_check_if_else.py
diff --git a/tests/test_check_list_comp.py b/pythonwhat/tests/test_check_list_comp.py
similarity index 100%
rename from tests/test_check_list_comp.py
rename to pythonwhat/tests/test_check_list_comp.py
diff --git a/tests/test_check_logic.py b/pythonwhat/tests/test_check_logic.py
similarity index 100%
rename from tests/test_check_logic.py
rename to pythonwhat/tests/test_check_logic.py
diff --git a/tests/test_check_object.py b/pythonwhat/tests/test_check_object.py
similarity index 100%
rename from tests/test_check_object.py
rename to pythonwhat/tests/test_check_object.py
diff --git a/tests/test_check_try_except.py b/pythonwhat/tests/test_check_try_except.py
similarity index 100%
rename from tests/test_check_try_except.py
rename to pythonwhat/tests/test_check_try_except.py
diff --git a/tests/test_converters.py b/pythonwhat/tests/test_converters.py
similarity index 100%
rename from tests/test_converters.py
rename to pythonwhat/tests/test_converters.py
diff --git a/tests/test_debug.py b/pythonwhat/tests/test_debug.py
similarity index 100%
rename from tests/test_debug.py
rename to pythonwhat/tests/test_debug.py
diff --git a/tests/test_docs.py b/pythonwhat/tests/test_docs.py
similarity index 100%
rename from tests/test_docs.py
rename to pythonwhat/tests/test_docs.py
diff --git a/tests/test_has_chosen.py b/pythonwhat/tests/test_has_chosen.py
similarity index 100%
rename from tests/test_has_chosen.py
rename to pythonwhat/tests/test_has_chosen.py
diff --git a/tests/test_has_code.py b/pythonwhat/tests/test_has_code.py
similarity index 100%
rename from tests/test_has_code.py
rename to pythonwhat/tests/test_has_code.py
diff --git a/tests/test_has_expr.py b/pythonwhat/tests/test_has_expr.py
similarity index 100%
rename from tests/test_has_expr.py
rename to pythonwhat/tests/test_has_expr.py
diff --git a/tests/test_has_import.py b/pythonwhat/tests/test_has_import.py
similarity index 100%
rename from tests/test_has_import.py
rename to pythonwhat/tests/test_has_import.py
diff --git a/tests/test_has_no_error.py b/pythonwhat/tests/test_has_no_error.py
similarity index 100%
rename from tests/test_has_no_error.py
rename to pythonwhat/tests/test_has_no_error.py
diff --git a/tests/test_has_output.py b/pythonwhat/tests/test_has_output.py
similarity index 100%
rename from tests/test_has_output.py
rename to pythonwhat/tests/test_has_output.py
diff --git a/tests/test_has_printout.py b/pythonwhat/tests/test_has_printout.py
similarity index 100%
rename from tests/test_has_printout.py
rename to pythonwhat/tests/test_has_printout.py
diff --git a/tests/test_highlighting.py b/pythonwhat/tests/test_highlighting.py
similarity index 100%
rename from tests/test_highlighting.py
rename to pythonwhat/tests/test_highlighting.py
diff --git a/tests/test_messaging.py b/pythonwhat/tests/test_messaging.py
similarity index 99%
rename from tests/test_messaging.py
rename to pythonwhat/tests/test_messaging.py
index 86bf1222..3fff54b5 100644
--- a/tests/test_messaging.py
+++ b/pythonwhat/tests/test_messaging.py
@@ -1,6 +1,6 @@
import pytest
import tests.helper as helper
-from pythonwhat.reporter import Reporter
+from protowhat.Reporter import Reporter
from difflib import Differ
diff --git a/tests/test_set_context.py b/pythonwhat/tests/test_set_context.py
similarity index 100%
rename from tests/test_set_context.py
rename to pythonwhat/tests/test_set_context.py
diff --git a/tests/test_set_env.py b/pythonwhat/tests/test_set_env.py
similarity index 100%
rename from tests/test_set_env.py
rename to pythonwhat/tests/test_set_env.py
diff --git a/tests/test_signatures.py b/pythonwhat/tests/test_signatures.py
similarity index 100%
rename from tests/test_signatures.py
rename to pythonwhat/tests/test_signatures.py
diff --git a/tests/test_spec.py b/pythonwhat/tests/test_spec.py
similarity index 100%
rename from tests/test_spec.py
rename to pythonwhat/tests/test_spec.py
diff --git a/tests/test_state.py b/pythonwhat/tests/test_state.py
similarity index 91%
rename from tests/test_state.py
rename to pythonwhat/tests/test_state.py
index 96dc0e8e..5f69c573 100644
--- a/tests/test_state.py
+++ b/pythonwhat/tests/test_state.py
@@ -12,5 +12,5 @@ def test_pec_parsing_error():
student_process=None,
solution_process=None,
reporter=None,
- raw_student_output=None,
+ raw_student_output=None
)
diff --git a/tests/test_test_compound_statement.py b/pythonwhat/tests/test_test_compound_statement.py
similarity index 100%
rename from tests/test_test_compound_statement.py
rename to pythonwhat/tests/test_test_compound_statement.py
diff --git a/tests/test_test_exercise.py b/pythonwhat/tests/test_test_exercise.py
similarity index 96%
rename from tests/test_test_exercise.py
rename to pythonwhat/tests/test_test_exercise.py
index 83d898dd..2a978501 100644
--- a/tests/test_test_exercise.py
+++ b/pythonwhat/tests/test_test_exercise.py
@@ -4,10 +4,10 @@
import tests.helper as helper
-@pytest.fixture(scope="session", autouse=True)
+@pytest.fixture(scope='session', autouse=True)
def log_calls():
yield
- print("Output test data")
+ print('Output test data')
with open("docs/test_data.json", "w") as write_file:
json.dump(helper.test_data, write_file)
diff --git a/tests/test_test_object_accessed.py b/pythonwhat/tests/test_test_object_accessed.py
similarity index 100%
rename from tests/test_test_object_accessed.py
rename to pythonwhat/tests/test_test_object_accessed.py
diff --git a/tests/test_test_with.py b/pythonwhat/tests/test_test_with.py
similarity index 100%
rename from tests/test_test_with.py
rename to pythonwhat/tests/test_test_with.py
diff --git a/tests/test_utils.py b/pythonwhat/tests/test_utils.py
similarity index 100%
rename from tests/test_utils.py
rename to pythonwhat/tests/test_utils.py
diff --git a/tests/test_v2_only.py b/pythonwhat/tests/test_v2_only.py
similarity index 100%
rename from tests/test_v2_only.py
rename to pythonwhat/tests/test_v2_only.py
diff --git a/tests/test_check_files.py b/tests/test_check_files.py
deleted file mode 100644
index 4e34575f..00000000
--- a/tests/test_check_files.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from tempfile import NamedTemporaryFile
-
-import pytest
-import tests.helper as helper
-from protowhat.sct_syntax import F
-
-from pythonwhat.local import setup_state
-from protowhat.checks import check_files as cf
-
-
-@pytest.fixture(scope="function")
-def tf():
- with NamedTemporaryFile() as tmp:
- tmp.file.write(b"1 + 1")
- tmp.file.flush()
- yield tmp
-
-
-def test_file_existence(tf):
- s = setup_state("", "", pec="")
-
- child = cf.check_file(s._state, tf.name)
- assert "1 + 1" in child.student_code
-
- file_chain = s.check_file(tf.name)
- assert "1 + 1" in file_chain._state.student_code
-
- with helper.verify_sct(True):
- s >> F(attr_scts={"check_file": cf.check_file}).check_file(tf.name)