test_extending.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. from importlib.util import spec_from_file_location, module_from_spec
  2. import os
  3. import pathlib
  4. import pytest
  5. import shutil
  6. import subprocess
  7. import sys
  8. import sysconfig
  9. import textwrap
  10. import warnings
  11. import numpy as np
  12. from numpy.testing import IS_WASM
  13. try:
  14. import cffi
  15. except ImportError:
  16. cffi = None
  17. if sys.flags.optimize > 1:
  18. # no docstrings present to inspect when PYTHONOPTIMIZE/Py_OptimizeFlag > 1
  19. # cffi cannot succeed
  20. cffi = None
  21. try:
  22. with warnings.catch_warnings(record=True) as w:
  23. # numba issue gh-4733
  24. warnings.filterwarnings('always', '', DeprecationWarning)
  25. import numba
  26. except (ImportError, SystemError):
  27. # Certain numpy/numba versions trigger a SystemError due to a numba bug
  28. numba = None
  29. try:
  30. import cython
  31. from Cython.Compiler.Version import version as cython_version
  32. except ImportError:
  33. cython = None
  34. else:
  35. from numpy._utils import _pep440
  36. # Cython 0.29.30 is required for Python 3.11 and there are
  37. # other fixes in the 0.29 series that are needed even for earlier
  38. # Python versions.
  39. # Note: keep in sync with the one in pyproject.toml
  40. required_version = '0.29.35'
  41. if _pep440.parse(cython_version) < _pep440.Version(required_version):
  42. # too old or wrong cython, skip the test
  43. cython = None
  44. @pytest.mark.skipif(
  45. sys.platform == "win32" and sys.maxsize < 2**32,
  46. reason="Failing in 32-bit Windows wheel build job, skip for now"
  47. )
  48. @pytest.mark.skipif(IS_WASM, reason="Can't start subprocess")
  49. @pytest.mark.skipif(cython is None, reason="requires cython")
  50. @pytest.mark.slow
  51. def test_cython(tmp_path):
  52. import glob
  53. # build the examples in a temporary directory
  54. srcdir = os.path.join(os.path.dirname(__file__), '..')
  55. shutil.copytree(srcdir, tmp_path / 'random')
  56. build_dir = tmp_path / 'random' / '_examples' / 'cython'
  57. target_dir = build_dir / "build"
  58. os.makedirs(target_dir, exist_ok=True)
  59. if sys.platform == "win32":
  60. subprocess.check_call(["meson", "setup",
  61. "--buildtype=release",
  62. "--vsenv", str(build_dir)],
  63. cwd=target_dir,
  64. )
  65. else:
  66. subprocess.check_call(["meson", "setup", str(build_dir)],
  67. cwd=target_dir
  68. )
  69. subprocess.check_call(["meson", "compile", "-vv"], cwd=target_dir)
  70. # gh-16162: make sure numpy's __init__.pxd was used for cython
  71. # not really part of this test, but it is a convenient place to check
  72. g = glob.glob(str(target_dir / "*" / "extending.pyx.c"))
  73. with open(g[0]) as fid:
  74. txt_to_find = 'NumPy API declarations from "numpy/__init__'
  75. for i, line in enumerate(fid):
  76. if txt_to_find in line:
  77. break
  78. else:
  79. assert False, ("Could not find '{}' in C file, "
  80. "wrong pxd used".format(txt_to_find))
  81. # import without adding the directory to sys.path
  82. suffix = sysconfig.get_config_var('EXT_SUFFIX')
  83. def load(modname):
  84. so = (target_dir / modname).with_suffix(suffix)
  85. spec = spec_from_file_location(modname, so)
  86. mod = module_from_spec(spec)
  87. spec.loader.exec_module(mod)
  88. return mod
  89. # test that the module can be imported
  90. load("extending")
  91. load("extending_cpp")
  92. # actually test the cython c-extension
  93. extending_distributions = load("extending_distributions")
  94. from numpy.random import PCG64
  95. values = extending_distributions.uniforms_ex(PCG64(0), 10, 'd')
  96. assert values.shape == (10,)
  97. assert values.dtype == np.float64
  98. @pytest.mark.skipif(numba is None or cffi is None,
  99. reason="requires numba and cffi")
  100. def test_numba():
  101. from numpy.random._examples.numba import extending # noqa: F401
  102. @pytest.mark.skipif(cffi is None, reason="requires cffi")
  103. def test_cffi():
  104. from numpy.random._examples.cffi import extending # noqa: F401