From aa03c6ea64b103440274337d3214c976f7bd5d56 Mon Sep 17 00:00:00 2001 From: bruno <97033386+bruno-at-orange@users.noreply.github.com> Date: Fri, 26 Jun 2026 15:40:58 +0200 Subject: [PATCH 1/2] fix: use sysconfig to locate khiops_env.cmd on Windows On Windows without a virtual environment, sys.executable points to the Python binary directory (e.g. C:\Python312\) while pip installs console scripts into the Scripts\ subdirectory. Using Path(sys.executable).parent therefore missed khiops_env.cmd. Replace with sysconfig.get_path("scripts") which always returns the correct Scripts directory for the active Python environment, covering virtual envs, system installs, and user installs. Also check the user-scheme scripts dir (nt_user) as a fallback for pip install --user. --- khiops/core/internals/runner.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/khiops/core/internals/runner.py b/khiops/core/internals/runner.py index b16a8acd..fa651989 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 @@ -970,15 +971,23 @@ def _initialize_khiops_environment(self): ) # 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: + # Use sysconfig.get_path("scripts") to find the Scripts directory. + # This is the directory where pip places console scripts and works + # correctly in all scenarios: virtual envs, system installs, and + # user installs (unlike Path(sys.executable).parent which only + # points to the Scripts dir when inside a virtual env). + scripts_dirs = [sysconfig.get_path("scripts")] + # Also check the user scripts directory to handle `pip install --user` + user_scripts = sysconfig.get_path("scripts", f"{os.name}_user") + if user_scripts and user_scripts not in scripts_dirs: + scripts_dirs.append(user_scripts) + khiops_env_path = None + for scripts_dir in scripts_dirs: + candidate = os.path.join(scripts_dir, "khiops_env.cmd") + if os.path.exists(candidate): + khiops_env_path = candidate + break + if khiops_env_path is None: raise KhiopsEnvironmentError( "No 'khiops_env.cmd' found in the current environment. " "Make sure you have installed Khiops properly. " From 7d4e314aa6c1028b3962870a6f995bedb03efaba Mon Sep 17 00:00:00 2001 From: bruno <97033386+bruno-at-orange@users.noreply.github.com> Date: Fri, 26 Jun 2026 17:01:18 +0200 Subject: [PATCH 2/2] refactor: restructure khiops_env lookup with match/case and extract helper - Replace if/elif chain with a match/case on installation_method for clarity; each case now owns its full lookup logic - Extract _infer_khiops_env_from_path() helper (shutil.which + error) used by 'conda' and 'pip'-on-Unix cases - Fix 'conda' and 'pip'+Unix cases: return value of helper was previously discarded, leaving khiops_env_path unbound - Fix 'pip' case: restore missing distribution('khiops-core') check - pip + Windows: use sysconfig.get_path('scripts') / 'nt_user' scheme determined by whether this library lives under user site-packages, avoiding a blind directory search and correctly handling venv, system-wide and --user installs --- khiops/core/internals/runner.py | 112 +++++++++++++++++--------------- 1 file changed, 58 insertions(+), 54 deletions(-) diff --git a/khiops/core/internals/runner.py b/khiops/core/internals/runner.py index fa651989..c8f3d638 100644 --- a/khiops/core/internals/runner.py +++ b/khiops/core/internals/runner.py @@ -956,63 +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" + 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" ) - # Search for the `khiops_env` script location - if platform.system() == "Windows": - # Use sysconfig.get_path("scripts") to find the Scripts directory. - # This is the directory where pip places console scripts and works - # correctly in all scenarios: virtual envs, system installs, and - # user installs (unlike Path(sys.executable).parent which only - # points to the Scripts dir when inside a virtual env). - scripts_dirs = [sysconfig.get_path("scripts")] - # Also check the user scripts directory to handle `pip install --user` - user_scripts = sysconfig.get_path("scripts", f"{os.name}_user") - if user_scripts and user_scripts not in scripts_dirs: - scripts_dirs.append(user_scripts) - khiops_env_path = None - for scripts_dir in scripts_dirs: - candidate = os.path.join(scripts_dir, "khiops_env.cmd") - if os.path.exists(candidate): - khiops_env_path = candidate - break - if khiops_env_path is None: - 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." - ) - # 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, @@ -1072,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()