Skip to content

Commit 50ef378

Browse files
authored
Fix the detection of unhappy installation paths under Windows (#554)
- External tools like VSCode can modify for example the case of the path saved in `sys.executable` - As a general rule a pathlib.Path object is always built. Depending on the OS either a PosixPath or a WindowsPath instance is created. The latter considers paths as case-insensitive.
1 parent 465566c commit 50ef378

File tree

2 files changed

+38
-21
lines changed

2 files changed

+38
-21
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
- (`sklearn`) `n_feature_parts` parameter to the supervised estimators
1313

1414
### Fixed
15-
- (`sklearn`) Default value of `n_features` for the supervised estimators
15+
- (`sklearn`) Default value of `n_features` for the supervised estimators.
16+
- *Internals*:
17+
- Detection of unsupported installation modes on Windows operating systems.
1618

1719
## 11.0.0.2 - 2026-01-26
1820

khiops/core/internals/runner.py

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -542,14 +542,14 @@ def _build_status_message(self):
542542
assert (
543543
os.path.basename(Path(__file__).parents[2]) == "khiops"
544544
), "Please fix the `Path.parents` in this method "
545-
library_root_dir = Path(__file__).parents[2]
545+
library_root_dir_path = Path(__file__).parents[2]
546546

547547
status_msg = "Khiops Python library settings\n"
548548
status_msg += f"version : {khiops.__version__}\n"
549549
status_msg += f"runner class : {self.__class__.__name__}\n"
550550
status_msg += f"root temp dir : {self.root_temp_dir}\n"
551551
status_msg += f"sample datasets dir : {samples_dir_path}\n"
552-
status_msg += f"library root dir : {library_root_dir}\n"
552+
status_msg += f"library root dir : {library_root_dir_path}\n"
553553

554554
error_list = []
555555

@@ -1127,7 +1127,7 @@ def _initialize_khiops_version(self):
11271127
stacklevel=3,
11281128
)
11291129

1130-
def _detect_library_installation_incompatibilities(self, library_root_dir):
1130+
def _detect_library_installation_incompatibilities(self, library_root_dir_path):
11311131
"""Detects known incompatible installations of this library
11321132
in the 3 installation modes see `_infer_khiops_installation_method`
11331133
(binary+pip, conda, conda-based)
@@ -1137,7 +1137,7 @@ def _detect_library_installation_incompatibilities(self, library_root_dir):
11371137
11381138
Parameters
11391139
----------
1140-
library_root_dir : PosixPath
1140+
library_root_dir_path : Path
11411141
path to this current library
11421142
11431143
@@ -1172,10 +1172,12 @@ def _detect_library_installation_incompatibilities(self, library_root_dir):
11721172
)
11731173
warning_list.append(warning)
11741174

1175+
conda_prefix_path = Path(os.environ["CONDA_PREFIX"])
11751176
# the conda environment must match the library installation
1176-
if not str(library_root_dir).startswith(os.environ["CONDA_PREFIX"]):
1177+
if not library_root_dir_path.is_relative_to(conda_prefix_path):
11771178
error = (
1178-
f"Khiops Python library installation path '{library_root_dir}' "
1179+
"Khiops Python library installation "
1180+
f"path '{library_root_dir_path}' "
11791181
"does not match the current Conda environment "
11801182
f"'{os.environ['CONDA_PREFIX']}'. "
11811183
"Either deactivate the current Conda environment "
@@ -1184,9 +1186,10 @@ def _detect_library_installation_incompatibilities(self, library_root_dir):
11841186
"Go to https://khiops.org for instructions.\n"
11851187
)
11861188
error_list.append(error)
1189+
khiops_path = Path(self.khiops_path)
11871190
# the khiops executable path must also match the conda environment one
11881191
# meaning khiops core was installed using conda
1189-
if not self.khiops_path.startswith(os.environ["CONDA_PREFIX"]):
1192+
if not khiops_path.is_relative_to(conda_prefix_path):
11901193
error = (
11911194
f"Khiops binary path '{self.khiops_path}' "
11921195
"does not match the current Conda environment "
@@ -1226,32 +1229,39 @@ def _detect_library_installation_incompatibilities(self, library_root_dir):
12261229
# no further check cannot be performed)
12271230
base_dir = _infer_base_dir_for_conda_based_or_pip_installations()
12281231
if len(base_dir) > 0:
1232+
# Store in separate variable(s) to abstract over
1233+
# the path casing on Windows
1234+
sys_base_prefix_path = Path(sys.base_prefix)
1235+
sys_prefix_path = Path(sys.prefix)
12291236
# within a virtual env, sys.prefix is set to the virtual env folder
12301237
# whereas sys.base_prefix remains unchanged.
12311238
# Please be aware that if a python executable of a virtual env is used
12321239
# the corresponding virtual env is activated and sys.prefix updated
1233-
if sys.base_prefix != sys.prefix:
1240+
if sys_base_prefix_path != sys_prefix_path:
12341241
# the python executable location
12351242
# (within the virtual env or the conda-based env)
12361243
# must match the library installation
1244+
sys_executable_direct_parent = Path(sys.executable).parents[0]
1245+
sys_executable_grand_parent = Path(sys.executable).parents[1]
1246+
base_dir_path = Path(base_dir)
12371247
if (
12381248
platform.system() == "Windows"
12391249
and
12401250
# Under Windows, there are two cases :
12411251
(
12421252
# for conda-based installations python is inside 'base_dir'
1243-
str(Path(sys.executable).parents[0]) != base_dir
1253+
sys_executable_direct_parent != base_dir_path
12441254
and
12451255
# for 'binary+pip' installations (within a virtual env)
12461256
# python is inside 'base_dir'/Scripts
1247-
str(Path(sys.executable).parents[1]) != base_dir
1257+
sys_executable_grand_parent != base_dir_path
12481258
)
12491259
# Under Linux or MacOS a bin/ folder exists
1250-
or str(Path(sys.executable).parents[1]) != base_dir
1260+
or sys_executable_grand_parent != base_dir_path
12511261
):
12521262
error = (
12531263
"Khiops Python library installation path "
1254-
f"'{library_root_dir}' "
1264+
f"'{library_root_dir_path}' "
12551265
"does not match the current python environment "
12561266
f"('{sys.executable}'). "
12571267
"Go to https://khiops.org for instructions "
@@ -1260,14 +1270,15 @@ def _detect_library_installation_incompatibilities(self, library_root_dir):
12601270
)
12611271
error_list.append(error)
12621272
else:
1273+
sys_executable_path = Path(sys.executable)
12631274
# the installation is not within a virtual env
12641275
# (sys.base_prefix == sys.prefix)
1265-
if not sys.executable.startswith(sys.base_prefix):
1276+
if not sys_executable_path.is_relative_to(sys_base_prefix_path):
12661277
# the executable is not the expected one
12671278
# (the system-wide python)
12681279
error = (
12691280
"Khiops Python library installed in "
1270-
f"'{library_root_dir}' "
1281+
f"'{library_root_dir_path}' "
12711282
"is run with an unexpected executable "
12721283
f"'{sys.executable}'. "
12731284
"The system-wide python located in "
@@ -1280,15 +1291,19 @@ def _detect_library_installation_incompatibilities(self, library_root_dir):
12801291
error_list.append(error)
12811292
# fetch the 'User site' site-packages path
12821293
# which is already adapted for each OS (Windows, MacOS, Linux)
1283-
user_site_packages_dir = site.getusersitepackages()
1284-
if not str(library_root_dir).startswith(user_site_packages_dir):
1294+
user_site_packages_path = Path(site.getusersitepackages())
1295+
if not library_root_dir_path.is_relative_to(
1296+
user_site_packages_path
1297+
):
12851298
# the library is not installed on the 'User site'
1286-
if not str(library_root_dir).startswith(sys.base_prefix):
1299+
if not library_root_dir_path.is_relative_to(
1300+
sys_base_prefix_path
1301+
):
12871302
# the library is supposed to be installed system-wide,
12881303
# but it seems that the location is wrong
12891304
error = (
12901305
"Khiops Python library installation path "
1291-
f"'{library_root_dir}' "
1306+
f"'{library_root_dir_path}' "
12921307
"does not match the system-wide Python prefix in "
12931308
f"'{sys.base_prefix}'. "
12941309
"Go to https://khiops.org for instructions "
@@ -1303,10 +1318,10 @@ def _build_status_message(self):
13031318
# Call the parent's method
13041319
status_msg, error_list, warning_list = super()._build_status_message()
13051320

1306-
library_root_dir = Path(__file__).parents[2]
1321+
library_root_dir_path = Path(__file__).parents[2]
13071322

13081323
installation_errors, installation_warnings = (
1309-
self._detect_library_installation_incompatibilities(library_root_dir)
1324+
self._detect_library_installation_incompatibilities(library_root_dir_path)
13101325
)
13111326

13121327
# Build the messages for install type and mpi

0 commit comments

Comments
 (0)