##// END OF EJS Templates
Apply suggestions from code review
M Bussonnier -
Show More
@@ -1,34 +1,34 b''
1 1 """Simple script to be run *twice*, to check reference counting bugs.
2 2
3 3 See test_run for details."""
4 4
5 5
6 6 import sys
7 7
8 8 # We want to ensure that while objects remain available for immediate access,
9 9 # objects from *previous* runs of the same script get collected, to avoid
10 10 # accumulating massive amounts of old references.
11 11 class C(object):
12 12 def __init__(self,name):
13 13 self.name = name
14 14 self.p = print
15 15 self.flush_stdout = sys.stdout.flush
16 16
17 17 def __del__(self):
18 18 self.p('tclass.py: deleting object:',self.name)
19 19 self.flush_stdout()
20 20
21 21 try:
22 22 name = sys.argv[1]
23 23 except IndexError:
24 24 pass
25 25 else:
26 26 if name.startswith('C'):
27 27 c = C(name)
28 28
29 # print(>> sys.stderr, "ARGV:", sys.argv) # dbg
29 # print("ARGV:", sys.argv, file=sys.stderr) # dbg
30 30
31 31 # This next print statement is NOT debugging, we're making the check on a
32 32 # completely separate process so we verify by capturing stdout:
33 33 print('ARGV 1-:', sys.argv[1:])
34 34 sys.stdout.flush()
@@ -1,299 +1,299 b''
1 1 """Nose Plugin that supports IPython doctests.
2 2
3 3 Limitations:
4 4
5 5 - When generating examples for use as doctests, make sure that you have
6 6 pretty-printing OFF. This can be done either by setting the
7 7 ``PlainTextFormatter.pprint`` option in your configuration file to False, or
8 8 by interactively disabling it with %Pprint. This is required so that IPython
9 9 output matches that of normal Python, which is used by doctest for internal
10 10 execution.
11 11
12 12 - Do not rely on specific prompt numbers for results (such as using
13 13 '_34==True', for example). For IPython tests run via an external process the
14 14 prompt numbers may be different, and IPython tests run as normal python code
15 15 won't even have these special _NN variables set at all.
16 16 """
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Module imports
20 20
21 21 # From the standard library
22 22 import doctest
23 23 import logging
24 24 import re
25 25
26 26 from testpath import modified_env
27 27
28 28 #-----------------------------------------------------------------------------
29 29 # Module globals and other constants
30 30 #-----------------------------------------------------------------------------
31 31
32 32 log = logging.getLogger(__name__)
33 33
34 34
35 35 #-----------------------------------------------------------------------------
36 36 # Classes and functions
37 37 #-----------------------------------------------------------------------------
38 38
39 39
40 40 class DocTestFinder(doctest.DocTestFinder):
41 41 def _get_test(self, obj, name, module, globs, source_lines):
42 42 test = super()._get_test(obj, name, module, globs, source_lines)
43 43
44 44 if bool(getattr(obj, "__skip_doctest__", False)) and test is not None:
45 45 for example in test.examples:
46 46 example.options[doctest.SKIP] = True
47 47
48 48 return test
49 49
50 50
51 51 class IPDoctestOutputChecker(doctest.OutputChecker):
52 52 """Second-chance checker with support for random tests.
53 53
54 54 If the default comparison doesn't pass, this checker looks in the expected
55 55 output string for flags that tell us to ignore the output.
56 56 """
57 57
58 58 random_re = re.compile(r'#\s*random\s+')
59 59
60 60 def check_output(self, want, got, optionflags):
61 61 """Check output, accepting special markers embedded in the output.
62 62
63 63 If the output didn't pass the default validation but the special string
64 64 '#random' is included, we accept it."""
65 65
66 66 # Let the original tester verify first, in case people have valid tests
67 67 # that happen to have a comment saying '#random' embedded in.
68 68 ret = doctest.OutputChecker.check_output(self, want, got,
69 69 optionflags)
70 70 if not ret and self.random_re.search(want):
71 # print(>> sys.stderr, 'RANDOM OK:',want) # dbg
71 # print('RANDOM OK:',want, file=sys.stderr) # dbg
72 72 return True
73 73
74 74 return ret
75 75
76 76
77 77 # A simple subclassing of the original with a different class name, so we can
78 78 # distinguish and treat differently IPython examples from pure python ones.
79 79 class IPExample(doctest.Example): pass
80 80
81 81
82 82 class IPDocTestParser(doctest.DocTestParser):
83 83 """
84 84 A class used to parse strings containing doctest examples.
85 85
86 86 Note: This is a version modified to properly recognize IPython input and
87 87 convert any IPython examples into valid Python ones.
88 88 """
89 89 # This regular expression is used to find doctest examples in a
90 90 # string. It defines three groups: `source` is the source code
91 91 # (including leading indentation and prompts); `indent` is the
92 92 # indentation of the first (PS1) line of the source code; and
93 93 # `want` is the expected output (including leading indentation).
94 94
95 95 # Classic Python prompts or default IPython ones
96 96 _PS1_PY = r'>>>'
97 97 _PS2_PY = r'\.\.\.'
98 98
99 99 _PS1_IP = r'In\ \[\d+\]:'
100 100 _PS2_IP = r'\ \ \ \.\.\.+:'
101 101
102 102 _RE_TPL = r'''
103 103 # Source consists of a PS1 line followed by zero or more PS2 lines.
104 104 (?P<source>
105 105 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
106 106 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
107 107 \n? # a newline
108 108 # Want consists of any non-blank lines that do not start with PS1.
109 109 (?P<want> (?:(?![ ]*$) # Not a blank line
110 110 (?![ ]*%s) # Not a line starting with PS1
111 111 (?![ ]*%s) # Not a line starting with PS2
112 112 .*$\n? # But any other line
113 113 )*)
114 114 '''
115 115
116 116 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
117 117 re.MULTILINE | re.VERBOSE)
118 118
119 119 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
120 120 re.MULTILINE | re.VERBOSE)
121 121
122 122 # Mark a test as being fully random. In this case, we simply append the
123 123 # random marker ('#random') to each individual example's output. This way
124 124 # we don't need to modify any other code.
125 125 _RANDOM_TEST = re.compile(r'#\s*all-random\s+')
126 126
127 127 def ip2py(self,source):
128 128 """Convert input IPython source into valid Python."""
129 129 block = _ip.input_transformer_manager.transform_cell(source)
130 130 if len(block.splitlines()) == 1:
131 131 return _ip.prefilter(block)
132 132 else:
133 133 return block
134 134
135 135 def parse(self, string, name='<string>'):
136 136 """
137 137 Divide the given string into examples and intervening text,
138 138 and return them as a list of alternating Examples and strings.
139 139 Line numbers for the Examples are 0-based. The optional
140 140 argument `name` is a name identifying this string, and is only
141 141 used for error messages.
142 142 """
143 143
144 144 # print('Parse string:\n',string) # dbg
145 145
146 146 string = string.expandtabs()
147 147 # If all lines begin with the same indentation, then strip it.
148 148 min_indent = self._min_indent(string)
149 149 if min_indent > 0:
150 150 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
151 151
152 152 output = []
153 153 charno, lineno = 0, 0
154 154
155 155 # We make 'all random' tests by adding the '# random' mark to every
156 156 # block of output in the test.
157 157 if self._RANDOM_TEST.search(string):
158 158 random_marker = '\n# random'
159 159 else:
160 160 random_marker = ''
161 161
162 162 # Whether to convert the input from ipython to python syntax
163 163 ip2py = False
164 164 # Find all doctest examples in the string. First, try them as Python
165 165 # examples, then as IPython ones
166 166 terms = list(self._EXAMPLE_RE_PY.finditer(string))
167 167 if terms:
168 168 # Normal Python example
169 169 Example = doctest.Example
170 170 else:
171 171 # It's an ipython example.
172 172 terms = list(self._EXAMPLE_RE_IP.finditer(string))
173 173 Example = IPExample
174 174 ip2py = True
175 175
176 176 for m in terms:
177 177 # Add the pre-example text to `output`.
178 178 output.append(string[charno:m.start()])
179 179 # Update lineno (lines before this example)
180 180 lineno += string.count('\n', charno, m.start())
181 181 # Extract info from the regexp match.
182 182 (source, options, want, exc_msg) = \
183 183 self._parse_example(m, name, lineno,ip2py)
184 184
185 185 # Append the random-output marker (it defaults to empty in most
186 186 # cases, it's only non-empty for 'all-random' tests):
187 187 want += random_marker
188 188
189 189 # Create an Example, and add it to the list.
190 190 if not self._IS_BLANK_OR_COMMENT(source):
191 191 output.append(Example(source, want, exc_msg,
192 192 lineno=lineno,
193 193 indent=min_indent+len(m.group('indent')),
194 194 options=options))
195 195 # Update lineno (lines inside this example)
196 196 lineno += string.count('\n', m.start(), m.end())
197 197 # Update charno.
198 198 charno = m.end()
199 199 # Add any remaining post-example text to `output`.
200 200 output.append(string[charno:])
201 201 return output
202 202
203 203 def _parse_example(self, m, name, lineno,ip2py=False):
204 204 """
205 205 Given a regular expression match from `_EXAMPLE_RE` (`m`),
206 206 return a pair `(source, want)`, where `source` is the matched
207 207 example's source code (with prompts and indentation stripped);
208 208 and `want` is the example's expected output (with indentation
209 209 stripped).
210 210
211 211 `name` is the string's name, and `lineno` is the line number
212 212 where the example starts; both are used for error messages.
213 213
214 214 Optional:
215 215 `ip2py`: if true, filter the input via IPython to convert the syntax
216 216 into valid python.
217 217 """
218 218
219 219 # Get the example's indentation level.
220 220 indent = len(m.group('indent'))
221 221
222 222 # Divide source into lines; check that they're properly
223 223 # indented; and then strip their indentation & prompts.
224 224 source_lines = m.group('source').split('\n')
225 225
226 226 # We're using variable-length input prompts
227 227 ps1 = m.group('ps1')
228 228 ps2 = m.group('ps2')
229 229 ps1_len = len(ps1)
230 230
231 231 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
232 232 if ps2:
233 233 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
234 234
235 235 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
236 236
237 237 if ip2py:
238 238 # Convert source input from IPython into valid Python syntax
239 239 source = self.ip2py(source)
240 240
241 241 # Divide want into lines; check that it's properly indented; and
242 242 # then strip the indentation. Spaces before the last newline should
243 243 # be preserved, so plain rstrip() isn't good enough.
244 244 want = m.group('want')
245 245 want_lines = want.split('\n')
246 246 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
247 247 del want_lines[-1] # forget final newline & spaces after it
248 248 self._check_prefix(want_lines, ' '*indent, name,
249 249 lineno + len(source_lines))
250 250
251 251 # Remove ipython output prompt that might be present in the first line
252 252 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
253 253
254 254 want = '\n'.join([wl[indent:] for wl in want_lines])
255 255
256 256 # If `want` contains a traceback message, then extract it.
257 257 m = self._EXCEPTION_RE.match(want)
258 258 if m:
259 259 exc_msg = m.group('msg')
260 260 else:
261 261 exc_msg = None
262 262
263 263 # Extract options from the source.
264 264 options = self._find_options(source, name, lineno)
265 265
266 266 return source, options, want, exc_msg
267 267
268 268 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
269 269 """
270 270 Given the lines of a source string (including prompts and
271 271 leading indentation), check to make sure that every prompt is
272 272 followed by a space character. If any line is not followed by
273 273 a space character, then raise ValueError.
274 274
275 275 Note: IPython-modified version which takes the input prompt length as a
276 276 parameter, so that prompts of variable length can be dealt with.
277 277 """
278 278 space_idx = indent+ps1_len
279 279 min_len = space_idx+1
280 280 for i, line in enumerate(lines):
281 281 if len(line) >= min_len and line[space_idx] != ' ':
282 282 raise ValueError('line %r of the docstring for %s '
283 283 'lacks blank after %s: %r' %
284 284 (lineno+i+1, name,
285 285 line[indent:space_idx], line))
286 286
287 287
288 288 SKIP = doctest.register_optionflag('SKIP')
289 289
290 290
291 291 class IPDocTestRunner(doctest.DocTestRunner,object):
292 292 """Test runner that synchronizes the IPython namespace with test globals.
293 293 """
294 294
295 295 def run(self, test, compileflags=None, out=None, clear_globs=True):
296 296 # Override terminal size to standardise traceback format
297 297 with modified_env({'COLUMNS': '80', 'LINES': '24'}):
298 298 return super(IPDocTestRunner,self).run(test,
299 299 compileflags,out,clear_globs)
General Comments 0
You need to be logged in to leave comments. Login now