Skip to content
Snippets Groups Projects
Commit d56da32e authored by Martyn Gigg's avatar Martyn Gigg
Browse files

Merge pull request #13311 from mantidproject/feature/13310_Doctests_for_Python_Introduction

Doctests for training courses
parents 44e5d935 5b411d5b
No related branches found
No related tags found
No related merge requests found
Showing
with 192 additions and 11 deletions
File added
Code/Mantid/Images/Mantid_Logo_Forum.png

17.7 KiB

ac963bf816b36ca7353f6922ce856604
01acf5f7645c9901efece0ed8dd00ced
6df0f1c2fc472af200eec43762e9a874
b6f6a905288dd986b5340673d08f93cb
435f4d573bfe6e64b014223d793e02a2
602cbf8646a81b25e221bba3fe691a93
22402246b0d14072b1fd9e6d920c3bb6
3541bd5715fb57d8cdf884c500d8e485
a3d0edcb36ab8e9e3342cd8a4440b779
3950f6d890c10dd67c320cab6054d3d8
fb38e6a49b8d90ad5cbe7ad5674ab09d
7906ac610fe7805b872dc47f19164a91
9ae79fe97b23970e6977217254207c60
...@@ -126,6 +126,7 @@ to alter and those properties are detailed below. ...@@ -126,6 +126,7 @@ to alter and those properties are detailed below.
+-------------------------------------------+---------------------------------------------------+-----------------------+ +-------------------------------------------+---------------------------------------------------+-----------------------+
|logging.channels.fileChannel.path | The Path to the log file. |../logs/mantid.log | |logging.channels.fileChannel.path | The Path to the log file. |../logs/mantid.log |
+-------------------------------------------+---------------------------------------------------+-----------------------+ +-------------------------------------------+---------------------------------------------------+-----------------------+
The logging priority levels for the file logging and console logging can also be adjusted in python using the commands: The logging priority levels for the file logging and console logging can also be adjusted in python using the commands:
.. testcode:: LoggingConfigExample .. testcode:: LoggingConfigExample
...@@ -135,8 +136,6 @@ The logging priority levels for the file logging and console logging can also be ...@@ -135,8 +136,6 @@ The logging priority levels for the file logging and console logging can also be
#Set the file to only log at critical level (2=critical) #Set the file to only log at critical level (2=critical)
ConfigService.setConsoleLogLevel(2) ConfigService.setConsoleLogLevel(2)
.. testoutput:: AddSampleLogExample
MantidPlot Properties MantidPlot Properties
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
Document: bar/FooDoc Document: bar/FooDoc
-------------------- --------------------
********************************************************************** **********************************************************************
File "bar/FooDoc.rst", line 127, in Ex2 File "bar/FooDoc.rst", line 127, in Ex2[31]
Failed example: Failed example:
print "Multi-line failed" print "Multi-line failed"
print "test" print "test"
...@@ -62,13 +62,13 @@ ...@@ -62,13 +62,13 @@
********************************************************************** **********************************************************************
2 items had failures: 2 items had failures:
1 of 1 in Ex1 1 of 1 in Ex1
1 of 1 in Ex2 1 of 1 in Ex2[31]
2 tests in 2 items. 2 tests in 2 items.
0 passed and 2 failed. 0 passed and 2 failed.
***Test Failed*** 2 failures. ***Test Failed*** 2 failures.
2 items passed all tests: 2 items passed all tests:
1 tests in Ex1 (cleanup code) 1 tests in Ex1 (cleanup code)
1 tests in Ex2 (cleanup code) 1 tests in Ex2[31] (cleanup code)
2 tests in 2 items. 2 tests in 2 items.
2 passed and 0 failed. 2 passed and 0 failed.
Test passed. Test passed.
...@@ -152,7 +152,7 @@ NUMBER_PASSED_RE = re.compile(r"^(\d+) items passed all tests:$") ...@@ -152,7 +152,7 @@ NUMBER_PASSED_RE = re.compile(r"^(\d+) items passed all tests:$")
TEST_PASSED_END_RE = re.compile(r"Test passed.") TEST_PASSED_END_RE = re.compile(r"Test passed.")
TEST_FAILED_END_RE = re.compile(r"\*\*\*Test Failed\*\*\* (\d+) failures.") TEST_FAILED_END_RE = re.compile(r"\*\*\*Test Failed\*\*\* (\d+) failures.")
FAILURE_LOC_RE = re.compile(r"^File \"(.+)\",\s+line\s+(\d+),\s+in\s+(\w+)(\s\((setup|cleanup) code\))?$") FAILURE_LOC_RE = re.compile(r"^File \"(.+)\",\s+line\s+(\d+),\s+in\s+(\S+)(\s\((setup|cleanup) code\))?$")
MIX_FAIL_RE = re.compile(r'^\s+(\d+)\s+of\s+(\d+)\s+in\s+(\w+)$') MIX_FAIL_RE = re.compile(r'^\s+(\d+)\s+of\s+(\d+)\s+in\s+(\w+)$')
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
......
...@@ -149,7 +149,7 @@ ALL_FAIL_EX = \ ...@@ -149,7 +149,7 @@ ALL_FAIL_EX = \
"""Document: algorithms/AllFailed """Document: algorithms/AllFailed
------------------------------ ------------------------------
********************************************************************** **********************************************************************
File "algorithms/AllFailed.rst", line 127, in Ex2 File "algorithms/AllFailed.rst", line 127, in Ex2[31]
Failed example: Failed example:
print "Multi-line failed" print "Multi-line failed"
print "test" print "test"
...@@ -169,13 +169,13 @@ Got: ...@@ -169,13 +169,13 @@ Got:
********************************************************************** **********************************************************************
2 items had failures: 2 items had failures:
1 of 1 in Ex1 1 of 1 in Ex1
1 of 1 in Ex2 1 of 1 in Ex2[31]
2 tests in 2 items. 2 tests in 2 items.
0 passed and 2 failed. 0 passed and 2 failed.
***Test Failed*** 2 failures. ***Test Failed*** 2 failures.
2 items passed all tests: 2 items passed all tests:
1 tests in Ex1 (cleanup code) 1 tests in Ex1 (cleanup code)
1 tests in Ex2 (cleanup code) 1 tests in Ex2[31] (cleanup code)
2 tests in 2 items. 2 tests in 2 items.
2 passed and 0 failed. 2 passed and 0 failed.
Test passed. Test passed.
...@@ -275,9 +275,9 @@ class DocTestOutputParserTest(unittest.TestCase): ...@@ -275,9 +275,9 @@ class DocTestOutputParserTest(unittest.TestCase):
self.assertEquals(2, suite.ntests) self.assertEquals(2, suite.ntests)
cases = suite.testcases cases = suite.testcases
expected_names = ["Ex2", "Ex1"] expected_names = ["Ex2[31]", "Ex1"]
expected_errors = [ expected_errors = [
"""File "algorithms/AllFailed.rst", line 127, in Ex2 """File "algorithms/AllFailed.rst", line 127, in Ex2[31]
Failed example: Failed example:
print "Multi-line failed" print "Multi-line failed"
print "test" print "test"
......
###########################################################################################
# Extracts code blocks marked as python from mediawiki pages and checks they run
# run with -h for command line arguments
#
##########################################################################################
import os
import re
import sys
import urllib2
import urlparse
import argparse
import json
def readWebPage(url):
# set your environment HTTP_PROXY to be your proxy
# for ral HTTP_PROXY=http://wwwcache.rl.ac.uk:8080
aResp = urllib2.urlopen(url)
web_pg = aResp.read()
return web_pg
def getTestablePages(url):
retList = []
print "Reading Testable pages from " + url
output = readWebPage(url)
jsonObj = json.loads(output)
for member in jsonObj['query']['categorymembers']:
retList.append(member['title'])
return retList
def convertURLToRaw(url):
return url.replace(" ","%20") + "?action=raw"
def writeTestRst(filepath,mediawikiText,pageName):
'''
for a block of wiki text, writes out all tests found to an rst page
:param mediawikiText: mediawiki text
:param filepath: the filepath to write out the rst file to, if None then print to stdout
:return: None
'''
try:
if filepath is not None:
sys.stdout = open(filepath, 'w')
print ":orphan:\n"
findCodeSections(mediawikiText,pageName)
finally:
sys.stdout = sys.__stdout__
def findCodeSections(mediawikiText,pageName):
'''
Finds code sections that look like this:
#<div style="border:1pt dashed blue; background:#f9f9f9;padding: 1em 0;">
# <!--skip-->
# <!-- setup
# x=1
# y=2
# file.open()
# -->
# <source lang="python">
# x = 'The meaning of life is ... '
# answer = 42
# y = x + str(answer) # This converts the number 5 to a string and joins
# # it with the first string and then assigns y to a
# # new string containing the concatenated string
# </source>
# <!-- teardown
# print y # print statements can be in the teardown if you do not want them visible
# file.close()
# -->
# <!-- result
# the lazy dog
# -->
# </div>
'''
wikiTestPattern = re.compile(
r'''(<!--\s*skip\s*-->)?\s* # optional skip section (group 1)
(<!--\s*testsetup\s*(.*?)\s*-->)?\s* # optional setup section (group 2), just setup code (group 3)
<source.*?lang="python".*?>\s*(.*?)\s*<\/source>\s* # mandatory source section, code (group 4)
(<!--\s*testcleanup\s*(.*?)\s*-->)?\s* # optional teardown section (group 5), code (group 6)
(<!--\s+testoutput\s*(.*?)\s*-->)? # optional result section (group 7), text group (8)
''', re.IGNORECASE + re.DOTALL + re.VERBOSE)
for m in re.finditer(wikiTestPattern, mediawikiText):
(line,col) = coords_of_str_index(mediawikiText, m.start(0))
testName = pageName + "[" + str(line) + "]"
#print the test
if m.group(1) is not None:
print ".. Skipping Test ", testName
print
else:
printDirective("testsetup",testName,m.group(3),True)
printDirective("testcode",testName,m.group(4),False)
printDirective("testcleanup",testName,m.group(6),True)
printDirective("testoutput",testName,m.group(8),True, "+ELLIPSIS, +NORMALIZE_WHITESPACE")
print
def printDirective (directive, name, contents, hideIfNone = False,options = None):
if not(hideIfNone and contents is None):
print ".. {}:: {}".format(directive,name)
if options is not None:
print " :options: {}".format(options)
print
if contents is not None:
for line in contents.split("\n"):
print " " + line
print
def coords_of_str_index(string, index):
"""Get (line_number, col) of `index` in `string`."""
lines = string.splitlines(True)
curr_pos = 0
for linenum, line in enumerate(lines):
if curr_pos + len(line) > index:
return linenum + 1, index-curr_pos
curr_pos += len(line)
def ensureDirectoriesExist(path):
try:
os.makedirs(path)
except OSError:
pass
################################################################################################################
parser = argparse.ArgumentParser(description='Extracts code blocks marked as python from mediawiki pages and tests they run.')
parser.add_argument('-i', '--i',
help='The title of a mediawiki page, if not specified it will search the Tested Examples category')
parser.add_argument('-s', '--s',
help='The url of a mantid mediawiki site')
parser.add_argument('-o', '--o',
help='Provide a path to output to an output directory')
args = parser.parse_args()
urlList = [];
baseUrl = "http://www.mantidproject.org/"
if args.s is not None:
baseUrl = args.s
if args.i is None:
urlList = getTestablePages(baseUrl +
"api.php?action=query&list=categorymembers&format=json&cmlimit=500&cmtitle=Category:Tested%20Examples")
else:
urlList.append(args.i)
outputDir = None
if args.o is not None:
ensureDirectoriesExist(args.o)
if not os.path.isdir(args.o):
print "Output directory not found", args.o
print "Output will be to stdout"
else:
outputDir = args.o
for url in urlList:
pageName = "mwTest_" + url.replace(" ","_").replace(":","")
print "Parsing ", pageName,
outputFile = None
if outputDir is not None:
outputFile = os.path.join(outputDir, pageName+".rst")
print "->", outputFile
#run pandoc and get the output in rst
mediawikiText = readWebPage(convertURLToRaw(baseUrl + url))
writeTestRst(outputFile,mediawikiText,pageName)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment