In this unit you will learn how to use Python to implement the first ever program that every programmer starts with. This also serves as an example for the master notebook format.
# All markdown cells are searched for triple-backtick blocks. Within a triple quoted block,
# a match on /^# ASSIGNMENT METADATA/ will trigger handling this as an assignment-level metadata
# block. The rest of the metadata is parsed as YAML and may be used e.g. for setting
# default settings for autograder isolation (memory limit etc).
# The assignment_id is useful to identify which assignment a submission pertains to.
# ASSIGNMENT METADATA
assignment_id: "HelloWorld"
Here is the traditional first programming exercise, called "Hello world". The task is to print the message: "Hello, world".
Here are a few examples to get you started. Run the following cells and see how you can print a message. To run a cell, click with mouse inside a cell, then press Ctrl+Enter to execute it. If you want to execute a few cells sequentially, then press Shift+Enter instead, and the focus will be automatically moved to the next cell as soon as one cell finishes execution.
In [1]:
print("hello")
In [2]:
print("bye bye")
In [3]:
print("hey", "you")
In [4]:
print("one")
print("two")
In [5]:
# MASTER ONLY
from prog_edu_assistant_tools.summary_test_result import SummaryTestResult
# imports %%solution, %%submission, %%template
%load_ext prog_edu_assistant_tools.magics
from prog_edu_assistant_tools.magics import report, autotest
# The markdown cell with triple-backtick block matching /^# EXERCISE METADATA/ is an exercise-level
# metadata. The next block is assumed to be the solution block, and will get annotated with
# the exercise_id.
# EXERCISE METADATA
exercise_id: "hello1"
Now it is your turn. Please create a program in the next cell that would print a message "Hello, world":
In [6]:
%%solution
""" # BEGIN PROMPT
# ... put your program here
""" # END PROMPT
# BEGIN SOLUTION
print("Hello, world")
# END SOLUTION
In [7]:
# MASTER ONLY
submission_source
Out[7]:
In [8]:
# This will not be included in the student notebook because of BEGIN UNITTEST marker below.
# The test below assumes that the solution above was written into two files:
# - submission.py with the solution code
# - submission_source.py which defines a single variable source whose value holds the source
# code of the submission.
#
# and then imported with
#
# import submission_source
# import submission
#
# In the Jupyter notebook, this setup is performed by %%solution and %%submission magics.
# BEGIN UNITTEST
# The unit tests main part is contained between "BEGIN UNITTEST" and "END UNITTEST". It will be copied
# verbatim into the autograder directory, with 'import submission' or 'import submission_source' uncommented.
import unittest
#import submission_source
import sys
import io
from contextlib import contextmanager
from io import StringIO
# TODO(salikh): Move the helper code into a library.
@contextmanager
def capture_output():
"""Captures the stdout and stderr into StringIO objects."""
capture_out, capture_err = StringIO(), StringIO()
save_out, save_err = sys.stdout, sys.stderr
try:
sys.stdout, sys.stderr = capture_out, capture_err
yield sys.stdout, sys.stderr
finally:
sys.stdout, sys.stderr = save_out, save_err
class HelloOutputTest(unittest.TestCase):
def test_output(self):
with capture_output() as (out, err):
# Exercise the code under test:
exec(submission_source.source)
self.assertEqual(err.getvalue(), "")
self.assertEqual(out.getvalue(), "Hello, world\n")
def test_not_empty(self):
with capture_output() as (out, err):
exec(submission_source.source)
self.assertNotEqual(out.getvalue(), "")
def test_has_hello(self):
with capture_output() as (out, err):
exec(submission_source.source)
self.assertTrue("Hello" in out.getvalue())
# END UNITTEST
# The parts after END UNITTEST are executed in the notebook environment, but not copied
# to the autograder scripts or to student notebooks.
result, log = autotest(HelloOutputTest)
# Optional. Useful for debugging.
print(log)
# Can assert the results
for k in result.results.keys():
assert(result.results[k])
In [9]:
%%template HelloOutputTest_template
<h2 style='color: #387;'>Your submission</h2>
<pre style='background: #F0F0F0; padding: 3pt; margin: 4pt; border: 1pt solid #DDD; border-radius: 3pt;'>{{ formatted_source }}</pre>
{% if 'HelloOutputTest' not in results %}
There was something wrong with HelloOutputTest
{% else %}
<h2 style='color: #387;'>Results</h2>
{% if 'test_not_empty' in results['HelloOutputTest'] and not results['HelloOutputTest']['test_not_empty'] %}
Your snippet does not produce any output. Please add a print statement.
{% elif 'test_has_hello' in results['HelloOutputTest'] and not results['HelloOutputTest']['test_has_hello'] %}
The output of your code does not include "Hello" string. Please add it.
{% elif 'test_output' in results['HelloOutputTest'] and not results['HelloOutputTest']['test_output'] %}
The output is incorrect. Please make sure it is exactly "Hello, world".
{% elif 'error' in results['HelloOutputTest'] %}
{{results['HelloOutputTest']['error']}}
{% elif 'passed' in results['HelloOutputTest'] and not results['HelloOutputTest']['passed'] %}
Something went wrong with auto-checking.
{% else %}
Your code looks okay.
{% endif %}
{% endif %}
<hr>
<h2>Full result vector</h2>
{{results}}
{% if logs: %}
<h2>Logs</h2>
{% for k in logs: %}
<h3>{{k}}</h3>
<pre>{{logs[k]}}</pre>
{% endfor %}
{% endif %}
In [10]:
from prog_edu_assistant_tools.magics import report
report(HelloOutputTest_template, source=submission_source.source, results=result.results)
Out[10]:
In [11]:
%%solution
def hello(name):
""" # BEGIN PROMPT
# Please put your solution here:
# return ...
pass
""" # END PROMPT
# BEGIN SOLUTION
return "Hello, " + name
# END SOLUTION
In [12]:
%%studenttest StudentTest
# Test your solution by running this cell.
assert(hello("world") == "Hello, world")
In [13]:
# The part before "BEGIN UNITTEST" -- preamble -- sets up the environment so that 'submission.hello'
# is a function that we need to test. In the autograder worker environment, the preamble will be
# replaced with 'import submission' with an assumption that the student's solution will be written
# to the file 'submission.py'. The submission source will be available in submission_source.py
# that will define a single variable named 'source'.
# The unit tests main part is contained between "BEGIN UNITTEST" and "END UNITTEST". It will be copied
# verbatim into the autograder directory, with an addition of 'import submission' and 'import submission_source'
# BEGIN UNITTEST
#import submission
import unittest
import ast
class HelloTest(unittest.TestCase):
def test_hello(self):
self.assertEqual(submission.hello("one"), "Hello, one")
def test_includes_arg(self):
self.assertTrue("xyz123" in submission.hello("xyz123"))
def test_includes_hello(self):
self.assertTrue("Hello" in submission.hello("xyz123"))
# END UNITTEST
from prog_edu_assistant_tools.magics import autotest
result, log = autotest(HelloTest)
# Optional.
print(log)
print(result.results)
assert result.results['HelloTest']['test_hello'] == True
In [14]:
%%template HelloTest_template
<h2 style='color: #387;'>Your submission</h2>
<pre style='background: #F0F0F0; padding: 3pt; margin: 4pt; border: 1pt solid #DDD; border-radius: 3pt;'>{{ formatted_source }}</pre>
{% if 'HelloTest' in results: %}
<h2 style='color: #387;'>Results</h2>
{% if 'passed' in results['HelloTest'] and results['HelloTest']['passed']: %}
Your code looks okay.
{% elif 'test_includes_hello' in results['HelloTest'] and not results['HelloTest']['test_includes_hello'] %}
The response from your function does not include "Hello" string. Please check if you have included it.
{% elif 'test_includes_arg' in results['HelloTest'] and not results['HelloTest']['test_includes_arg'] %}
The response from your function does not include the person name, which as given as the function argument.
{% elif 'test_hello' in results['HelloTest'] and not results['HelloTest']['test_hello'] %}
You greeting is incorrect in some way.
{% else %}
Something is wrong.
{% endif %}
{% else %}
<b style='color: red'>ERROR</b>: Something is wrong with running the test HelloTest.
{% endif %}
<hr>
<h2>Full result vector</h2>
{{results}}
{#
{% if logs: %}
<h2>Logs</h2>
{% for k in logs: %}
<h3>{{k}}</h3>
<pre>{{logs[k]}}</pre>
{% endfor %}
{% endif %}#}
In [15]:
# MASTER ONLY
report(HelloTest_template, source=submission_source.source, results=result.results)
Out[15]:
In [16]:
# BEGIN UNITTEST
#import submission_source
import unittest
import ast
class HelloSyntaxTest(unittest.TestCase):
def test_compiles(self):
# Expect success or exception.
ast.parse(submission_source.source)
# END UNITTEST
from prog_edu_assistant_tools.magics import autotest
result, log = autotest(HelloSyntaxTest)
# Optional.
print(log)
print(result.results)
assert result.results['HelloSyntaxTest']['test_compiles'] == True
In [17]:
%%template HelloSyntaxTest_template
<style type='text/css'>
.k { color: purple; }
.c1 { color: green; }
.s2 { color: brown; }
</style>
<h2 style='color: #387;'>Your submission</h2>
<pre style='background: #F0F0F0; padding: 3pt; margin: 4pt; border: 1pt solid #DDD; border-radius: 3pt;'>{{ formatted_source }}</pre>
<h2 style='color: #087;'>Results</h2>
{% if 'HelloSyntaxTest' not in results %}
Something is wrong with running the test HelloSyntaxTest.
{% else %}
{% if 'test_compiles' in results['HelloSyntaxTest'] and not results['HelloSyntaxTest']['test_compiles']: %}
Do you even compile, bro?
{% else %}
Syntax looks fine.
{% endif %}
{% endif %}
<hr>
<h2>Full result vector</h2>
{{results}}
{#
{% if logs: %}
<h2>Logs</h2>
{% for k in logs: %}
<h3>{{k}}</h3>
<pre>{{logs[k]}}</pre>
{% endfor %}
{% endif %} #}
In [18]:
report(HelloSyntaxTest_template, source=submission_source.source, results=result.results)
Out[18]:
In [19]:
%%submission
def hello(name):
return "Bye, " + name
In [20]:
# MASTER ONLY
result1, log = autotest(HelloTest)
#print(log)
assert result1.results['HelloTest']['test_hello'] == False
assert result1.results['HelloTest']['test_includes_hello'] == False
assert result1.results['HelloTest']['test_includes_arg'] == True
In [21]:
# MASTER ONLY
print(result1.results)
In [22]:
# MASTER ONLY
report(HelloTest_template, source=submission_source.source, results=result1.results)
Out[22]:
In [23]:
# MASTER ONLY
result2, log = autotest(HelloSyntaxTest)
assert result2.results['HelloSyntaxTest']['test_compiles'] == True
results = {**result1.results, **result2.results}
In [24]:
# MASTER ONLY
report(HelloSyntaxTest_template, source=submission_source.source, results=result2.results)
Out[24]:
In [25]:
# MASTER ONLY
report(HelloTest_template, source=submission_source.source, results=result2.results)
Out[25]:
In [26]:
%%submission
def wrong_hello(name):
return "Bye, " + name
In [27]:
# MASTER ONLY
result, log = autotest(HelloSyntaxTest)
print(log)
print(result.results)
In [28]:
%%submission
# This submission has a syntax error. Note that invalid code inside %%submission cell
# does not break the notebook execution.
def syntax_error(name:
return name
In [29]:
# MASTER ONLY
# In case of syntax error in submission, it is initiliazed to None value.
assert submission == None
# TODO(salikh): Fix the behavior, since in the autograder 'import submission' will fail,
# producing empty outcome vector rather than a bunch of ERROR outcomes.
In [30]:
# autotest expects the submission to have been set with %%submission cell magic previously.
result3, log = autotest(HelloSyntaxTest)
print(log)
print(result3.results)
assert(result3.results['HelloSyntaxTest']['test_compiles'] == False)
In [31]:
# MASTER ONLY
report(HelloSyntaxTest_template, source=submission_source.source, results=result3.results)
Out[31]: