@@ -44,9 +44,9 @@ pub enum ToolchainRequest {
44
44
File ( PathBuf ) ,
45
45
/// The name of a Python executable (i.e. for lookup in the PATH) e.g. `foopython3`
46
46
ExecutableName ( String ) ,
47
- /// A Python implementation without a version e.g. `pypy`
47
+ /// A Python implementation without a version e.g. `pypy` or `pp`
48
48
Implementation ( ImplementationName ) ,
49
- /// A Python implementation name and version e.g. `pypy3.8` or `[email protected] `
49
+ /// A Python implementation name and version e.g. `pypy3.8` or `[email protected] ` or `pp38`
50
50
ImplementationVersion ( ImplementationName , VersionRequest ) ,
51
51
/// A request for a specific toolchain key e.g. `cpython-3.12-x86_64-linux-gnu`
52
52
/// Generally these refer to uv-managed toolchain downloads.
@@ -919,7 +919,7 @@ impl ToolchainRequest {
919
919
///
920
920
/// This cannot fail, which means weird inputs will be parsed as [`ToolchainRequest::File`] or [`ToolchainRequest::ExecutableName`].
921
921
pub fn parse ( value : & str ) -> Self {
922
- // e.g. `3.12.1`
922
+ // e.g. `3.12.1`, `312`, or `>=3.12`
923
923
if let Ok ( version) = VersionRequest :: from_str ( value) {
924
924
return Self :: Version ( version) ;
925
925
}
@@ -937,18 +937,25 @@ impl ToolchainRequest {
937
937
}
938
938
}
939
939
}
940
- for implementation in ImplementationName :: iter ( ) {
940
+ for implementation in ImplementationName :: possible_names ( ) {
941
941
if let Some ( remainder) = value
942
942
. to_ascii_lowercase ( )
943
943
. strip_prefix ( Into :: < & str > :: into ( implementation) )
944
944
{
945
945
// e.g. `pypy`
946
946
if remainder. is_empty ( ) {
947
- return Self :: Implementation ( * implementation) ;
947
+ return Self :: Implementation (
948
+ // Safety: The name matched the possible names above
949
+ ImplementationName :: from_str ( implementation) . unwrap ( ) ,
950
+ ) ;
948
951
}
949
- // e.g. `pypy3.12`
952
+ // e.g. `pypy3.12` or `pp312`
950
953
if let Ok ( version) = VersionRequest :: from_str ( remainder) {
951
- return Self :: ImplementationVersion ( * implementation, version) ;
954
+ return Self :: ImplementationVersion (
955
+ // Safety: The name matched the possible names above
956
+ ImplementationName :: from_str ( implementation) . unwrap ( ) ,
957
+ version,
958
+ ) ;
952
959
}
953
960
}
954
961
}
@@ -1267,8 +1274,22 @@ impl FromStr for VersionRequest {
1267
1274
type Err = Error ;
1268
1275
1269
1276
fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
1277
+ fn parse_nosep ( s : & str ) -> Option < VersionRequest > {
1278
+ let mut chars = s. chars ( ) ;
1279
+ let major = chars. next ( ) ?. to_digit ( 10 ) ?. try_into ( ) . ok ( ) ?;
1280
+ if chars. as_str ( ) . is_empty ( ) {
1281
+ return Some ( VersionRequest :: Major ( major) ) ;
1282
+ }
1283
+ let minor = chars. as_str ( ) . parse :: < u8 > ( ) . ok ( ) ?;
1284
+ Some ( VersionRequest :: MajorMinor ( major, minor) )
1285
+ }
1286
+
1287
+ // e.g. `3`, `38`, `312`
1288
+ if let Some ( request) = parse_nosep ( s) {
1289
+ Ok ( request)
1290
+ }
1270
1291
// e.g. `3.12.1`
1271
- if let Ok ( versions) = s
1292
+ else if let Ok ( versions) = s
1272
1293
. splitn ( 3 , '.' )
1273
1294
. map ( str:: parse :: < u8 > )
1274
1295
. collect :: < Result < Vec < _ > , _ > > ( )
@@ -1284,6 +1305,7 @@ impl FromStr for VersionRequest {
1284
1305
} ;
1285
1306
1286
1307
Ok ( selector)
1308
+
1287
1309
// e.g. `>=3.12.1,<3.12`
1288
1310
} else if let Ok ( specifiers) = VersionSpecifiers :: from_str ( s) {
1289
1311
if specifiers. is_empty ( ) {
@@ -1549,6 +1571,7 @@ mod tests {
1549
1571
use assert_fs:: { prelude:: * , TempDir } ;
1550
1572
use test_log:: test;
1551
1573
1574
+ use super :: Error ;
1552
1575
use crate :: {
1553
1576
discovery:: { ToolchainRequest , VersionRequest } ,
1554
1577
implementation:: ImplementationName ,
@@ -1587,13 +1610,35 @@ mod tests {
1587
1610
ToolchainRequest :: parse( "pypy" ) ,
1588
1611
ToolchainRequest :: Implementation ( ImplementationName :: PyPy )
1589
1612
) ;
1613
+ assert_eq ! (
1614
+ ToolchainRequest :: parse( "pp" ) ,
1615
+ ToolchainRequest :: Implementation ( ImplementationName :: PyPy )
1616
+ ) ;
1617
+ assert_eq ! (
1618
+ ToolchainRequest :: parse( "cp" ) ,
1619
+ ToolchainRequest :: Implementation ( ImplementationName :: CPython )
1620
+ ) ;
1590
1621
assert_eq ! (
1591
1622
ToolchainRequest :: parse( "pypy3.10" ) ,
1592
1623
ToolchainRequest :: ImplementationVersion (
1593
1624
ImplementationName :: PyPy ,
1594
1625
VersionRequest :: from_str( "3.10" ) . unwrap( )
1595
1626
)
1596
1627
) ;
1628
+ assert_eq ! (
1629
+ ToolchainRequest :: parse( "pp310" ) ,
1630
+ ToolchainRequest :: ImplementationVersion (
1631
+ ImplementationName :: PyPy ,
1632
+ VersionRequest :: from_str( "3.10" ) . unwrap( )
1633
+ )
1634
+ ) ;
1635
+ assert_eq ! (
1636
+ ToolchainRequest :: parse( "cp38" ) ,
1637
+ ToolchainRequest :: ImplementationVersion (
1638
+ ImplementationName :: CPython ,
1639
+ VersionRequest :: from_str( "3.8" ) . unwrap( )
1640
+ )
1641
+ ) ;
1597
1642
assert_eq ! (
1598
1643
ToolchainRequest :: parse
( "[email protected] " ) ,
1599
1644
ToolchainRequest :: ImplementationVersion (
@@ -1603,7 +1648,10 @@ mod tests {
1603
1648
) ;
1604
1649
assert_eq ! (
1605
1650
ToolchainRequest :: parse( "pypy310" ) ,
1606
- ToolchainRequest :: ExecutableName ( "pypy310" . to_string( ) )
1651
+ ToolchainRequest :: ImplementationVersion (
1652
+ ImplementationName :: PyPy ,
1653
+ VersionRequest :: from_str( "3.10" ) . unwrap( )
1654
+ )
1607
1655
) ;
1608
1656
1609
1657
let tempdir = TempDir :: new ( ) . unwrap ( ) ;
@@ -1645,5 +1693,28 @@ mod tests {
1645
1693
VersionRequest :: MajorMinorPatch ( 3 , 12 , 1 )
1646
1694
) ;
1647
1695
assert ! ( VersionRequest :: from_str( "1.foo.1" ) . is_err( ) ) ;
1696
+ assert_eq ! (
1697
+ VersionRequest :: from_str( "3" ) . unwrap( ) ,
1698
+ VersionRequest :: Major ( 3 )
1699
+ ) ;
1700
+ assert_eq ! (
1701
+ VersionRequest :: from_str( "38" ) . unwrap( ) ,
1702
+ VersionRequest :: MajorMinor ( 3 , 8 )
1703
+ ) ;
1704
+ assert_eq ! (
1705
+ VersionRequest :: from_str( "312" ) . unwrap( ) ,
1706
+ VersionRequest :: MajorMinor ( 3 , 12 )
1707
+ ) ;
1708
+ assert_eq ! (
1709
+ VersionRequest :: from_str( "3100" ) . unwrap( ) ,
1710
+ VersionRequest :: MajorMinor ( 3 , 100 )
1711
+ ) ;
1712
+ assert ! (
1713
+ // Test for overflow
1714
+ matches!(
1715
+ VersionRequest :: from_str( "31000" ) ,
1716
+ Err ( Error :: InvalidVersionRequest ( _) )
1717
+ )
1718
+ ) ;
1648
1719
}
1649
1720
}
0 commit comments