diff --git a/scipy/_build_utils/__init__.py b/scipy/_build_utils/__init__.py index cb3e495522dd..602295493838 100644 --- a/scipy/_build_utils/__init__.py +++ b/scipy/_build_utils/__init__.py @@ -18,6 +18,17 @@ def uses_blas64(): return (os.environ.get("NPY_USE_BLAS_ILP64", "0") != "0") +def import_file(folder, module_name): + """Import a file directly, avoiding importing scipy""" + import importlib + import pathlib + + fname = pathlib.Path(folder) / f'{module_name}.py' + spec = importlib.util.spec_from_file_location(module_name, str(fname)) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + from scipy._lib._testutils import PytestTester test = PytestTester(__name__) diff --git a/scipy/stats/_boost/__init__.py b/scipy/stats/_boost/__init__.py index d33dc07faa45..89fccf0abab5 100644 --- a/scipy/stats/_boost/__init__.py +++ b/scipy/stats/_boost/__init__.py @@ -1,22 +1,17 @@ -try: - from scipy.stats._boost.beta_ufunc import ( - _beta_pdf, _beta_cdf, _beta_sf, _beta_ppf, - _beta_isf, _beta_mean, _beta_variance, - _beta_skewness, _beta_kurtosis_excess, - ) +from scipy.stats._boost.beta_ufunc import ( + _beta_pdf, _beta_cdf, _beta_sf, _beta_ppf, + _beta_isf, _beta_mean, _beta_variance, + _beta_skewness, _beta_kurtosis_excess, +) - from scipy.stats._boost.binom_ufunc import ( - _binom_pdf, _binom_cdf, _binom_sf, _binom_ppf, - _binom_isf, _binom_mean, _binom_variance, - _binom_skewness, _binom_kurtosis_excess, - ) +from scipy.stats._boost.binom_ufunc import ( + _binom_pdf, _binom_cdf, _binom_sf, _binom_ppf, + _binom_isf, _binom_mean, _binom_variance, + _binom_skewness, _binom_kurtosis_excess, +) - from scipy.stats._boost.nbinom_ufunc import ( - _nbinom_pdf, _nbinom_cdf, _nbinom_sf, _nbinom_ppf, - _nbinom_isf, _nbinom_mean, _nbinom_variance, - _nbinom_skewness, _nbinom_kurtosis_excess, - ) -except (ModuleNotFoundError, ImportError): - # scipy.stats doesn't exist during cythonize - # step of build! - pass +from scipy.stats._boost.nbinom_ufunc import ( + _nbinom_pdf, _nbinom_cdf, _nbinom_sf, _nbinom_ppf, + _nbinom_isf, _nbinom_mean, _nbinom_variance, + _nbinom_skewness, _nbinom_kurtosis_excess, +) diff --git a/scipy/stats/_boost/_info.py b/scipy/stats/_boost/include/_info.py similarity index 100% rename from scipy/stats/_boost/_info.py rename to scipy/stats/_boost/include/_info.py diff --git a/scipy/stats/_boost/include/code_gen.py b/scipy/stats/_boost/include/code_gen.py index 093329b464bb..3d1faa2ba8b3 100644 --- a/scipy/stats/_boost/include/code_gen.py +++ b/scipy/stats/_boost/include/code_gen.py @@ -3,7 +3,14 @@ from typing import NamedTuple from warnings import warn from textwrap import dedent +from shutil import copyfile +import pathlib +import os +from gen_func_defs_pxd import ( # type: ignore + _gen_func_defs_pxd) +from _info import ( # type: ignore + _x_funcs, _no_x_funcs, _klass_mapper) class _MethodDef(NamedTuple): ufunc_name: str @@ -139,3 +146,32 @@ def _ufunc_gen(scipy_dist: str, types: list, ctor_args: tuple, 0 # unused ) ''')) + + +if __name__ == '__main__': + # create target directory + _boost_dir = pathlib.Path(__file__).resolve().parent.parent + src_dir = _boost_dir / 'src' + src_dir.mkdir(exist_ok=True, parents=True) + + # copy contents of include into directory to satisfy Cython + # PXD include conditions + inc_dir = _boost_dir / 'include' + src = 'templated_pyufunc.pxd' + copyfile(inc_dir / src, src_dir / src) + + # generate the PXD and PYX wrappers + _gen_func_defs_pxd( + f'{src_dir}/func_defs.pxd', + x_funcs=_x_funcs, + no_x_funcs=_no_x_funcs) + for b, s in _klass_mapper.items(): + _ufunc_gen( + scipy_dist=s.scipy_name, + types=['NPY_FLOAT', 'NPY_DOUBLE', 'NPY_LONGDOUBLE'], + ctor_args=s.ctor_args, + filename=f'{src_dir}/{s.scipy_name}_ufunc.pyx', + boost_dist=f'{b}_distribution', + x_funcs=_x_funcs, + no_x_funcs=_no_x_funcs, + ) diff --git a/scipy/stats/_boost/setup.py b/scipy/stats/_boost/setup.py index ed7dac5b1414..7f92ee76e10d 100644 --- a/scipy/stats/_boost/setup.py +++ b/scipy/stats/_boost/setup.py @@ -11,7 +11,7 @@ def pre_build_hook(build_ext, ext): def configuration(parent_package='', top_path=None): from scipy._lib._boost_utils import _boost_dir - from _info import _klass_mapper # type: ignore + from scipy._build_utils import import_file from numpy.distutils.misc_util import Configuration import numpy as np config = Configuration('_boost', parent_package, top_path) @@ -32,7 +32,9 @@ def configuration(parent_package='', top_path=None): ] # generate the PXD and PYX wrappers - src_dir = pathlib.Path(__file__).parent / 'src' + boost_dir = pathlib.Path(__file__).parent + src_dir = boost_dir / 'src' + _klass_mapper = import_file(boost_dir / 'include', '_info')._klass_mapper for s in _klass_mapper.values(): ext = config.add_extension( f'{s.scipy_name}_ufunc', diff --git a/scipy/stats/_generate_pyx.py b/scipy/stats/_generate_pyx.py index d8944424dcd6..f6c088d647d0 100644 --- a/scipy/stats/_generate_pyx.py +++ b/scipy/stats/_generate_pyx.py @@ -1,5 +1,7 @@ import pathlib from shutil import copyfile +import subprocess +import sys def isNPY_OLD(): @@ -22,37 +24,9 @@ def make_biasedurn(): def make_boost(): - # create target directory - (pathlib.Path(__file__).parent / '_boost/src').mkdir(exist_ok=True, - parents=True) - src_dir = pathlib.Path(__file__).parent / '_boost/src' - - # copy contents of include into directory to satisfy Cython - # PXD include conditions - inc_dir = pathlib.Path(__file__).parent / '_boost/include' - src = 'templated_pyufunc.pxd' - copyfile(inc_dir / src, src_dir / src) - - # generate the PXD and PYX wrappers - from _boost.include.gen_func_defs_pxd import ( # type: ignore - _gen_func_defs_pxd) - from _boost.include.code_gen import _ufunc_gen # type: ignore - from _boost._info import ( # type: ignore - _x_funcs, _no_x_funcs, _klass_mapper) - _gen_func_defs_pxd( - f'{src_dir}/func_defs.pxd', - x_funcs=_x_funcs, - no_x_funcs=_no_x_funcs) - for b, s in _klass_mapper.items(): - _ufunc_gen( - scipy_dist=s.scipy_name, - types=['NPY_FLOAT', 'NPY_DOUBLE', 'NPY_LONGDOUBLE'], - ctor_args=s.ctor_args, - filename=f'{src_dir}/{s.scipy_name}_ufunc.pyx', - boost_dist=f'{b}_distribution', - x_funcs=_x_funcs, - no_x_funcs=_no_x_funcs, - ) + # Call code generator inside _boost directory + code_gen = pathlib.Path(__file__).parent / '_boost/include/code_gen.py' + subprocess.run([sys.executable, str(code_gen)], check=True) if __name__ == '__main__':