xml="""
<quiz>
<!-- question: 850758 -->
<question type="coderunner">
<name>
<text>How many items in a list?</text>
</name>
<questiontext format="html">
<text><![CDATA[<p>Write a function <code>how_many(list_in)</code> which returns the number of items in <code>list_in</code>.</p>
<p><br></p>]]></text>
</questiontext>
<generalfeedback format="html">
<text><![CDATA[<p><b>len() </b>returns the length (the number of items) of an object. The argument may be a sequence (such as a string, bytes, tuple, list, or range) or a collection (such as a dictionary, set, or frozen set).<br></p>]]></text>
</generalfeedback>
<defaultgrade>1</defaultgrade>
<penalty>0</penalty>
<hidden>0</hidden>
<idnumber></idnumber>
<coderunnertype>python3</coderunnertype>
<prototypetype>0</prototypetype>
<allornothing>1</allornothing>
<penaltyregime>10, 20, ...</penaltyregime>
<precheck>0</precheck>
<showsource>0</showsource>
<answerboxlines>6</answerboxlines>
<answerboxcolumns>100</answerboxcolumns>
<answerpreload>def how_many(list_in):
'''returns the number of elements in list_in'''
</answerpreload>
<globalextra></globalextra>
<useace></useace>
<resultcolumns></resultcolumns>
<template></template>
<iscombinatortemplate></iscombinatortemplate>
<allowmultiplestdins></allowmultiplestdins>
<answer>def how_many(list_in):
'''returns the number of elements in list_in'''
return len(list_in)</answer>
<validateonsave>1</validateonsave>
<testsplitterre></testsplitterre>
<language></language>
<acelang></acelang>
<sandbox></sandbox>
<grader></grader>
<cputimelimitsecs></cputimelimitsecs>
<memlimitmb></memlimitmb>
<sandboxparams></sandboxparams>
<templateparams></templateparams>
<hoisttemplateparams>1</hoisttemplateparams>
<twigall>0</twigall>
<uiplugin></uiplugin>
<attachments>0</attachments>
<attachmentsrequired>0</attachmentsrequired>
<maxfilesize>10240</maxfilesize>
<filenamesregex></filenamesregex>
<filenamesexplain></filenamesexplain>
<displayfeedback>1</displayfeedback>
<testcases>
<testcase testtype="0" useasexample="1" hiderestiffail="0" mark="1.0000000" >
<testcode>
<text>print ( how_many(['fig', 'kiwi', 13.7]))</text>
</testcode>
<stdin>
<text></text>
</stdin>
<expected>
<text>3</text>
</expected>
<extra>
<text></text>
</extra>
<display>
<text>SHOW</text>
</display>
</testcase>
<testcase testtype="0" useasexample="0" hiderestiffail="0" mark="1.0000000" >
<testcode>
<text>print(how_many([]))</text>
</testcode>
<stdin>
<text></text>
</stdin>
<expected>
<text>0 </text>
</expected>
<extra>
<text></text>
</extra>
<display>
<text>SHOW</text>
</display>
</testcase>
<testcase testtype="0" useasexample="0" hiderestiffail="0" mark="1.0000000" >
<testcode>
<text>print(how_many([' ']))</text>
</testcode>
<stdin>
<text></text>
</stdin>
<expected>
<text>1</text>
</expected>
<extra>
<text></text>
</extra>
<display>
<text>SHOW</text>
</display>
</testcase>
<testcase testtype="0" useasexample="1" hiderestiffail="0" mark="1.0000000" >
<testcode>
<text>print(how_many([['only', 'count'], ['the' 'top'], ['level', 'lists']]))</text>
</testcode>
<stdin>
<text></text>
</stdin>
<expected>
<text>3</text>
</expected>
<extra>
<text></text>
</extra>
<display>
<text>SHOW</text>
</display>
</testcase>
<testcase testtype="0" useasexample="0" hiderestiffail="0" mark="1.0000000" >
<testcode>
<text>print(how_many([1, [2, 3, [4, 5], 6], 7]))</text>
</testcode>
<stdin>
<text></text>
</stdin>
<expected>
<text>3</text>
</expected>
<extra>
<text></text>
</extra>
<display>
<text>SHOW</text>
</display>
</testcase>
</testcases>
</question>
</quiz>
"""
import xml.etree.ElementTree as ET
root = ET.fromstring(xml)
root
<Element 'quiz' at 0x108729fb0>
root.tag
'quiz'
for child in root:
print(child.tag, child.attrib)
for gchild in child:
print(gchild.tag, gchild.attrib)
question {'type': 'coderunner'} name {} questiontext {'format': 'html'} generalfeedback {'format': 'html'} defaultgrade {} penalty {} hidden {} idnumber {} coderunnertype {} prototypetype {} allornothing {} penaltyregime {} precheck {} showsource {} answerboxlines {} answerboxcolumns {} answerpreload {} globalextra {} useace {} resultcolumns {} template {} iscombinatortemplate {} allowmultiplestdins {} answer {} validateonsave {} testsplitterre {} language {} acelang {} sandbox {} grader {} cputimelimitsecs {} memlimitmb {} sandboxparams {} templateparams {} hoisttemplateparams {} twigall {} uiplugin {} attachments {} attachmentsrequired {} maxfilesize {} filenamesregex {} filenamesexplain {} displayfeedback {} testcases {}
from IPython.display import HTML
for question in root.findall('question'):
display(HTML(question.find('questiontext/text').text))
answer = question.find('answer').text
print(answer)
print()
tests = question.find('testcases')
for test in tests.findall('testcase'):
print(test.find('testcode/text').text, test.find('expected/text').text)
Write a function how_many(list_in)
which returns the number of items in list_in
.
def how_many(list_in): '''returns the number of elements in list_in''' return len(list_in) print ( how_many(['fig', 'kiwi', 13.7])) 3 print(how_many([])) 0 print(how_many([' '])) 1 print(how_many([['only', 'count'], ['the' 'top'], ['level', 'lists']])) 3 print(how_many([1, [2, 3, [4, 5], 6], 7])) 3
There are a couple of ways we can evaluate the code represented by the string, specifically using eval()
or exec()
.
exec()
doesn't return anything;eval()
returns the final value;exec(answer)
how_many(['fig', 'kiwi', 13.7])
3
%%capture my_answer --no-stderr
answers = []
for question in root.findall('question'):
tests = question.find('testcases')
for test in tests.findall('testcase'):
exec(test.find('testcode/text').text)
answers.append(test.find('expected/text').text)
The way the question is written and handled is really shonky... I suspect it gives all sorts of opportunities for false somethings?!
my_answer.stdout, '\n'.join(answers)+'\n'
('3\n0\n1\n3\n3\n', '3\n0 \n1\n3\n3\n')
my_answer.stdout, '\n'.join(answers)+'\n'
('3\n0\n1\n3\n3\n', '3\n0 \n1\n3\n3\n')
If we try to do some simple assertion tests on evaluated code, things all go wrong... we need to to type checking, white space clearning (nut might whitespace be important?) etc etc.
import re
for question in root.findall('question'):
tests = question.find('testcases')
for test in tests.findall('testcase'):
_test = test.find('testcode/text').text
print(_test, '..')
matches = re.findall(r"print\s*\(\s*(.*)\s*\)", _test)
if matches:
print(matches[0])
answer = eval(matches[0])
#Hack the assertion so tests pass - this leads to a loss of information
# and suggests there's something wrong with how answers are represented?
assert str(answer) == test.find('expected/text').text.strip()
#The following replicates the %%capture
#https://stackoverflow.com/a/3906309/454773
import sys
from io import StringIO
import contextlib
@contextlib.contextmanager
def stdoutIO(stdout=None):
old = sys.stdout
if stdout is None:
stdout = StringIO()
sys.stdout = stdout
yield stdout
sys.stdout = old
with stdoutIO() as s:
exec('print("hello")')
s.getvalue()
'hello\n'