@@ -510,7 +510,7 @@ System site-packages will not be used for module resolution.",
510
510
// of the dir we're looking for.
511
511
let version = version. as_ref ( ) . map ( |v| v. version ) ;
512
512
if let Some ( system_sys_prefix) =
513
- SysPrefixPath :: from_executable_home_path ( base_executable_home_path)
513
+ SysPrefixPath :: from_executable_home_path_real ( system , base_executable_home_path)
514
514
{
515
515
let real_stdlib_directory = real_stdlib_directory_from_sys_prefix (
516
516
& system_sys_prefix,
@@ -1243,6 +1243,81 @@ impl SysPrefixPath {
1243
1243
} )
1244
1244
}
1245
1245
}
1246
+ /// Like `from_executable_home_path` but attempts to resolve through symlink facades
1247
+ /// to find a sys prefix that will actually contain the stdlib.
1248
+ fn from_executable_home_path_real ( system : & dyn System , path : & PythonHomePath ) -> Option < Self > {
1249
+ let mut home_path = path. 0 . clone ( ) ;
1250
+
1251
+ // Try to find the python executable in the given directory and canonicalize it
1252
+ // to resolve any symlink. This is (at least) necessary for homebrew pythons
1253
+ // and the macOS system python.
1254
+ //
1255
+ // In python installations like homebrew, the home path points to a directory like
1256
+ // `/opt/homebrew/opt/[email protected] /bin` and indeed if you look for `../lib/python3.13/`
1257
+ // you *will* find `site-packages` but you *won't* find the stdlib! (For the macOS
1258
+ // system install you won't even find `site-packages` here.)
1259
+ //
1260
+ // However if you look at `/opt/homebrew/opt/[email protected] /bin/python3.13` (the actual
1261
+ // python executable in that dir) you will find that it's a symlink to something like
1262
+ // `../Frameworks/Python.framework/Versions/3.13/bin/python3.13`
1263
+ //
1264
+ // From this Framework binary path if you go to `../../lib/python3.13/` you will then
1265
+ // find the python stdlib as expected (and a different instance of site-packages).
1266
+ //
1267
+ // FIXME: it would be nice to include a "we know the python name" fastpath like in
1268
+ // `real_stdlib_directory_from_sys_prefix`.
1269
+ if let Ok ( dir) = system. read_directory ( & home_path) {
1270
+ for entry_result in dir {
1271
+ let Ok ( entry) = entry_result else {
1272
+ continue ;
1273
+ } ;
1274
+
1275
+ if entry. file_type ( ) . is_directory ( ) {
1276
+ continue ;
1277
+ }
1278
+
1279
+ let path = entry. into_path ( ) ;
1280
+
1281
+ let name = path. file_name ( ) . expect (
1282
+ "File name to be non-null because path is guaranteed to be a child of `lib`" ,
1283
+ ) ;
1284
+
1285
+ if !( name. starts_with ( "python3." ) || name. starts_with ( "pypy3." ) ) {
1286
+ continue ;
1287
+ }
1288
+
1289
+ if system. is_directory ( & path) {
1290
+ continue ;
1291
+ }
1292
+
1293
+ let Ok ( canonical_path) = system. canonicalize_path ( & path) else {
1294
+ continue ;
1295
+ } ;
1296
+
1297
+ let Some ( parent) = canonical_path. parent ( ) else {
1298
+ continue ;
1299
+ } ;
1300
+
1301
+ home_path = parent. to_path_buf ( ) ;
1302
+ break ;
1303
+ }
1304
+ }
1305
+
1306
+ // No need to check whether `path.parent()` is a directory:
1307
+ // the parent of a canonicalised path that is known to exist
1308
+ // is guaranteed to be a directory.
1309
+ if cfg ! ( target_os = "windows" ) {
1310
+ Some ( Self {
1311
+ inner : home_path. to_path_buf ( ) ,
1312
+ origin : SysPrefixPathOrigin :: DerivedFromPyvenvCfg ,
1313
+ } )
1314
+ } else {
1315
+ home_path. parent ( ) . map ( |home_path| Self {
1316
+ inner : home_path. to_path_buf ( ) ,
1317
+ origin : SysPrefixPathOrigin :: DerivedFromPyvenvCfg ,
1318
+ } )
1319
+ }
1320
+ }
1246
1321
}
1247
1322
1248
1323
impl Deref for SysPrefixPath {
0 commit comments