@@ -203,6 +203,7 @@ pub enum VersionRequest {
203203 MajorMinor ( u8 , u8 , PythonVariant ) ,
204204 MajorMinorPatch ( u8 , u8 , u8 , PythonVariant ) ,
205205 MajorMinorPrerelease ( u8 , u8 , Prerelease , PythonVariant ) ,
206+ MajorMinorPatchPrerelease ( u8 , u8 , u8 , Prerelease , PythonVariant ) ,
206207 Range ( VersionSpecifiers , PythonVariant ) ,
207208}
208209
@@ -729,7 +730,8 @@ fn find_all_minor(
729730 }
730731 VersionRequest :: MajorMinor ( _, _, _)
731732 | VersionRequest :: MajorMinorPatch ( _, _, _, _)
732- | VersionRequest :: MajorMinorPrerelease ( _, _, _, _) => Either :: Right ( iter:: empty ( ) ) ,
733+ | VersionRequest :: MajorMinorPrerelease ( _, _, _, _)
734+ | VersionRequest :: MajorMinorPatchPrerelease ( _, _, _, _, _) => Either :: Right ( iter:: empty ( ) ) ,
733735 }
734736}
735737
@@ -2669,7 +2671,8 @@ impl VersionRequest {
26692671 Self :: Major ( ..) => self ,
26702672 Self :: MajorMinor ( ..) => self ,
26712673 Self :: MajorMinorPatch ( major, minor, _, variant)
2672- | Self :: MajorMinorPrerelease ( major, minor, _, variant) => {
2674+ | Self :: MajorMinorPrerelease ( major, minor, _, variant)
2675+ | Self :: MajorMinorPatchPrerelease ( major, minor, _, _, variant) => {
26732676 Self :: MajorMinor ( major, minor, variant)
26742677 }
26752678 }
@@ -2680,11 +2683,13 @@ impl VersionRequest {
26802683 & self ,
26812684 implementation : Option < & ImplementationName > ,
26822685 ) -> Vec < ExecutableName > {
2683- let prerelease = if let Self :: MajorMinorPrerelease ( _, _, prerelease, _) = self {
2684- // Include the prerelease version, e.g., `python3.8a`
2685- Some ( prerelease)
2686- } else {
2687- None
2686+ let prerelease = match self {
2687+ Self :: MajorMinorPrerelease ( _, _, prerelease, _)
2688+ | Self :: MajorMinorPatchPrerelease ( _, _, _, prerelease, _) => {
2689+ // Include the prerelease version, e.g., `python3.8a`
2690+ Some ( prerelease)
2691+ }
2692+ _ => None ,
26882693 } ;
26892694
26902695 // Push a default one
@@ -2772,6 +2777,7 @@ impl VersionRequest {
27722777 Self :: MajorMinor ( major, _, _) => Some ( * major) ,
27732778 Self :: MajorMinorPatch ( major, _, _, _) => Some ( * major) ,
27742779 Self :: MajorMinorPrerelease ( major, _, _, _) => Some ( * major) ,
2780+ Self :: MajorMinorPatchPrerelease ( major, _, _, _, _) => Some ( * major) ,
27752781 }
27762782 }
27772783
@@ -2783,6 +2789,7 @@ impl VersionRequest {
27832789 Self :: MajorMinor ( _, minor, _) => Some ( * minor) ,
27842790 Self :: MajorMinorPatch ( _, minor, _, _) => Some ( * minor) ,
27852791 Self :: MajorMinorPrerelease ( _, minor, _, _) => Some ( * minor) ,
2792+ Self :: MajorMinorPatchPrerelease ( _, minor, _, _, _) => Some ( * minor) ,
27862793 }
27872794 }
27882795
@@ -2794,6 +2801,7 @@ impl VersionRequest {
27942801 Self :: MajorMinor ( _, _, _) => None ,
27952802 Self :: MajorMinorPatch ( _, _, patch, _) => Some ( * patch) ,
27962803 Self :: MajorMinorPrerelease ( _, _, _, _) => None ,
2804+ Self :: MajorMinorPatchPrerelease ( _, _, patch, _, _) => Some ( * patch) ,
27972805 }
27982806 }
27992807
@@ -2805,6 +2813,7 @@ impl VersionRequest {
28052813 Self :: MajorMinor ( _, _, _) => None ,
28062814 Self :: MajorMinorPatch ( _, _, _, _) => None ,
28072815 Self :: MajorMinorPrerelease ( _, _, prerelease, _) => Some ( prerelease) ,
2816+ Self :: MajorMinorPatchPrerelease ( _, _, _, prerelease, _) => Some ( prerelease) ,
28082817 }
28092818 }
28102819
@@ -2842,6 +2851,13 @@ impl VersionRequest {
28422851 ) ) ;
28432852 }
28442853 }
2854+ Self :: MajorMinorPatchPrerelease ( major, minor, patch, prerelease, _) => {
2855+ if ( * major, * minor) < ( 3 , 6 ) {
2856+ return Err ( format ! (
2857+ "Python <3.6 is not supported but {major}.{minor}.{patch}{prerelease} was requested."
2858+ ) ) ;
2859+ }
2860+ }
28452861 // TODO(zanieb): We could do some checking here to see if the range can be satisfied
28462862 Self :: Range ( _, _) => ( ) ,
28472863 }
@@ -2933,6 +2949,19 @@ impl VersionRequest {
29332949 ) == ( * major, * minor, * prerelease)
29342950 && variant. matches_interpreter ( interpreter)
29352951 }
2952+ Self :: MajorMinorPatchPrerelease ( major, minor, patch, prerelease, variant) => {
2953+ let version = interpreter. python_version ( ) ;
2954+ let Some ( interpreter_prerelease) = version. pre ( ) else {
2955+ return false ;
2956+ } ;
2957+ (
2958+ interpreter. python_major ( ) ,
2959+ interpreter. python_minor ( ) ,
2960+ interpreter. python_patch ( ) ,
2961+ interpreter_prerelease,
2962+ ) == ( * major, * minor, * patch, * prerelease)
2963+ && variant. matches_interpreter ( interpreter)
2964+ }
29362965 }
29372966 }
29382967
@@ -2968,6 +2997,14 @@ impl VersionRequest {
29682997 ( version. major ( ) , version. minor ( ) , version. pre ( ) )
29692998 == ( * major, * minor, Some ( * prerelease) )
29702999 }
3000+ Self :: MajorMinorPatchPrerelease ( major, minor, patch, prerelease, _) => {
3001+ (
3002+ version. major ( ) ,
3003+ version. minor ( ) ,
3004+ version. patch ( ) ,
3005+ version. pre ( ) ,
3006+ ) == ( * major, * minor, Some ( * patch) , Some ( * prerelease) )
3007+ }
29713008 }
29723009 }
29733010
@@ -3007,6 +3044,9 @@ impl VersionRequest {
30073044 Self :: MajorMinorPrerelease ( self_major, self_minor, _, _) => {
30083045 ( * self_major, * self_minor) == ( major, minor)
30093046 }
3047+ Self :: MajorMinorPatchPrerelease ( self_major, self_minor, _, _, _) => {
3048+ ( * self_major, * self_minor) == ( major, minor)
3049+ }
30103050 }
30113051 }
30123052
@@ -3039,10 +3079,24 @@ impl VersionRequest {
30393079 . with_pre ( prerelease) ,
30403080 ) ,
30413081 Self :: MajorMinorPrerelease ( self_major, self_minor, self_prerelease, _) => {
3042- // Pre-releases of Python versions are always for the zero patch version
3082+ // Pre-releases without a patch in the request match the zero patch version
30433083 ( * self_major, * self_minor, 0 , Some ( * self_prerelease) )
30443084 == ( major, minor, patch, prerelease)
30453085 }
3086+ Self :: MajorMinorPatchPrerelease (
3087+ self_major,
3088+ self_minor,
3089+ self_patch,
3090+ self_prerelease,
3091+ _,
3092+ ) => {
3093+ (
3094+ * self_major,
3095+ * self_minor,
3096+ * self_patch,
3097+ Some ( * self_prerelease) ,
3098+ ) == ( major, minor, patch, prerelease)
3099+ }
30463100 }
30473101 }
30483102
@@ -3062,6 +3116,7 @@ impl VersionRequest {
30623116 Self :: MajorMinor ( ..) => false ,
30633117 Self :: MajorMinorPatch ( ..) => true ,
30643118 Self :: MajorMinorPrerelease ( ..) => false ,
3119+ Self :: MajorMinorPatchPrerelease ( ..) => true ,
30653120 Self :: Range ( _, _) => false ,
30663121 }
30673122 }
@@ -3082,6 +3137,9 @@ impl VersionRequest {
30823137 Self :: MajorMinorPrerelease ( major, minor, prerelease, variant) => {
30833138 Self :: MajorMinorPrerelease ( major, minor, prerelease, variant)
30843139 }
3140+ Self :: MajorMinorPatchPrerelease ( major, minor, _, prerelease, variant) => {
3141+ Self :: MajorMinorPrerelease ( major, minor, prerelease, variant)
3142+ }
30853143 Self :: Range ( _, _) => self ,
30863144 }
30873145 }
@@ -3095,6 +3153,7 @@ impl VersionRequest {
30953153 Self :: MajorMinor ( ..) => false ,
30963154 Self :: MajorMinorPatch ( ..) => false ,
30973155 Self :: MajorMinorPrerelease ( ..) => true ,
3156+ Self :: MajorMinorPatchPrerelease ( ..) => true ,
30983157 Self :: Range ( specifiers, _) => specifiers. iter ( ) . any ( VersionSpecifier :: any_prerelease) ,
30993158 }
31003159 }
@@ -3107,6 +3166,7 @@ impl VersionRequest {
31073166 | Self :: MajorMinor ( _, _, variant)
31083167 | Self :: MajorMinorPatch ( _, _, _, variant)
31093168 | Self :: MajorMinorPrerelease ( _, _, _, variant)
3169+ | Self :: MajorMinorPatchPrerelease ( _, _, _, _, variant)
31103170 | Self :: Range ( _, variant) => variant. is_debug ( ) ,
31113171 }
31123172 }
@@ -3119,6 +3179,7 @@ impl VersionRequest {
31193179 | Self :: MajorMinor ( _, _, variant)
31203180 | Self :: MajorMinorPatch ( _, _, _, variant)
31213181 | Self :: MajorMinorPrerelease ( _, _, _, variant)
3182+ | Self :: MajorMinorPatchPrerelease ( _, _, _, _, variant)
31223183 | Self :: Range ( _, variant) => variant. is_freethreaded ( ) ,
31233184 }
31243185 }
@@ -3142,6 +3203,15 @@ impl VersionRequest {
31423203 Self :: MajorMinorPrerelease ( major, minor, prerelease, _) => {
31433204 Self :: MajorMinorPrerelease ( major, minor, prerelease, PythonVariant :: Default )
31443205 }
3206+ Self :: MajorMinorPatchPrerelease ( major, minor, patch, prerelease, _) => {
3207+ Self :: MajorMinorPatchPrerelease (
3208+ major,
3209+ minor,
3210+ patch,
3211+ prerelease,
3212+ PythonVariant :: Default ,
3213+ )
3214+ }
31453215 Self :: Range ( specifiers, _) => Self :: Range ( specifiers, PythonVariant :: Default ) ,
31463216 }
31473217 }
@@ -3155,6 +3225,7 @@ impl VersionRequest {
31553225 | Self :: MajorMinor ( _, _, variant)
31563226 | Self :: MajorMinorPatch ( _, _, _, variant)
31573227 | Self :: MajorMinorPrerelease ( _, _, _, variant)
3228+ | Self :: MajorMinorPatchPrerelease ( _, _, _, _, variant)
31583229 | Self :: Range ( _, variant) => Some ( * variant) ,
31593230 }
31603231 }
@@ -3174,10 +3245,14 @@ impl VersionRequest {
31743245 u64:: from ( * minor) ,
31753246 u64:: from ( * patch) ,
31763247 ] ) ) ,
3177- // Pre-releases of Python versions are always for the zero patch version
3248+ // Pre-releases without a patch use the zero patch version
31783249 Self :: MajorMinorPrerelease ( major, minor, prerelease, _) => Some (
31793250 Version :: new ( [ u64:: from ( * major) , u64:: from ( * minor) , 0 ] ) . with_pre ( Some ( * prerelease) ) ,
31803251 ) ,
3252+ Self :: MajorMinorPatchPrerelease ( major, minor, patch, prerelease, _) => Some (
3253+ Version :: new ( [ u64:: from ( * major) , u64:: from ( * minor) , u64:: from ( * patch) ] )
3254+ . with_pre ( Some ( * prerelease) ) ,
3255+ ) ,
31813256 }
31823257 }
31833258
@@ -3209,6 +3284,12 @@ impl VersionRequest {
32093284 . with_pre ( Some ( * prerelease) ) ,
32103285 ) ) )
32113286 }
3287+ Self :: MajorMinorPatchPrerelease ( major, minor, patch, prerelease, _) => {
3288+ Some ( VersionSpecifiers :: from ( VersionSpecifier :: equals_version (
3289+ Version :: new ( [ u64:: from ( * major) , u64:: from ( * minor) , u64:: from ( * patch) ] )
3290+ . with_pre ( Some ( * prerelease) ) ,
3291+ ) ) )
3292+ }
32123293 Self :: Range ( specifiers, _) => Some ( specifiers. clone ( ) ) ,
32133294 }
32143295 }
@@ -3298,16 +3379,16 @@ impl FromStr for VersionRequest {
32983379 }
32993380 Ok ( Self :: MajorMinor ( * major, * minor, variant) )
33003381 }
3301- // e.g. `3.12.1` or `3.13.0rc1`
3382+ // e.g. `3.12.1`, `3.13.0rc1`, or `3.14.5rc1 `
33023383 [ major, minor, patch] => {
33033384 if let Some ( prerelease) = prerelease {
3304- // Prereleases are only allowed for the first patch version, e.g, 3.12.2rc1
3305- // isn't a proper Python release
3306- if * patch != 0 {
3307- return Err ( Error :: InvalidVersionRequest ( s . to_string ( ) ) ) ;
3385+ if * patch == 0 {
3386+ return Ok ( Self :: MajorMinorPrerelease (
3387+ * major , * minor , prerelease , variant ,
3388+ ) ) ;
33083389 }
3309- return Ok ( Self :: MajorMinorPrerelease (
3310- * major, * minor, prerelease, variant,
3390+ return Ok ( Self :: MajorMinorPatchPrerelease (
3391+ * major, * minor, * patch , prerelease, variant,
33113392 ) ) ;
33123393 }
33133394 Ok ( Self :: MajorMinorPatch ( * major, * minor, * patch, variant) )
@@ -3381,6 +3462,13 @@ impl fmt::Display for VersionRequest {
33813462 Self :: MajorMinorPrerelease ( major, minor, prerelease, variant) => {
33823463 write ! ( f, "{major}.{minor}{prerelease}{}" , variant. display_suffix( ) )
33833464 }
3465+ Self :: MajorMinorPatchPrerelease ( major, minor, patch, prerelease, variant) => {
3466+ write ! (
3467+ f,
3468+ "{major}.{minor}.{patch}{prerelease}{}" ,
3469+ variant. display_suffix( )
3470+ )
3471+ }
33843472 Self :: Range ( specifiers, _) => write ! ( f, "{specifiers}" ) ,
33853473 }
33863474 }
@@ -3940,6 +4028,12 @@ mod tests {
39404028 "3.13rc4"
39414029 ) ;
39424030
4031+ assert_eq ! (
4032+ PythonRequest :: Version ( VersionRequest :: from_str( "3.14.5rc1" ) . unwrap( ) )
4033+ . to_canonical_string( ) ,
4034+ "3.14.5rc1"
4035+ ) ;
4036+
39434037 assert_eq ! (
39444038 PythonRequest :: ExecutableName ( "foo" . to_string( ) ) . to_canonical_string( ) ,
39454039 "foo"
@@ -4091,12 +4185,32 @@ mod tests {
40914185 ) ,
40924186 "Pre-release version requests require a minor version"
40934187 ) ;
4094- assert ! (
4095- matches!(
4096- VersionRequest :: from_str( "3.13.2rc1" ) ,
4097- Err ( Error :: InvalidVersionRequest ( _) )
4188+ assert_eq ! (
4189+ VersionRequest :: from_str( "3.14.5rc1" ) . unwrap( ) ,
4190+ VersionRequest :: MajorMinorPatchPrerelease (
4191+ 3 ,
4192+ 14 ,
4193+ 5 ,
4194+ Prerelease {
4195+ kind: PrereleaseKind :: Rc ,
4196+ number: 1
4197+ } ,
4198+ PythonVariant :: Default
40984199 ) ,
4099- "Pre-release version requests require a patch version of zero"
4200+ "Pre-release version requests with a non-zero patch are allowed (e.g., `3.14.5rc1`)"
4201+ ) ;
4202+ assert_eq ! (
4203+ VersionRequest :: from_str( "3.13.2rc1" ) . unwrap( ) ,
4204+ VersionRequest :: MajorMinorPatchPrerelease (
4205+ 3 ,
4206+ 13 ,
4207+ 2 ,
4208+ Prerelease {
4209+ kind: PrereleaseKind :: Rc ,
4210+ number: 1
4211+ } ,
4212+ PythonVariant :: Default
4213+ )
41004214 ) ;
41014215 assert ! (
41024216 matches!(
0 commit comments