""" Utilities useful during the build. """ # author: Andy Mueller, Gael Varoquaux # license: BSD import contextlib import os import sklearn from .._min_dependencies import CYTHON_MIN_VERSION from ..externals._packaging.version import parse from .openmp_helpers import check_openmp_support from .pre_build_helpers import basic_check_build DEFAULT_ROOT = "sklearn" def _check_cython_version(): message = ( "Please install Cython with a version >= {0} in order " "to build a scikit-learn from source." ).format(CYTHON_MIN_VERSION) try: import Cython except ModuleNotFoundError as e: # Re-raise with more informative error message instead: raise ModuleNotFoundError(message) from e if parse(Cython.__version__) < parse(CYTHON_MIN_VERSION): message += " The current version of Cython is {} installed in {}.".format( Cython.__version__, Cython.__path__ ) raise ValueError(message) def cythonize_extensions(extension): """Check that a recent Cython is available and cythonize extensions""" _check_cython_version() from Cython.Build import cythonize # Fast fail before cythonization if compiler fails compiling basic test # code even without OpenMP basic_check_build() # check simple compilation with OpenMP. If it fails scikit-learn will be # built without OpenMP and the test test_openmp_supported in the test suite # will fail. # `check_openmp_support` compiles a small test program to see if the # compilers are properly configured to build with OpenMP. This is expensive # and we only want to call this function once. # The result of this check is cached as a private attribute on the sklearn # module (only at build-time) to be used in the build_ext subclass defined # in the top-level setup.py file to actually build the compiled extensions # with OpenMP flags if needed. sklearn._OPENMP_SUPPORTED = check_openmp_support() n_jobs = 1 with contextlib.suppress(ImportError): import joblib n_jobs = joblib.cpu_count() # Additional checks for Cython cython_enable_debug_directives = ( os.environ.get("SKLEARN_ENABLE_DEBUG_CYTHON_DIRECTIVES", "0") != "0" ) compiler_directives = { "language_level": 3, "boundscheck": cython_enable_debug_directives, "wraparound": False, "initializedcheck": False, "nonecheck": False, "cdivision": True, "profile": False, } return cythonize( extension, nthreads=n_jobs, compiler_directives=compiler_directives, annotate=False, ) def gen_from_templates(templates): """Generate cython files from a list of templates""" # Lazy import because cython is not a runtime dependency. from Cython import Tempita for template in templates: outfile = template.replace(".tp", "") # if the template is not updated, no need to output the cython file if not ( os.path.exists(outfile) and os.stat(template).st_mtime < os.stat(outfile).st_mtime ): with open(template, "r") as f: tmpl = f.read() tmpl_ = Tempita.sub(tmpl) warn_msg = ( "# WARNING: Do not edit this file directly.\n" f"# It is automatically generated from {template!r}.\n" "# Changes must be made there.\n\n" ) with open(outfile, "w") as f: f.write(warn_msg) f.write(tmpl_)