diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a8f5ded..d6a37bc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,9 @@ - (`sklearn`) `n_feature_parts` parameter to the supervised estimators ### Fixed -- (`sklearn`) Default value of `n_features` for the supervised estimators +- (`sklearn`) Default value of `n_features` for the supervised estimators. +- *Internals*: + - Detection of unsupported installation modes on Windows operating systems. ## 11.0.0.2 - 2026-01-26 diff --git a/khiops/core/internals/runner.py b/khiops/core/internals/runner.py index c464e88a..b651ec03 100644 --- a/khiops/core/internals/runner.py +++ b/khiops/core/internals/runner.py @@ -542,14 +542,14 @@ def _build_status_message(self): assert ( os.path.basename(Path(__file__).parents[2]) == "khiops" ), "Please fix the `Path.parents` in this method " - library_root_dir = Path(__file__).parents[2] + library_root_dir_path = Path(__file__).parents[2] status_msg = "Khiops Python library settings\n" status_msg += f"version : {khiops.__version__}\n" status_msg += f"runner class : {self.__class__.__name__}\n" status_msg += f"root temp dir : {self.root_temp_dir}\n" status_msg += f"sample datasets dir : {samples_dir_path}\n" - status_msg += f"library root dir : {library_root_dir}\n" + status_msg += f"library root dir : {library_root_dir_path}\n" error_list = [] @@ -1127,7 +1127,7 @@ def _initialize_khiops_version(self): stacklevel=3, ) - def _detect_library_installation_incompatibilities(self, library_root_dir): + def _detect_library_installation_incompatibilities(self, library_root_dir_path): """Detects known incompatible installations of this library in the 3 installation modes see `_infer_khiops_installation_method` (binary+pip, conda, conda-based) @@ -1137,7 +1137,7 @@ def _detect_library_installation_incompatibilities(self, library_root_dir): Parameters ---------- - library_root_dir : PosixPath + library_root_dir_path : PosixPath path to this current library @@ -1172,10 +1172,18 @@ def _detect_library_installation_incompatibilities(self, library_root_dir): ) warning_list.append(warning) + # Store in separate variable(s) to abstract over the path casing on Windows + library_root_dir = str(library_root_dir_path) + conda_prefix = os.environ["CONDA_PREFIX"] + if platform.system() == "Windows": + # warning : paths are case-insensitive under Windows + library_root_dir = library_root_dir.lower() + conda_prefix = conda_prefix.lower() # the conda environment must match the library installation - if not str(library_root_dir).startswith(os.environ["CONDA_PREFIX"]): + if not library_root_dir.startswith(conda_prefix): error = ( - f"Khiops Python library installation path '{library_root_dir}' " + f"Khiops Python library installation " + f"path '{library_root_dir_path}' " "does not match the current Conda environment " f"'{os.environ['CONDA_PREFIX']}'. " "Either deactivate the current Conda environment " @@ -1184,9 +1192,14 @@ def _detect_library_installation_incompatibilities(self, library_root_dir): "Go to https://khiops.org for instructions.\n" ) error_list.append(error) + # Store in separate variable to abstract over the path casing on Windows + khiops_path = self.khiops_path + if platform.system() == "Windows": + # warning : paths are case-insensitive under Windows + khiops_path = khiops_path.lower() # the khiops executable path must also match the conda environment one # meaning khiops core was installed using conda - if not self.khiops_path.startswith(os.environ["CONDA_PREFIX"]): + if not khiops_path.startswith(conda_prefix): error = ( f"Khiops binary path '{self.khiops_path}' " "does not match the current Conda environment " @@ -1226,11 +1239,19 @@ def _detect_library_installation_incompatibilities(self, library_root_dir): # no further check cannot be performed) base_dir = _infer_base_dir_for_conda_based_or_pip_installations() if len(base_dir) > 0: + # Store in separate variable(s) to abstract over + # the path casing on Windows + base_prefix = sys.base_prefix + sys_prefix = sys.prefix + if platform.system() == "Windows": + # warning : paths are case-insensitive under Windows + base_prefix = base_prefix.lower() + sys_prefix = sys_prefix.lower() # within a virtual env, sys.prefix is set to the virtual env folder # whereas sys.base_prefix remains unchanged. # Please be aware that if a python executable of a virtual env is used # the corresponding virtual env is activated and sys.prefix updated - if sys.base_prefix != sys.prefix: + if base_prefix != sys_prefix: # the python executable location # (within the virtual env or the conda-based env) # must match the library installation @@ -1240,18 +1261,22 @@ def _detect_library_installation_incompatibilities(self, library_root_dir): # Under Windows, there are two cases : ( # for conda-based installations python is inside 'base_dir' - str(Path(sys.executable).parents[0]) != base_dir + # warning : paths are case-insensitive under Windows + str(Path(sys.executable.lower()).parents[0]) + != base_dir.lower() and # for 'binary+pip' installations (within a virtual env) # python is inside 'base_dir'/Scripts - str(Path(sys.executable).parents[1]) != base_dir + # warning : paths are case-insensitive under Windows + str(Path(sys.executable.lower()).parents[1]) + != base_dir.lower() ) # Under Linux or MacOS a bin/ folder exists or str(Path(sys.executable).parents[1]) != base_dir ): error = ( "Khiops Python library installation path " - f"'{library_root_dir}' " + f"'{library_root_dir_path}' " "does not match the current python environment " f"('{sys.executable}'). " "Go to https://khiops.org for instructions " @@ -1260,14 +1285,22 @@ def _detect_library_installation_incompatibilities(self, library_root_dir): ) error_list.append(error) else: + # Store in separate variable(s) to abstract + # over the path casing on Windows + sys_executable = sys.executable + base_prefix = sys.base_prefix + if platform.system() == "Windows": + # warning : paths are case-insensitive under Windows + sys_executable = sys_executable.lower() + base_prefix = base_prefix.lower() # the installation is not within a virtual env # (sys.base_prefix == sys.prefix) - if not sys.executable.startswith(sys.base_prefix): + if not sys_executable.startswith(base_prefix): # the executable is not the expected one # (the system-wide python) error = ( "Khiops Python library installed in " - f"'{library_root_dir}' " + f"'{library_root_dir_path}' " "is run with an unexpected executable " f"'{sys.executable}'. " "The system-wide python located in " @@ -1281,14 +1314,18 @@ def _detect_library_installation_incompatibilities(self, library_root_dir): # fetch the 'User site' site-packages path # which is already adapted for each OS (Windows, MacOS, Linux) user_site_packages_dir = site.getusersitepackages() - if not str(library_root_dir).startswith(user_site_packages_dir): + library_root_dir = str(library_root_dir_path) + if platform.system() == "Windows": + # warning : paths are case-insensitive under Windows + library_root_dir = library_root_dir.lower() + if not library_root_dir.startswith(user_site_packages_dir): # the library is not installed on the 'User site' - if not str(library_root_dir).startswith(sys.base_prefix): + if not library_root_dir.startswith(base_prefix): # the library is supposed to be installed system-wide, # but it seems that the location is wrong error = ( "Khiops Python library installation path " - f"'{library_root_dir}' " + f"'{library_root_dir_path}' " "does not match the system-wide Python prefix in " f"'{sys.base_prefix}'. " "Go to https://khiops.org for instructions " @@ -1303,10 +1340,10 @@ def _build_status_message(self): # Call the parent's method status_msg, error_list, warning_list = super()._build_status_message() - library_root_dir = Path(__file__).parents[2] + library_root_dir_path = Path(__file__).parents[2] installation_errors, installation_warnings = ( - self._detect_library_installation_incompatibilities(library_root_dir) + self._detect_library_installation_incompatibilities(library_root_dir_path) ) # Build the messages for install type and mpi