##// END OF EJS Templates
mis renamed varaible
Matthias Bussonnier -
Show More
@@ -1,186 +1,186 b''
1 1 """Experimental code for cleaner support of IPython syntax with unittest.
2 2
3 3 In IPython up until 0.10, we've used very hacked up nose machinery for running
4 4 tests with IPython special syntax, and this has proved to be extremely slow.
5 5 This module provides decorators to try a different approach, stemming from a
6 6 conversation Brian and I (FP) had about this problem Sept/09.
7 7
8 8 The goal is to be able to easily write simple functions that can be seen by
9 9 unittest as tests, and ultimately for these to support doctests with full
10 10 IPython syntax. Nose already offers this based on naming conventions and our
11 11 hackish plugins, but we are seeking to move away from nose dependencies if
12 12 possible.
13 13
14 14 This module follows a different approach, based on decorators.
15 15
16 16 - A decorator called @ipdoctest can mark any function as having a docstring
17 17 that should be viewed as a doctest, but after syntax conversion.
18 18
19 19 Authors
20 20 -------
21 21
22 22 - Fernando Perez <Fernando.Perez@berkeley.edu>
23 23 """
24 24
25 25
26 26 #-----------------------------------------------------------------------------
27 27 # Copyright (C) 2009-2011 The IPython Development Team
28 28 #
29 29 # Distributed under the terms of the BSD License. The full license is in
30 30 # the file COPYING, distributed as part of this software.
31 31 #-----------------------------------------------------------------------------
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Imports
35 35 #-----------------------------------------------------------------------------
36 36
37 37 # Stdlib
38 38 import re
39 39 import sys
40 40 import unittest
41 41 from doctest import DocTestFinder, DocTestRunner, TestResults
42 42 from IPython.terminal.interactiveshell import InteractiveShell
43 43
44 44 #-----------------------------------------------------------------------------
45 45 # Classes and functions
46 46 #-----------------------------------------------------------------------------
47 47
48 48 def count_failures(runner):
49 49 """Count number of failures in a doctest runner.
50 50
51 51 Code modeled after the summarize() method in doctest.
52 52 """
53 53 if sys.version_info < (3, 13):
54 54 return [TestResults(f, t) for f, t in runner._name2ft.values() if f > 0]
55 55 else:
56 56 return [
57 57 TestResults(failure, try_)
58 58 for failure, try_, skip in runner._stats.values()
59 if f > 0
59 if failure > 0
60 60 ]
61 61
62 62
63 63 class IPython2PythonConverter(object):
64 64 """Convert IPython 'syntax' to valid Python.
65 65
66 66 Eventually this code may grow to be the full IPython syntax conversion
67 67 implementation, but for now it only does prompt conversion."""
68 68
69 69 def __init__(self):
70 70 self.rps1 = re.compile(r'In\ \[\d+\]: ')
71 71 self.rps2 = re.compile(r'\ \ \ \.\.\.+: ')
72 72 self.rout = re.compile(r'Out\[\d+\]: \s*?\n?')
73 73 self.pyps1 = '>>> '
74 74 self.pyps2 = '... '
75 75 self.rpyps1 = re.compile (r'(\s*%s)(.*)$' % self.pyps1)
76 76 self.rpyps2 = re.compile (r'(\s*%s)(.*)$' % self.pyps2)
77 77
78 78 def __call__(self, ds):
79 79 """Convert IPython prompts to python ones in a string."""
80 80 from . import globalipapp
81 81
82 82 pyps1 = '>>> '
83 83 pyps2 = '... '
84 84 pyout = ''
85 85
86 86 dnew = ds
87 87 dnew = self.rps1.sub(pyps1, dnew)
88 88 dnew = self.rps2.sub(pyps2, dnew)
89 89 dnew = self.rout.sub(pyout, dnew)
90 90 ip = InteractiveShell.instance()
91 91
92 92 # Convert input IPython source into valid Python.
93 93 out = []
94 94 newline = out.append
95 95 for line in dnew.splitlines():
96 96
97 97 mps1 = self.rpyps1.match(line)
98 98 if mps1 is not None:
99 99 prompt, text = mps1.groups()
100 100 newline(prompt+ip.prefilter(text, False))
101 101 continue
102 102
103 103 mps2 = self.rpyps2.match(line)
104 104 if mps2 is not None:
105 105 prompt, text = mps2.groups()
106 106 newline(prompt+ip.prefilter(text, True))
107 107 continue
108 108
109 109 newline(line)
110 110 newline('') # ensure a closing newline, needed by doctest
111 111 #print "PYSRC:", '\n'.join(out) # dbg
112 112 return '\n'.join(out)
113 113
114 114 #return dnew
115 115
116 116
117 117 class Doc2UnitTester(object):
118 118 """Class whose instances act as a decorator for docstring testing.
119 119
120 120 In practice we're only likely to need one instance ever, made below (though
121 121 no attempt is made at turning it into a singleton, there is no need for
122 122 that).
123 123 """
124 124 def __init__(self, verbose=False):
125 125 """New decorator.
126 126
127 127 Parameters
128 128 ----------
129 129
130 130 verbose : boolean, optional (False)
131 131 Passed to the doctest finder and runner to control verbosity.
132 132 """
133 133 self.verbose = verbose
134 134 # We can reuse the same finder for all instances
135 135 self.finder = DocTestFinder(verbose=verbose, recurse=False)
136 136
137 137 def __call__(self, func):
138 138 """Use as a decorator: doctest a function's docstring as a unittest.
139 139
140 140 This version runs normal doctests, but the idea is to make it later run
141 141 ipython syntax instead."""
142 142
143 143 # Capture the enclosing instance with a different name, so the new
144 144 # class below can see it without confusion regarding its own 'self'
145 145 # that will point to the test instance at runtime
146 146 d2u = self
147 147
148 148 # Rewrite the function's docstring to have python syntax
149 149 if func.__doc__ is not None:
150 150 func.__doc__ = ip2py(func.__doc__)
151 151
152 152 # Now, create a tester object that is a real unittest instance, so
153 153 # normal unittest machinery (or Nose, or Trial) can find it.
154 154 class Tester(unittest.TestCase):
155 155 def test(self):
156 156 # Make a new runner per function to be tested
157 157 runner = DocTestRunner(verbose=d2u.verbose)
158 158 for the_test in d2u.finder.find(func, func.__name__):
159 159 runner.run(the_test)
160 160 failed = count_failures(runner)
161 161 if failed:
162 162 # Since we only looked at a single function's docstring,
163 163 # failed should contain at most one item. More than that
164 164 # is a case we can't handle and should error out on
165 165 if len(failed) > 1:
166 166 err = "Invalid number of test results: %s" % failed
167 167 raise ValueError(err)
168 168 # Report a normal failure.
169 169 self.fail('failed doctests: %s' % str(failed[0]))
170 170
171 171 # Rename it so test reports have the original signature.
172 172 Tester.__name__ = func.__name__
173 173 return Tester
174 174
175 175
176 176 def ipdocstring(func):
177 177 """Change the function docstring via ip2py.
178 178 """
179 179 if func.__doc__ is not None:
180 180 func.__doc__ = ip2py(func.__doc__)
181 181 return func
182 182
183 183
184 184 # Make an instance of the classes for public use
185 185 ipdoctest = Doc2UnitTester()
186 186 ip2py = IPython2PythonConverter()
General Comments 0
You need to be logged in to leave comments. Login now