diff --git a/tests/conftest.py b/tests/conftest.py index 30d46cc..1595c80 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,8 +3,6 @@ import pytest -collect_ignore = ['ufunc_example'] - def _wrap_docstring_in_func(func_name, docstring): template = textwrap.dedent(r""" diff --git a/tests/test_doctest_ufunc.py b/tests/test_doctest_ufunc.py deleted file mode 100644 index b388aee..0000000 --- a/tests/test_doctest_ufunc.py +++ /dev/null @@ -1,42 +0,0 @@ -# -*- coding: utf-8 -*- -import sys -import glob - -import pytest - -pytest.importorskip('numpy') - -pytest_plugins = ['pytester'] - - -def test_help_message(testdir): - result = testdir.runpytest( - '--help', - ) - # fnmatch_lines does an assertion internally - result.stdout.fnmatch_lines([ - '*--doctest-ufunc*Enable running doctests in ' - 'docstrings of Numpy ufuncs.', - ]) - - -def test_example(testdir): - # Create and build example module - testdir.copy_example('tests/ufunc_example/_module2.c') - testdir.copy_example('tests/ufunc_example/module1.py') - testdir.copy_example('tests/ufunc_example/module2.py') - testdir.copy_example('tests/ufunc_example/setup.py') - testdir.run(sys.executable, 'setup.py', 'build') - build_dir, = glob.glob(str(testdir.tmpdir / 'build/lib.*')) - - # Run pytest without doctests: 0 tests run - result = testdir.runpytest(build_dir) - result.assert_outcomes(passed=0, failed=0) - - # Run pytest with doctests: 1 test run - result = testdir.runpytest(build_dir, '--doctest-modules') - result.assert_outcomes(passed=1, failed=0) - - # Run pytest with doctests including ufuncs: 2 tests run - result = testdir.runpytest(build_dir, '--doctest-plus', '--doctest-modules', '--doctest-ufunc') - result.assert_outcomes(passed=2, failed=0) diff --git a/tests/test_doctestplus.py b/tests/test_doctestplus.py index e5b2109..abc973d 100644 --- a/tests/test_doctestplus.py +++ b/tests/test_doctestplus.py @@ -1,6 +1,8 @@ +import glob import os from packaging.version import Version from textwrap import dedent +import sys import pytest @@ -930,3 +932,114 @@ def test_remote_data_ignore_warnings(testdir): ) testdir.inline_run(p, '--doctest-plus', '--doctest-rst', '--remote-data').assertoutcome(passed=1) testdir.inline_run(p, '--doctest-plus', '--doctest-rst').assertoutcome(skipped=1) + + +def test_ufunc(testdir): + pytest.importorskip('numpy') + + # Create and build example module + testdir.makepyfile(module1=""" + def foo(): + '''A doctest... + + >>> foo() + 1 + ''' + return 1 + """) + testdir.makepyfile(module2=""" + from _module2 import foo + """) + testdir.makepyfile(setup=""" + from setuptools import setup, Extension + import numpy as np + + ext = Extension('_module2', ['_module2.c'], + extra_compile_args=['-std=c99'], + include_dirs=[np.get_include()]) + setup(name='example', py_modules=['module1', 'module2'], ext_modules=[ext]) + """) + testdir.makefile('.c', _module2=r""" + #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION + + #include + #include + #include + + + static double foo_inner(double a, double b) + { + return a + b; + } + + + static void foo_loop( + char **args, + const npy_intp *dimensions, + const npy_intp *steps, + void *NPY_UNUSED(data) + ) { + const npy_intp n = dimensions[0]; + for (npy_intp i = 0; i < n; i ++) + { + *(double *) &args[2][i * steps[2]] = foo_inner( + *(double *) &args[0][i * steps[0]], + *(double *) &args[1][i * steps[1]]); + } + } + + + static PyUFuncGenericFunction foo_loops[] = {foo_loop}; + static char foo_types[] = {NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE}; + static void *foo_data[] = {NULL}; + static const char foo_name[] = "foo"; + static const char foo_docstring[] = ">>> foo(1, 2)\n3.0"; + + static PyModuleDef moduledef = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = "_module2", + .m_size = -1 + }; + + + PyMODINIT_FUNC PyInit__module2(void) + { + import_array(); + import_ufunc(); + + PyObject *module = PyModule_Create(&moduledef); + if (!module) + return NULL; + + PyObject *obj = PyUFunc_FromFuncAndData( + foo_loops, foo_data, foo_types, 1, 2, 1, PyUFunc_None, foo_name, + foo_docstring, 0); + if (!obj) + { + Py_DECREF(module); + return NULL; + } + if (PyModule_AddObject(module, foo_name, obj) < 0) + { + Py_DECREF(obj); + Py_DECREF(module); + return NULL; + } + + return module; + } + """) + testdir.run(sys.executable, 'setup.py', 'build') + build_dir, = glob.glob(str(testdir.tmpdir / 'build/lib.*')) + + # Run pytest without doctests: 0 tests run + result = testdir.runpytest(build_dir) + result.assert_outcomes(passed=0, failed=0) + + # Run pytest with doctests: 1 test run + result = testdir.runpytest(build_dir, '--doctest-modules') + result.assert_outcomes(passed=1, failed=0) + + # Run pytest with doctests including ufuncs: 2 tests run + result = testdir.runpytest(build_dir, '--doctest-plus', '--doctest-modules', '--doctest-ufunc') + result.assert_outcomes(passed=2, failed=0) diff --git a/tests/ufunc_example/_module2.c b/tests/ufunc_example/_module2.c deleted file mode 100644 index 8b42391..0000000 --- a/tests/ufunc_example/_module2.c +++ /dev/null @@ -1,68 +0,0 @@ -#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION - -#include -#include -#include - - -static double foo_inner(double a, double b) -{ - return a + b; -} - - -static void foo_loop( - char **args, - const npy_intp *dimensions, - const npy_intp *steps, - void *NPY_UNUSED(data) -) { - const npy_intp n = dimensions[0]; - for (npy_intp i = 0; i < n; i ++) - { - *(double *) &args[2][i * steps[2]] = foo_inner( - *(double *) &args[0][i * steps[0]], - *(double *) &args[1][i * steps[1]]); - } -} - - -static PyUFuncGenericFunction foo_loops[] = {foo_loop}; -static char foo_types[] = {NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE}; -static void *foo_data[] = {NULL}; -static const char foo_name[] = "foo"; -static const char foo_docstring[] = ">>> foo(1, 2)\n3.0"; - -static PyModuleDef moduledef = { - .m_base = PyModuleDef_HEAD_INIT, - .m_name = "_module2", - .m_size = -1 -}; - - -PyMODINIT_FUNC PyInit__module2(void) -{ - import_array(); - import_ufunc(); - - PyObject *module = PyModule_Create(&moduledef); - if (!module) - return NULL; - - PyObject *obj = PyUFunc_FromFuncAndData( - foo_loops, foo_data, foo_types, 1, 2, 1, PyUFunc_None, foo_name, - foo_docstring, 0); - if (!obj) - { - Py_DECREF(module); - return NULL; - } - if (PyModule_AddObject(module, foo_name, obj) < 0) - { - Py_DECREF(obj); - Py_DECREF(module); - return NULL; - } - - return module; -} diff --git a/tests/ufunc_example/module1.py b/tests/ufunc_example/module1.py deleted file mode 100644 index e343a7a..0000000 --- a/tests/ufunc_example/module1.py +++ /dev/null @@ -1,7 +0,0 @@ -def foo(): - '''A doctest... - - >>> foo() - 1 - ''' - return 1 diff --git a/tests/ufunc_example/module2.py b/tests/ufunc_example/module2.py deleted file mode 100644 index d2c28f2..0000000 --- a/tests/ufunc_example/module2.py +++ /dev/null @@ -1 +0,0 @@ -from _module2 import foo # noqa: F401 diff --git a/tests/ufunc_example/setup.py b/tests/ufunc_example/setup.py deleted file mode 100644 index f32d9f1..0000000 --- a/tests/ufunc_example/setup.py +++ /dev/null @@ -1,7 +0,0 @@ -from setuptools import setup, Extension -import numpy as np - -ext = Extension('_module2', ['_module2.c'], - extra_compile_args=['-std=c99'], - include_dirs=[np.get_include()]) -setup(name='example', py_modules=['module1', 'module2'], ext_modules=[ext])