__init__.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. """
  2. Utilities useful during the build.
  3. """
  4. # author: Andy Mueller, Gael Varoquaux
  5. # license: BSD
  6. import contextlib
  7. import os
  8. import sklearn
  9. from .._min_dependencies import CYTHON_MIN_VERSION
  10. from ..externals._packaging.version import parse
  11. from .openmp_helpers import check_openmp_support
  12. from .pre_build_helpers import basic_check_build
  13. DEFAULT_ROOT = "sklearn"
  14. def _check_cython_version():
  15. message = (
  16. "Please install Cython with a version >= {0} in order "
  17. "to build a scikit-learn from source."
  18. ).format(CYTHON_MIN_VERSION)
  19. try:
  20. import Cython
  21. except ModuleNotFoundError as e:
  22. # Re-raise with more informative error message instead:
  23. raise ModuleNotFoundError(message) from e
  24. if parse(Cython.__version__) < parse(CYTHON_MIN_VERSION):
  25. message += " The current version of Cython is {} installed in {}.".format(
  26. Cython.__version__, Cython.__path__
  27. )
  28. raise ValueError(message)
  29. def cythonize_extensions(extension):
  30. """Check that a recent Cython is available and cythonize extensions"""
  31. _check_cython_version()
  32. from Cython.Build import cythonize
  33. # Fast fail before cythonization if compiler fails compiling basic test
  34. # code even without OpenMP
  35. basic_check_build()
  36. # check simple compilation with OpenMP. If it fails scikit-learn will be
  37. # built without OpenMP and the test test_openmp_supported in the test suite
  38. # will fail.
  39. # `check_openmp_support` compiles a small test program to see if the
  40. # compilers are properly configured to build with OpenMP. This is expensive
  41. # and we only want to call this function once.
  42. # The result of this check is cached as a private attribute on the sklearn
  43. # module (only at build-time) to be used in the build_ext subclass defined
  44. # in the top-level setup.py file to actually build the compiled extensions
  45. # with OpenMP flags if needed.
  46. sklearn._OPENMP_SUPPORTED = check_openmp_support()
  47. n_jobs = 1
  48. with contextlib.suppress(ImportError):
  49. import joblib
  50. n_jobs = joblib.cpu_count()
  51. # Additional checks for Cython
  52. cython_enable_debug_directives = (
  53. os.environ.get("SKLEARN_ENABLE_DEBUG_CYTHON_DIRECTIVES", "0") != "0"
  54. )
  55. compiler_directives = {
  56. "language_level": 3,
  57. "boundscheck": cython_enable_debug_directives,
  58. "wraparound": False,
  59. "initializedcheck": False,
  60. "nonecheck": False,
  61. "cdivision": True,
  62. }
  63. return cythonize(
  64. extension,
  65. nthreads=n_jobs,
  66. compiler_directives=compiler_directives,
  67. )
  68. def gen_from_templates(templates):
  69. """Generate cython files from a list of templates"""
  70. # Lazy import because cython is not a runtime dependency.
  71. from Cython import Tempita
  72. for template in templates:
  73. outfile = template.replace(".tp", "")
  74. # if the template is not updated, no need to output the cython file
  75. if not (
  76. os.path.exists(outfile)
  77. and os.stat(template).st_mtime < os.stat(outfile).st_mtime
  78. ):
  79. with open(template, "r") as f:
  80. tmpl = f.read()
  81. tmpl_ = Tempita.sub(tmpl)
  82. warn_msg = (
  83. "# WARNING: Do not edit this file directly.\n"
  84. f"# It is automatically generated from {template!r}.\n"
  85. "# Changes must be made there.\n\n"
  86. )
  87. with open(outfile, "w") as f:
  88. f.write(warn_msg)
  89. f.write(tmpl_)