summaryrefslogtreecommitdiff
path: root/pypers/recipes/doct0.py
diff options
context:
space:
mode:
Diffstat (limited to 'pypers/recipes/doct0.py')
-rwxr-xr-xpypers/recipes/doct0.py94
1 files changed, 94 insertions, 0 deletions
diff --git a/pypers/recipes/doct0.py b/pypers/recipes/doct0.py
new file mode 100755
index 0000000..95d6b36
--- /dev/null
+++ b/pypers/recipes/doct0.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python2.4
+# Author: michele.simionato@gmail.com
+"""Run doctest on text files. Examples of usage:
+
+$ doct file.txt # run the tests in file.txt
+$ doct -v file.txt # run the tests in verbose mode
+$ doct -u file.txt # run the tests as unittests
+$ doct directory # run all the tests in all the text files into directory.
+
+For simplicity, this version is not recursive.
+"""
+
+import os, sys, doctest, time, textwrap, re, types, unittest
+
+# regular expressions to identify code blocks of the form
+#<scriptname.py>
+#...
+#</scriptname.py>
+DOTNAME = r'\b[a-zA-Z_][\w\.]*', # identifier with or without dots
+SCRIPT = re.compile(r'(?s)#<(%s)>(.*?)#</\1>' % DOTNAME)
+
+# a simple utility to extract code blocks
+def extractscript(txt):
+ for MO in SCRIPT.finditer(txt):
+ yield MO.group(1), textwrap.dedent(MO.group(2))
+
+class TestRunner(object):
+ def __init__(self, verbose=False, unitest=False):
+ self.verbose = verbose
+ self.unitest = unitest
+ if unitest:
+ self.DocTestSuite = doctest.DocTestSuite
+ self.unitTestRunner = unittest.TextTestRunner(verbosity=verbose)
+ else:
+ self.docTestParser = doctest.DocTestParser()
+ self.docTestRunner = doctest.DocTestRunner(verbose=verbose)
+
+ def utest(self, fname, txt):
+ """Converts doctests to unittests."""
+ # works by dynamically generating a module with a suitable docstring
+ suite = self.DocTestSuite(types.ModuleType(fname, txt))
+ self.unitTestRunner.run(suite)
+ return ''
+
+ def dtest(self, fname, txt):
+ """Making dtest similar to utest."""
+ dt = self.docTestParser.get_doctest(txt, {}, fname, fname, 0)
+ t0 = time.clock()
+ failed, total = self.docTestRunner.run(dt)
+ if failed:
+ return ''
+ else:
+ return "%s: %s tests passed in %s seconds\n" % (
+ fname, total, time.clock() - t0)
+
+ def run(self, txt='', fname=''):
+ if fname:
+ txt += file(fname, 'U').read()
+ else:
+ fname = '<current-buffer>'
+ scriptnames = []; scriptdict = {}
+ for scriptname, script in extractscript(txt): # read scripts
+ if scriptname not in scriptnames:
+ scriptdict[scriptname] = script
+ scriptnames.append(scriptname)
+ else:
+ scriptdict[scriptname] += script
+ for scriptname in scriptnames: # save scripts
+ code = '# ' + scriptname + scriptdict[scriptname]
+ print >> file(scriptname, 'w'), code
+ if self.unitest:
+ return self.utest(fname, txt)
+ else:
+ return self.dtest(fname, txt)
+
+if __name__=='__main__':
+ # strip the option arguments
+ args = [arg for arg in sys.argv[1:] if not arg.startswith("-")]
+ if not args: # print usage message
+ print __doc__
+ if len(args) > 1:
+ print "Too many args"
+ else: # run
+ arg = args[0]
+ verbose = "-v" in sys.argv
+ unitest = "-u" in sys.argv
+ if os.path.isdir(arg):
+ for fname in os.listdir(arg):
+ f = os.path.join(arg, fname)
+ print TestRunner(verbose, unitest).run(fname=f)
+ elif os.path.isfile(arg):
+ print TestRunner(verbose, unitest).run(fname=args[0])
+ else:
+ raise IOError("File %s not found" % arg)