@@ -934,21 +934,44 @@ pub(crate) fn find_python_installation(
934934 return result;
935935 } ;
936936
937- // If it's a pre-release, and pre-releases aren't allowed skip it but store it for later
937+ // Check if we need to skip the interpreter because it is "not allowed", e.g., if it is a
938+ // pre-release version or an alternative implementation, using it requires opt-in.
939+
940+ // If the interpreter has a default executable name, e.g. `python`, and was found on the
941+ // search path, we consider this opt-in to use it.
942+ let has_default_executable_name = installation. interpreter . has_default_executable_name ( )
943+ && installation. source == PythonSource :: SearchPath ;
944+
945+ // If it's a pre-release and pre-releases aren't allowed, skip it — but store it for later
946+ // since we'll use a pre-release if no other versions are available.
938947 if installation. python_version ( ) . pre ( ) . is_some ( )
939948 && !request. allows_prereleases ( )
940949 && !installation. source . allows_prereleases ( )
950+ && !has_default_executable_name
941951 {
942952 debug ! ( "Skipping pre-release {}" , installation. key( ) ) ;
943953 first_prerelease = Some ( installation. clone ( ) ) ;
944954 continue ;
945955 }
946956
957+ // If it's an alternative implementation and alternative implementations aren't allowed,
958+ // skip it. Note we avoid querying these interpreters at all if they're on the search path
959+ // and are not requested, but other sources such as the managed installations will include
960+ // them.
961+ if installation. is_alternative_implementation ( )
962+ && !request. allows_alternative_implementations ( )
963+ && !installation. source . allows_alternative_implementations ( )
964+ && !has_default_executable_name
965+ {
966+ debug ! ( "Skipping alternative implementation {}" , installation. key( ) ) ;
967+ continue ;
968+ }
969+
947970 // If we didn't skip it, this is the installation to use
948971 return result;
949972 }
950973
951- // If we only found pre-releases, they're implicitly allowed and we should return the first one
974+ // If we only found pre-releases, they're implicitly allowed and we should return the first one.
952975 if let Some ( installation) = first_prerelease {
953976 return Ok ( Ok ( installation) ) ;
954977 }
@@ -1205,10 +1228,7 @@ impl PythonRequest {
12051228 for implementation in
12061229 ImplementationName :: long_names ( ) . chain ( ImplementationName :: short_names ( ) )
12071230 {
1208- if let Some ( remainder) = value
1209- . to_ascii_lowercase ( )
1210- . strip_prefix ( Into :: < & str > :: into ( implementation) )
1211- {
1231+ if let Some ( remainder) = value. to_ascii_lowercase ( ) . strip_prefix ( implementation) {
12121232 // e.g. `pypy`
12131233 if remainder. is_empty ( ) {
12141234 return Self :: Implementation (
@@ -1369,6 +1389,7 @@ impl PythonRequest {
13691389 }
13701390 }
13711391
1392+ /// Whether this request opts-in to a pre-release Python version.
13721393 pub ( crate ) fn allows_prereleases ( & self ) -> bool {
13731394 match self {
13741395 Self :: Default => false ,
@@ -1381,6 +1402,19 @@ impl PythonRequest {
13811402 }
13821403 }
13831404
1405+ /// Whether this request opts-in to an alternative Python implementation, e.g., PyPy.
1406+ pub ( crate ) fn allows_alternative_implementations ( & self ) -> bool {
1407+ match self {
1408+ Self :: Default => false ,
1409+ Self :: Any => true ,
1410+ Self :: Version ( _) => false ,
1411+ Self :: Directory ( _) | Self :: File ( _) | Self :: ExecutableName ( _) => true ,
1412+ Self :: Implementation ( _) => true ,
1413+ Self :: ImplementationVersion ( _, _) => true ,
1414+ Self :: Key ( request) => request. allows_alternative_implementations ( ) ,
1415+ }
1416+ }
1417+
13841418 pub ( crate ) fn is_explicit_system ( & self ) -> bool {
13851419 matches ! ( self , Self :: File ( _) | Self :: Directory ( _) )
13861420 }
@@ -1410,7 +1444,7 @@ impl PythonSource {
14101444 matches ! ( self , Self :: Managed )
14111445 }
14121446
1413- /// Whether a pre-release Python installation from the source should be used without opt-in.
1447+ /// Whether a pre-release Python installation from this source can be used without opt-in.
14141448 pub ( crate ) fn allows_prereleases ( self ) -> bool {
14151449 match self {
14161450 Self :: Managed | Self :: Registry | Self :: MicrosoftStore => false ,
@@ -1422,6 +1456,18 @@ impl PythonSource {
14221456 | Self :: DiscoveredEnvironment => true ,
14231457 }
14241458 }
1459+
1460+ /// Whether an alternative Python implementation from this source can be used without opt-in.
1461+ pub ( crate ) fn allows_alternative_implementations ( self ) -> bool {
1462+ match self {
1463+ Self :: Managed | Self :: Registry | Self :: SearchPath | Self :: MicrosoftStore => false ,
1464+ Self :: CondaPrefix
1465+ | Self :: ProvidedPath
1466+ | Self :: ParentInterpreter
1467+ | Self :: ActiveEnvironment
1468+ | Self :: DiscoveredEnvironment => true ,
1469+ }
1470+ }
14251471}
14261472
14271473impl PythonPreference {
0 commit comments