diff --git a/extras/__init__.py b/extras/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/extras/scripts/hw_0_helper.py b/extras/scripts/hw_0_helper.py new file mode 100644 index 00000000..390092d8 --- /dev/null +++ b/extras/scripts/hw_0_helper.py @@ -0,0 +1,31 @@ +import ast +import os +import pytest +from .nb_helper import * + + +# https://sadh.life/post/ast/ +class FuncCallChecker(ast.NodeVisitor): + def __init__(self, func: str): + self.func = func + self.num_calls = 0 + + def visit_Call(self, node): + if isinstance(node.func, ast.Name) and node.func.id == self.func: + self.num_calls += 1 + + +@pytest.fixture() +def notebook(): + nb_full_path = os.path.join(os.getcwd(), "hw_0.ipynb") + return read_notebook(nb_full_path) + + +@pytest.fixture() +def script(notebook): + return notebook_to_script(notebook) + + +@pytest.fixture() +def tree(script): + return ast.parse(script) diff --git a/hw_0.ipynb b/hw_0.ipynb index 88ca68f2..8c43739a 100644 --- a/hw_0.ipynb +++ b/hw_0.ipynb @@ -184,6 +184,76 @@ "# your code goes here" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Autograding\n", + "\n", + "Using [this approach](https://stackoverflow.com/a/70920396/358804)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "import ipytest\n", + "ipytest.autoconfig()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[31mF\u001b[0m\u001b[31m [100%]\u001b[0m\n", + "============================================= FAILURES =============================================\n", + "\u001b[31m\u001b[1m_________________________________________ test_num_inputs __________________________________________\u001b[0m\n", + "\n", + "tree = \n", + "\n", + " \u001b[94mdef\u001b[39;49;00m \u001b[92mtest_num_inputs\u001b[39;49;00m(tree):\n", + " checker = FuncCallChecker(\u001b[33m\"\u001b[39;49;00m\u001b[33minput\u001b[39;49;00m\u001b[33m\"\u001b[39;49;00m)\n", + " checker.visit(tree)\n", + " \n", + "> \u001b[94massert\u001b[39;49;00m checker.num_calls >= \u001b[94m8\u001b[39;49;00m, \u001b[33m\"\u001b[39;49;00m\u001b[33myou don\u001b[39;49;00m\u001b[33m'\u001b[39;49;00m\u001b[33mt have enough calls to input()\u001b[39;49;00m\u001b[33m\"\u001b[39;49;00m\n", + "\u001b[1m\u001b[31mE AssertionError: you don't have enough calls to input()\u001b[0m\n", + "\u001b[1m\u001b[31mE assert 2 >= 8\u001b[0m\n", + "\u001b[1m\u001b[31mE + where 2 = .num_calls\u001b[0m\n", + "\n", + "\u001b[1m\u001b[31m/var/folders/kg/1ys0dccx4237f5wsd_w10dt80000gn/T/ipykernel_68325/4145485001.py\u001b[0m:8: AssertionError\n", + "\u001b[33m========================================= warnings summary =========================================\u001b[0m\n", + "tmpc439hpt9.py::test_num_inputs\n", + " /usr/local/Caskroom/miniconda/base/envs/python-public-policy/lib/python3.10/site-packages/IPython/core/inputsplitter.py:21: DeprecationWarning: IPython.core.inputsplitter is deprecated since IPython 7 in favor of `IPython.core.inputtransformer2`\n", + " warn('IPython.core.inputsplitter is deprecated since IPython 7 in favor of `IPython.core.inputtransformer2`',\n", + "\n", + "-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\n", + "===================================== short test summary info ======================================\n", + "FAILED tmpc439hpt9.py::test_num_inputs - AssertionError: you don't have enough calls to input()\n" + ] + } + ], + "source": [ + "%%ipytest -qq\n", + "\n", + "from extras.scripts.hw_0_helper import *\n", + "\n", + "\n", + "def test_num_inputs(tree):\n", + " checker = FuncCallChecker(\"input\")\n", + " checker.visit(tree)\n", + "\n", + " assert checker.num_calls >= 8, \"you don't have enough calls to input()\"" + ] + }, { "cell_type": "markdown", "metadata": {},