Mercurial > hg > vamp-build-and-test
comparison DEPENDENCIES/mingw32/Python27/Lib/site-packages/numpy/testing/noseclasses.py @ 87:2a2c65a20a8b
Add Python libs and headers
author | Chris Cannam |
---|---|
date | Wed, 25 Feb 2015 14:05:22 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
86:413a9d26189e | 87:2a2c65a20a8b |
---|---|
1 # These classes implement a doctest runner plugin for nose, a "known failure" | |
2 # error class, and a customized TestProgram for NumPy. | |
3 | |
4 # Because this module imports nose directly, it should not | |
5 # be used except by nosetester.py to avoid a general NumPy | |
6 # dependency on nose. | |
7 from __future__ import division, absolute_import, print_function | |
8 | |
9 import os | |
10 import doctest | |
11 | |
12 import nose | |
13 from nose.plugins import doctests as npd | |
14 from nose.plugins.errorclass import ErrorClass, ErrorClassPlugin | |
15 from nose.plugins.base import Plugin | |
16 from nose.util import src | |
17 import numpy | |
18 from .nosetester import get_package_name | |
19 import inspect | |
20 | |
21 # Some of the classes in this module begin with 'Numpy' to clearly distinguish | |
22 # them from the plethora of very similar names from nose/unittest/doctest | |
23 | |
24 #----------------------------------------------------------------------------- | |
25 # Modified version of the one in the stdlib, that fixes a python bug (doctests | |
26 # not found in extension modules, http://bugs.python.org/issue3158) | |
27 class NumpyDocTestFinder(doctest.DocTestFinder): | |
28 | |
29 def _from_module(self, module, object): | |
30 """ | |
31 Return true if the given object is defined in the given | |
32 module. | |
33 """ | |
34 if module is None: | |
35 #print '_fm C1' # dbg | |
36 return True | |
37 elif inspect.isfunction(object): | |
38 #print '_fm C2' # dbg | |
39 return module.__dict__ is object.__globals__ | |
40 elif inspect.isbuiltin(object): | |
41 #print '_fm C2-1' # dbg | |
42 return module.__name__ == object.__module__ | |
43 elif inspect.isclass(object): | |
44 #print '_fm C3' # dbg | |
45 return module.__name__ == object.__module__ | |
46 elif inspect.ismethod(object): | |
47 # This one may be a bug in cython that fails to correctly set the | |
48 # __module__ attribute of methods, but since the same error is easy | |
49 # to make by extension code writers, having this safety in place | |
50 # isn't such a bad idea | |
51 #print '_fm C3-1' # dbg | |
52 return module.__name__ == object.__self__.__class__.__module__ | |
53 elif inspect.getmodule(object) is not None: | |
54 #print '_fm C4' # dbg | |
55 #print 'C4 mod',module,'obj',object # dbg | |
56 return module is inspect.getmodule(object) | |
57 elif hasattr(object, '__module__'): | |
58 #print '_fm C5' # dbg | |
59 return module.__name__ == object.__module__ | |
60 elif isinstance(object, property): | |
61 #print '_fm C6' # dbg | |
62 return True # [XX] no way not be sure. | |
63 else: | |
64 raise ValueError("object must be a class or function") | |
65 | |
66 def _find(self, tests, obj, name, module, source_lines, globs, seen): | |
67 """ | |
68 Find tests for the given object and any contained objects, and | |
69 add them to `tests`. | |
70 """ | |
71 | |
72 doctest.DocTestFinder._find(self, tests, obj, name, module, | |
73 source_lines, globs, seen) | |
74 | |
75 # Below we re-run pieces of the above method with manual modifications, | |
76 # because the original code is buggy and fails to correctly identify | |
77 # doctests in extension modules. | |
78 | |
79 # Local shorthands | |
80 from inspect import isroutine, isclass, ismodule, isfunction, \ | |
81 ismethod | |
82 | |
83 # Look for tests in a module's contained objects. | |
84 if ismodule(obj) and self._recurse: | |
85 for valname, val in obj.__dict__.items(): | |
86 valname1 = '%s.%s' % (name, valname) | |
87 if ( (isroutine(val) or isclass(val)) | |
88 and self._from_module(module, val) ): | |
89 | |
90 self._find(tests, val, valname1, module, source_lines, | |
91 globs, seen) | |
92 | |
93 | |
94 # Look for tests in a class's contained objects. | |
95 if isclass(obj) and self._recurse: | |
96 #print 'RECURSE into class:',obj # dbg | |
97 for valname, val in obj.__dict__.items(): | |
98 #valname1 = '%s.%s' % (name, valname) # dbg | |
99 #print 'N',name,'VN:',valname,'val:',str(val)[:77] # dbg | |
100 # Special handling for staticmethod/classmethod. | |
101 if isinstance(val, staticmethod): | |
102 val = getattr(obj, valname) | |
103 if isinstance(val, classmethod): | |
104 val = getattr(obj, valname).__func__ | |
105 | |
106 # Recurse to methods, properties, and nested classes. | |
107 if ((isfunction(val) or isclass(val) or | |
108 ismethod(val) or isinstance(val, property)) and | |
109 self._from_module(module, val)): | |
110 valname = '%s.%s' % (name, valname) | |
111 self._find(tests, val, valname, module, source_lines, | |
112 globs, seen) | |
113 | |
114 | |
115 # second-chance checker; if the default comparison doesn't | |
116 # pass, then see if the expected output string contains flags that | |
117 # tell us to ignore the output | |
118 class NumpyOutputChecker(doctest.OutputChecker): | |
119 def check_output(self, want, got, optionflags): | |
120 ret = doctest.OutputChecker.check_output(self, want, got, | |
121 optionflags) | |
122 if not ret: | |
123 if "#random" in want: | |
124 return True | |
125 | |
126 # it would be useful to normalize endianness so that | |
127 # bigendian machines don't fail all the tests (and there are | |
128 # actually some bigendian examples in the doctests). Let's try | |
129 # making them all little endian | |
130 got = got.replace("'>", "'<") | |
131 want= want.replace("'>", "'<") | |
132 | |
133 # try to normalize out 32 and 64 bit default int sizes | |
134 for sz in [4, 8]: | |
135 got = got.replace("'<i%d'"%sz, "int") | |
136 want= want.replace("'<i%d'"%sz, "int") | |
137 | |
138 ret = doctest.OutputChecker.check_output(self, want, | |
139 got, optionflags) | |
140 | |
141 return ret | |
142 | |
143 | |
144 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in | |
145 # its constructor that blocks non-default arguments from being passed | |
146 # down into doctest.DocTestCase | |
147 class NumpyDocTestCase(npd.DocTestCase): | |
148 def __init__(self, test, optionflags=0, setUp=None, tearDown=None, | |
149 checker=None, obj=None, result_var='_'): | |
150 self._result_var = result_var | |
151 self._nose_obj = obj | |
152 doctest.DocTestCase.__init__(self, test, | |
153 optionflags=optionflags, | |
154 setUp=setUp, tearDown=tearDown, | |
155 checker=checker) | |
156 | |
157 | |
158 print_state = numpy.get_printoptions() | |
159 | |
160 class NumpyDoctest(npd.Doctest): | |
161 name = 'numpydoctest' # call nosetests with --with-numpydoctest | |
162 score = 1000 # load late, after doctest builtin | |
163 | |
164 # always use whitespace and ellipsis options for doctests | |
165 doctest_optflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS | |
166 | |
167 # files that should be ignored for doctests | |
168 doctest_ignore = ['generate_numpy_api.py', | |
169 'setup.py'] | |
170 | |
171 # Custom classes; class variables to allow subclassing | |
172 doctest_case_class = NumpyDocTestCase | |
173 out_check_class = NumpyOutputChecker | |
174 test_finder_class = NumpyDocTestFinder | |
175 | |
176 # Don't use the standard doctest option handler; hard-code the option values | |
177 def options(self, parser, env=os.environ): | |
178 Plugin.options(self, parser, env) | |
179 # Test doctests in 'test' files / directories. Standard plugin default | |
180 # is False | |
181 self.doctest_tests = True | |
182 # Variable name; if defined, doctest results stored in this variable in | |
183 # the top-level namespace. None is the standard default | |
184 self.doctest_result_var = None | |
185 | |
186 def configure(self, options, config): | |
187 # parent method sets enabled flag from command line --with-numpydoctest | |
188 Plugin.configure(self, options, config) | |
189 self.finder = self.test_finder_class() | |
190 self.parser = doctest.DocTestParser() | |
191 if self.enabled: | |
192 # Pull standard doctest out of plugin list; there's no reason to run | |
193 # both. In practice the Unplugger plugin above would cover us when | |
194 # run from a standard numpy.test() call; this is just in case | |
195 # someone wants to run our plugin outside the numpy.test() machinery | |
196 config.plugins.plugins = [p for p in config.plugins.plugins | |
197 if p.name != 'doctest'] | |
198 | |
199 def set_test_context(self, test): | |
200 """ Configure `test` object to set test context | |
201 | |
202 We set the numpy / scipy standard doctest namespace | |
203 | |
204 Parameters | |
205 ---------- | |
206 test : test object | |
207 with ``globs`` dictionary defining namespace | |
208 | |
209 Returns | |
210 ------- | |
211 None | |
212 | |
213 Notes | |
214 ----- | |
215 `test` object modified in place | |
216 """ | |
217 # set the namespace for tests | |
218 pkg_name = get_package_name(os.path.dirname(test.filename)) | |
219 | |
220 # Each doctest should execute in an environment equivalent to | |
221 # starting Python and executing "import numpy as np", and, | |
222 # for SciPy packages, an additional import of the local | |
223 # package (so that scipy.linalg.basic.py's doctests have an | |
224 # implicit "from scipy import linalg" as well. | |
225 # | |
226 # Note: __file__ allows the doctest in NoseTester to run | |
227 # without producing an error | |
228 test.globs = {'__builtins__':__builtins__, | |
229 '__file__':'__main__', | |
230 '__name__':'__main__', | |
231 'np':numpy} | |
232 # add appropriate scipy import for SciPy tests | |
233 if 'scipy' in pkg_name: | |
234 p = pkg_name.split('.') | |
235 p2 = p[-1] | |
236 test.globs[p2] = __import__(pkg_name, test.globs, {}, [p2]) | |
237 | |
238 # Override test loading to customize test context (with set_test_context | |
239 # method), set standard docstring options, and install our own test output | |
240 # checker | |
241 def loadTestsFromModule(self, module): | |
242 if not self.matches(module.__name__): | |
243 npd.log.debug("Doctest doesn't want module %s", module) | |
244 return | |
245 try: | |
246 tests = self.finder.find(module) | |
247 except AttributeError: | |
248 # nose allows module.__test__ = False; doctest does not and | |
249 # throws AttributeError | |
250 return | |
251 if not tests: | |
252 return | |
253 tests.sort() | |
254 module_file = src(module.__file__) | |
255 for test in tests: | |
256 if not test.examples: | |
257 continue | |
258 if not test.filename: | |
259 test.filename = module_file | |
260 # Set test namespace; test altered in place | |
261 self.set_test_context(test) | |
262 yield self.doctest_case_class(test, | |
263 optionflags=self.doctest_optflags, | |
264 checker=self.out_check_class(), | |
265 result_var=self.doctest_result_var) | |
266 | |
267 # Add an afterContext method to nose.plugins.doctests.Doctest in order | |
268 # to restore print options to the original state after each doctest | |
269 def afterContext(self): | |
270 numpy.set_printoptions(**print_state) | |
271 | |
272 # Ignore NumPy-specific build files that shouldn't be searched for tests | |
273 def wantFile(self, file): | |
274 bn = os.path.basename(file) | |
275 if bn in self.doctest_ignore: | |
276 return False | |
277 return npd.Doctest.wantFile(self, file) | |
278 | |
279 | |
280 class Unplugger(object): | |
281 """ Nose plugin to remove named plugin late in loading | |
282 | |
283 By default it removes the "doctest" plugin. | |
284 """ | |
285 name = 'unplugger' | |
286 enabled = True # always enabled | |
287 score = 4000 # load late in order to be after builtins | |
288 | |
289 def __init__(self, to_unplug='doctest'): | |
290 self.to_unplug = to_unplug | |
291 | |
292 def options(self, parser, env): | |
293 pass | |
294 | |
295 def configure(self, options, config): | |
296 # Pull named plugin out of plugins list | |
297 config.plugins.plugins = [p for p in config.plugins.plugins | |
298 if p.name != self.to_unplug] | |
299 | |
300 | |
301 class KnownFailureTest(Exception): | |
302 '''Raise this exception to mark a test as a known failing test.''' | |
303 pass | |
304 | |
305 | |
306 class KnownFailure(ErrorClassPlugin): | |
307 '''Plugin that installs a KNOWNFAIL error class for the | |
308 KnownFailureClass exception. When KnownFailureTest is raised, | |
309 the exception will be logged in the knownfail attribute of the | |
310 result, 'K' or 'KNOWNFAIL' (verbose) will be output, and the | |
311 exception will not be counted as an error or failure.''' | |
312 enabled = True | |
313 knownfail = ErrorClass(KnownFailureTest, | |
314 label='KNOWNFAIL', | |
315 isfailure=False) | |
316 | |
317 def options(self, parser, env=os.environ): | |
318 env_opt = 'NOSE_WITHOUT_KNOWNFAIL' | |
319 parser.add_option('--no-knownfail', action='store_true', | |
320 dest='noKnownFail', default=env.get(env_opt, False), | |
321 help='Disable special handling of KnownFailureTest ' | |
322 'exceptions') | |
323 | |
324 def configure(self, options, conf): | |
325 if not self.can_configure: | |
326 return | |
327 self.conf = conf | |
328 disable = getattr(options, 'noKnownFail', False) | |
329 if disable: | |
330 self.enabled = False | |
331 | |
332 | |
333 # Class allows us to save the results of the tests in runTests - see runTests | |
334 # method docstring for details | |
335 class NumpyTestProgram(nose.core.TestProgram): | |
336 def runTests(self): | |
337 """Run Tests. Returns true on success, false on failure, and | |
338 sets self.success to the same value. | |
339 | |
340 Because nose currently discards the test result object, but we need | |
341 to return it to the user, override TestProgram.runTests to retain | |
342 the result | |
343 """ | |
344 if self.testRunner is None: | |
345 self.testRunner = nose.core.TextTestRunner(stream=self.config.stream, | |
346 verbosity=self.config.verbosity, | |
347 config=self.config) | |
348 plug_runner = self.config.plugins.prepareTestRunner(self.testRunner) | |
349 if plug_runner is not None: | |
350 self.testRunner = plug_runner | |
351 self.result = self.testRunner.run(self.test) | |
352 self.success = self.result.wasSuccessful() | |
353 return self.success |