diff --git a/khiops/core/internals/runner.py b/khiops/core/internals/runner.py index b16a8acd..c8f3d638 100644 --- a/khiops/core/internals/runner.py +++ b/khiops/core/internals/runner.py @@ -20,6 +20,7 @@ import site import subprocess import sys +import sysconfig import tempfile import uuid import warnings @@ -955,55 +956,56 @@ def __init__(self): def _initialize_khiops_environment(self): installation_method = _infer_khiops_installation_method() - # in a 'pip' installation method, - # the current Khiops Python library depends on the Khiops binary-only - # PyPI package. - # Let's ensure this dependency has not disappeared. - if installation_method == "pip": - try: - distribution("khiops-core") - except PackageNotFoundError as exc: - raise KhiopsEnvironmentError( - f"The Khiops binaries are not installed properly: {exc}. " - "Re-install the Khiops Python library to automatically install " - "Khiops. Go to https://khiops.org for more information.\n" - ) - # Search for the `khiops_env` script location - if platform.system() == "Windows": - sys_executable_direct_parent = Path(sys.executable).parents[0] - probable_khiops_env = os.path.join( - sys_executable_direct_parent, "khiops_env.cmd" - ) - # The script is found in the current environment - if os.path.exists(probable_khiops_env): - khiops_env_path = probable_khiops_env - # Raise error otherwise - else: - raise KhiopsEnvironmentError( - "No 'khiops_env.cmd' found in the current environment. " - "Make sure you have installed Khiops properly. " - "Go to https://khiops.org for more information." + match installation_method: + case "conda-based": + # In conda-based environments, khiops_env is not in PATH; + # its location must be inferred from the conda env directory. + khiops_env_path = os.path.join( + _infer_env_bin_dir_for_conda_based_installations(), "khiops_env" ) - # In Conda-based environments, `khiops_env` might not be in the PATH, - # hence its path must be inferred - elif installation_method == "conda-based": - khiops_env_path = os.path.join( - _infer_env_bin_dir_for_conda_based_installations(), "khiops_env" - ) - if platform.system() == "Windows": - khiops_env_path += ".cmd" - - # On UNIX or Conda, khiops_env is always in path for a proper installation - else: - khiops_env_path = shutil.which("khiops_env") - if khiops_env_path is None: + if platform.system() == "Windows": + khiops_env_path += ".cmd" + case "conda": + # In an activated conda environment, khiops_env is in PATH. + khiops_env_path = self._infer_khiops_env_from_path(installation_method) + case "pip": + # Ensure the binary dependency is still installed. + try: + distribution("khiops-core") + except PackageNotFoundError as exc: + raise KhiopsEnvironmentError( + f"The Khiops binaries are not installed properly: {exc}. " + "Re-install the Khiops Python library to automatically install " + "Khiops. Go to https://khiops.org for more information.\n" + ) + if platform.system() == "Windows": + # Determine the Scripts directory where pip placed khiops_env.cmd. + # If this library is installed under the user site-packages directory, + # khiops-core (and its khiops_env.cmd) was also installed there with + # `pip install --user`, so use the user Scripts directory. Otherwise + # use the standard Scripts directory (venv or system-wide install). + # This avoids an ambiguous search and mirrors how pip resolves scripts. + library_root_dir_path = Path(__file__).parents[2] + user_site_packages_path = Path(site.getusersitepackages()) + if library_root_dir_path.is_relative_to(user_site_packages_path): + scripts_dir = sysconfig.get_path("scripts", "nt_user") + else: + scripts_dir = sysconfig.get_path("scripts") + khiops_env_path = os.path.join(scripts_dir, "khiops_env.cmd") + if not os.path.exists(khiops_env_path): + raise KhiopsEnvironmentError( + "No 'khiops_env.cmd' found in the current environment. " + "Make sure you have installed Khiops properly. " + "Go to https://khiops.org for more information." + ) + else: + # On UNIX, pip places khiops_env in the bin directory, which is in PATH. + khiops_env_path = self._infer_khiops_env_from_path(installation_method) + case _: raise KhiopsEnvironmentError( - "The 'khiops_env' script not found for the current " - f"'{installation_method}' installation method. Make sure " - "you have installed Khiops properly. " - "Go to https://khiops.org for more information." + f"Unknown installation method '{installation_method}'." ) - + with subprocess.Popen( [khiops_env_path, "--env"], stdout=subprocess.PIPE, @@ -1063,6 +1065,17 @@ def _initialize_khiops_environment(self): # Initialize the default samples dir self._initialize_default_samples_dir() + def _infer_khiops_env_from_path(self, installation_method): + khiops_env_path = shutil.which("khiops_env") + if khiops_env_path is None: + raise KhiopsEnvironmentError( + "The 'khiops_env' script not found for the current " + f"'{installation_method}' installation method. Make sure " + "you have installed Khiops properly. " + "Go to https://khiops.org for more information." + ) + return khiops_env_path + def _initialize_default_samples_dir(self): """See class docstring""" samples_dir = get_default_samples_dir()