Created
March 26, 2011 02:11
-
-
Save zed/887961 to your computer and use it in GitHub Desktop.
doctest support for cython
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
Cython-compatible wrapper for doctest.testmod(). | |
Usage example, assuming a Cython module mymod.pyx is compiled. | |
This is run from the command line, passing a command to Python: | |
python -c "import cydoctest, mymod; cydoctest.testmod(mymod)" | |
(This still won't let a Cython module run its own doctests | |
when called with "python mymod.py", but it's pretty close. | |
Further options can be passed to testmod() as desired, e.g. | |
verbose=True.) | |
http://wiki.cython.org/FAQ#HowcanIrundoctestsinCythoncode.28pyxfiles.29.3F | |
Changed to support (with pyximport): | |
$ python -mcydoctest mymod.pyx | |
""" | |
try: | |
import pyximport; pyximport.install() | |
except ImportError: | |
pass # ignore | |
import doctest | |
import inspect | |
import os | |
import sys | |
def _from_module(module, object): | |
""" | |
Return true if the given object is defined in the given module. | |
""" | |
if module is None: | |
return True | |
elif inspect.getmodule(object) is not None: | |
return module is inspect.getmodule(object) | |
elif inspect.isfunction(object): | |
return module.__dict__ is object.func_globals | |
elif inspect.isclass(object): | |
return module.__name__ == object.__module__ | |
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 fix_module_doctest(module): | |
""" | |
Extract docstrings from cython functions, that would be skipped by doctest | |
otherwise. | |
""" | |
module.__test__ = {} | |
for name in dir(module): | |
value = getattr(module, name) | |
if inspect.isbuiltin(value) and isinstance(value.__doc__, str) and _from_module(module, value): | |
module.__test__[name] = value.__doc__ | |
def testmod(m=None, *args, **kwargs): | |
""" | |
Fix a Cython module's doctests, then call doctest.testmod() | |
All other arguments are passed directly to doctest.testmod(). | |
""" | |
fix_module_doctest(m) | |
return doctest.testmod(m, *args, **kwargs) | |
def _test(): | |
"""adapted from stdlib's doctest.py""" | |
testfiles = [arg for arg in sys.argv[1:] if arg and arg[0] != '-'] | |
if not testfiles: | |
name = os.path.basename(sys.argv[0]) | |
if '__loader__' in globals(): # python -m | |
name, _ = os.path.splitext(name) | |
print("usage: {0} [-v] file ...".format(name)) | |
return 2 | |
for filename in testfiles: | |
if filename.endswith((".py", ".pyx")): | |
# It is a module -- insert its dir into sys.path and try to | |
# import it. If it is part of a package, that possibly | |
# won't work because of package imports. | |
dirname, filename = os.path.split(filename) | |
sys.path.insert(0, dirname) | |
_, ext = os.path.splitext(filename) | |
m = __import__(filename[:-len(ext)]) | |
del sys.path[0] | |
failures, _ = testmod(m) | |
else: | |
failures, _ = doctest.testfile(filename, module_relative=False) | |
if failures: | |
return 1 | |
return 0 | |
if __name__ == "__main__": | |
sys.exit(_test()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment