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