annotate DEPENDENCIES/mingw32/Python27/Lib/site-packages/numpy/testing/noseclasses.py @ 133:4acb5d8d80b6 tip

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