reconnect moved files to git repo

This commit is contained in:
root
2025-08-01 04:33:03 -04:00
commit 5d3c35492d
23190 changed files with 4750716 additions and 0 deletions

View File

@ -0,0 +1,116 @@
"""
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_)

View File

@ -0,0 +1,127 @@
"""Helpers for OpenMP support during the build."""
# This code is adapted for a large part from the astropy openmp helpers, which
# can be found at: https://github.com/astropy/extension-helpers/blob/master/extension_helpers/_openmp_helpers.py # noqa
import os
import sys
import textwrap
import warnings
from .pre_build_helpers import compile_test_program
def get_openmp_flag():
if sys.platform == "win32":
return ["/openmp"]
elif sys.platform == "darwin" and "openmp" in os.getenv("CPPFLAGS", ""):
# -fopenmp can't be passed as compile flag when using Apple-clang.
# OpenMP support has to be enabled during preprocessing.
#
# For example, our macOS wheel build jobs use the following environment
# variables to build with Apple-clang and the brew installed "libomp":
#
# export CPPFLAGS="$CPPFLAGS -Xpreprocessor -fopenmp"
# export CFLAGS="$CFLAGS -I/usr/local/opt/libomp/include"
# export CXXFLAGS="$CXXFLAGS -I/usr/local/opt/libomp/include"
# export LDFLAGS="$LDFLAGS -Wl,-rpath,/usr/local/opt/libomp/lib
# -L/usr/local/opt/libomp/lib -lomp"
return []
# Default flag for GCC and clang:
return ["-fopenmp"]
def check_openmp_support():
"""Check whether OpenMP test code can be compiled and run"""
if "PYODIDE" in os.environ:
# Pyodide doesn't support OpenMP
return False
code = textwrap.dedent(
"""\
#include <omp.h>
#include <stdio.h>
int main(void) {
#pragma omp parallel
printf("nthreads=%d\\n", omp_get_num_threads());
return 0;
}
"""
)
extra_preargs = os.getenv("LDFLAGS", None)
if extra_preargs is not None:
extra_preargs = extra_preargs.strip().split(" ")
# FIXME: temporary fix to link against system libraries on linux
# "-Wl,--sysroot=/" should be removed
extra_preargs = [
flag
for flag in extra_preargs
if flag.startswith(("-L", "-Wl,-rpath", "-l", "-Wl,--sysroot=/"))
]
extra_postargs = get_openmp_flag()
openmp_exception = None
try:
output = compile_test_program(
code, extra_preargs=extra_preargs, extra_postargs=extra_postargs
)
if output and "nthreads=" in output[0]:
nthreads = int(output[0].strip().split("=")[1])
openmp_supported = len(output) == nthreads
elif "PYTHON_CROSSENV" in os.environ:
# Since we can't run the test program when cross-compiling
# assume that openmp is supported if the program can be
# compiled.
openmp_supported = True
else:
openmp_supported = False
except Exception as exception:
# We could be more specific and only catch: CompileError, LinkError,
# and subprocess.CalledProcessError.
# setuptools introduced CompileError and LinkError, but that requires
# version 61.1. Even the latest version of Ubuntu (22.04LTS) only
# ships with 59.6. So for now we catch all exceptions and reraise a
# generic exception with the original error message instead:
openmp_supported = False
openmp_exception = exception
if not openmp_supported:
if os.getenv("SKLEARN_FAIL_NO_OPENMP"):
raise Exception(
"Failed to build scikit-learn with OpenMP support"
) from openmp_exception
else:
message = textwrap.dedent(
"""
***********
* WARNING *
***********
It seems that scikit-learn cannot be built with OpenMP.
- Make sure you have followed the installation instructions:
https://scikit-learn.org/dev/developers/advanced_installation.html
- If your compiler supports OpenMP but you still see this
message, please submit a bug report at:
https://github.com/scikit-learn/scikit-learn/issues
- The build will continue with OpenMP-based parallelism
disabled. Note however that some estimators will run in
sequential mode instead of leveraging thread-based
parallelism.
***
"""
)
warnings.warn(message)
return openmp_supported

View File

@ -0,0 +1,75 @@
"""Helpers to check build environment before actual build of scikit-learn"""
import glob
import os
import subprocess
import sys
import tempfile
import textwrap
from setuptools.command.build_ext import customize_compiler, new_compiler
def compile_test_program(code, extra_preargs=None, extra_postargs=None):
"""Check that some C code can be compiled and run"""
ccompiler = new_compiler()
customize_compiler(ccompiler)
start_dir = os.path.abspath(".")
with tempfile.TemporaryDirectory() as tmp_dir:
try:
os.chdir(tmp_dir)
# Write test program
with open("test_program.c", "w") as f:
f.write(code)
os.mkdir("objects")
# Compile, test program
ccompiler.compile(
["test_program.c"], output_dir="objects", extra_postargs=extra_postargs
)
# Link test program
objects = glob.glob(os.path.join("objects", "*" + ccompiler.obj_extension))
ccompiler.link_executable(
objects,
"test_program",
extra_preargs=extra_preargs,
extra_postargs=extra_postargs,
)
if "PYTHON_CROSSENV" not in os.environ:
# Run test program if not cross compiling
# will raise a CalledProcessError if return code was non-zero
output = subprocess.check_output("./test_program")
output = output.decode(sys.stdout.encoding or "utf-8").splitlines()
else:
# Return an empty output if we are cross compiling
# as we cannot run the test_program
output = []
except Exception:
raise
finally:
os.chdir(start_dir)
return output
def basic_check_build():
"""Check basic compilation and linking of C code"""
if "PYODIDE" in os.environ:
# The following check won't work in pyodide
return
code = textwrap.dedent(
"""\
#include <stdio.h>
int main(void) {
return 0;
}
"""
)
compile_test_program(code)

View File

@ -0,0 +1,57 @@
import argparse
import os
from Cython import Tempita as tempita
# XXX: If this import ever fails (does it really?), vendor either
# cython.tempita or numpy/npy_tempita.
def process_tempita(fromfile, outfile=None):
"""Process tempita templated file and write out the result.
The template file is expected to end in `.c.tp` or `.pyx.tp`:
E.g. processing `template.c.in` generates `template.c`.
"""
with open(fromfile, "r", encoding="utf-8") as f:
template_content = f.read()
template = tempita.Template(template_content)
content = template.substitute()
with open(outfile, "w", encoding="utf-8") as f:
f.write(content)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("infile", type=str, help="Path to the input file")
parser.add_argument("-o", "--outdir", type=str, help="Path to the output directory")
parser.add_argument(
"-i",
"--ignore",
type=str,
help=(
"An ignored input - may be useful to add a "
"dependency between custom targets"
),
)
args = parser.parse_args()
if not args.infile.endswith(".tp"):
raise ValueError(f"Unexpected extension: {args.infile}")
if not args.outdir:
raise ValueError("Missing `--outdir` argument to tempita.py")
outdir_abs = os.path.join(os.getcwd(), args.outdir)
outfile = os.path.join(
outdir_abs, os.path.splitext(os.path.split(args.infile)[1])[0]
)
process_tempita(args.infile, outfile)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,13 @@
#!/usr/bin/env python3
"""Extract version number from __init__.py"""
import os
sklearn_init = os.path.join(os.path.dirname(__file__), "../__init__.py")
data = open(sklearn_init).readlines()
version_line = next(line for line in data if line.startswith("__version__"))
version = version_line.strip().split(" = ")[1].replace('"', "").replace("'", "")
print(version)