Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
73 changes: 55 additions & 18 deletions khiops/core/internals/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []

Expand Down Expand Up @@ -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)
Expand All @@ -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


Expand Down Expand Up @@ -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 "
Expand All @@ -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 "
Expand Down Expand Up @@ -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
Expand All @@ -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 "
Expand All @@ -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 "
Expand All @@ -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()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would .lower() the user_site_packages_dir variable on Windows as well, to observe the case-insesitiveness invariant on Windows.

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 "
Expand All @@ -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
Expand Down
Loading