# These classes implement a doctest runner plugin for nose, a "known failure" # error class, and a customized TestProgram for NumPy. # Because this module imports nose directly, it should not # be used except by nosetester.py to avoid a general NumPy # dependency on nose. from __future__ import division, absolute_import, print_function import os import doctest import inspect import numpy import pytest from .utils import KnownFailureException, SkipTest import _pytest.runner import _pytest.skipping class NpyPlugin(object): def pytest_runtest_makereport(self, call): if call.excinfo: if call.excinfo.errisinstance(KnownFailureException): #let's substitute the excinfo with a pytest.xfail one call2 = call.__class__( lambda: _pytest.runner.skip(str(call.excinfo.value)), call.when) print() print() print(call.excinfo._getreprcrash()) print() print(call.excinfo) print() print(call2.excinfo) print() call.excinfo = call2.excinfo if call.excinfo.errisinstance(SkipTest): #let's substitute the excinfo with a pytest.skip one call2 = call.__class__( lambda: _pytest.runner.skip(str(call.excinfo.value)), call.when) call.excinfo = call2.excinfo if False: from nose.plugins import doctests as npd from nose.plugins.errorclass import ErrorClass, ErrorClassPlugin from nose.plugins.base import Plugin from nose.util import src from .nosetester import get_package_name # Some of the classes in this module begin with 'Numpy' to clearly distinguish # them from the plethora of very similar names from nose/unittest/doctest #----------------------------------------------------------------------------- # Modified version of the one in the stdlib, that fixes a python bug (doctests # not found in extension modules, http://bugs.python.org/issue3158) class NumpyDocTestFinder(doctest.DocTestFinder): def _from_module(self, module, object): """ Return true if the given object is defined in the given module. """ if module is None: return True elif inspect.isfunction(object): return module.__dict__ is object.__globals__ elif inspect.isbuiltin(object): return module.__name__ == object.__module__ elif inspect.isclass(object): return module.__name__ == object.__module__ elif inspect.ismethod(object): # This one may be a bug in cython that fails to correctly set the # __module__ attribute of methods, but since the same error is easy # to make by extension code writers, having this safety in place # isn't such a bad idea return module.__name__ == object.__self__.__class__.__module__ elif inspect.getmodule(object) is not None: return module is inspect.getmodule(object) elif hasattr(object, '__module__'): return module.__name__ == object.__module__ elif isinstance(object, property): return True # [XX] no way not be sure. else: raise ValueError("object must be a class or function") def _find(self, tests, obj, name, module, source_lines, globs, seen): """ Find tests for the given object and any contained objects, and add them to `tests`. """ doctest.DocTestFinder._find(self, tests, obj, name, module, source_lines, globs, seen) # Below we re-run pieces of the above method with manual modifications, # because the original code is buggy and fails to correctly identify # doctests in extension modules. # Local shorthands from inspect import ( isroutine, isclass, ismodule, isfunction, ismethod ) # Look for tests in a module's contained objects. if ismodule(obj) and self._recurse: for valname, val in obj.__dict__.items(): valname1 = '%s.%s' % (name, valname) if ( (isroutine(val) or isclass(val)) and self._from_module(module, val)): self._find(tests, val, valname1, module, source_lines, globs, seen) # Look for tests in a class's contained objects. if isclass(obj) and self._recurse: for valname, val in obj.__dict__.items(): # Special handling for staticmethod/classmethod. if isinstance(val, staticmethod): val = getattr(obj, valname) if isinstance(val, classmethod): val = getattr(obj, valname).__func__ # Recurse to methods, properties, and nested classes. if ((isfunction(val) or isclass(val) or ismethod(val) or isinstance(val, property)) and self._from_module(module, val)): valname = '%s.%s' % (name, valname) self._find(tests, val, valname, module, source_lines, globs, seen) # second-chance checker; if the default comparison doesn't # pass, then see if the expected output string contains flags that # tell us to ignore the output class NumpyOutputChecker(doctest.OutputChecker): def check_output(self, want, got, optionflags): ret = doctest.OutputChecker.check_output(self, want, got, optionflags) if not ret: if "#random" in want: return True # it would be useful to normalize endianness so that # bigendian machines don't fail all the tests (and there are # actually some bigendian examples in the doctests). Let's try # making them all little endian got = got.replace("'>", "'<") want = want.replace("'>", "'<") # try to normalize out 32 and 64 bit default int sizes for sz in [4, 8]: got = got.replace("'