@@ -26,7 +26,9 @@ use uv_fs::{PythonExt, Simplified};
26
26
use uv_installer:: { SatisfiesResult , SitePackages } ;
27
27
use uv_normalize:: PackageName ;
28
28
use uv_python:: {
29
- EnvironmentPreference , Interpreter , PyVenvConfiguration , PythonDownloads , PythonEnvironment , PythonInstallation , PythonPreference , PythonRequest , PythonVersion , PythonVersionFile , VersionFileDiscoveryOptions
29
+ EnvironmentPreference , Interpreter , PyVenvConfiguration , PythonDownloads , PythonEnvironment ,
30
+ PythonInstallation , PythonPreference , PythonRequest , PythonVersion , PythonVersionFile ,
31
+ VersionFileDiscoveryOptions ,
30
32
} ;
31
33
use uv_requirements:: { RequirementsSource , RequirementsSpecification } ;
32
34
use uv_resolver:: Lock ;
@@ -1070,14 +1072,14 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
1070
1072
if interpreter. is_virtualenv ( ) {
1071
1073
process. env ( EnvVars :: VIRTUAL_ENV , interpreter. sys_prefix ( ) . as_os_str ( ) ) ;
1072
1074
} ;
1073
-
1075
+
1074
1076
// Spawn and wait for completion
1075
1077
// Standard input, output, and error streams are all inherited
1076
1078
// TODO(zanieb): Throw a nicer error message if the command is not found
1077
1079
let handle = process
1078
1080
. spawn ( )
1079
1081
. map_err ( |err| {
1080
- let executable: Cow < ' _ , str > = command. display_executable ( ) ;
1082
+ let executable: Cow < ' _ , str > = command. display_executable ( ) ;
1081
1083
// Special case for providing meaningful error message when users
1082
1084
// attempt to invoke python. E.g. "python3.11".
1083
1085
// Will not work if patch version is provided. I.E. "python3.11.9"
@@ -1092,13 +1094,11 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
1092
1094
// Construct the message dynamically
1093
1095
let message_suffix = if project_found {
1094
1096
format ! (
1095
- "Did you mean to change the environment to Python {} with `uv run -p {} python`?" ,
1096
- version_part, version_part
1097
+ "Did you mean to change the environment to Python {version_part} with `uv run -p {version_part} python`?"
1097
1098
)
1098
1099
} else {
1099
1100
format ! (
1100
- "Did you mean to search for a Python {} environment with `uv run -p {} python`?" ,
1101
- version_part, version_part
1101
+ "Did you mean to search for a Python {version_part} environment with `uv run -p {version_part} python`?"
1102
1102
)
1103
1103
} ;
1104
1104
anyhow ! (
@@ -1557,76 +1557,114 @@ fn read_recursion_depth_from_environment_variable() -> anyhow::Result<u32> {
1557
1557
. with_context ( || format ! ( "invalid value for {}" , EnvVars :: UV_RUN_RECURSION_DEPTH ) )
1558
1558
}
1559
1559
1560
-
1561
1560
/// Matches valid Python executable names:
1562
1561
/// - ✅ "python", "python3", "python3.9", "python4", "python3.10", "python3.13.3"
1563
1562
/// - ❌ "python39", "python3abc", "python3.12b3", "", "python-foo"
1564
1563
fn is_python_executable ( executable_command : & str ) -> bool {
1565
1564
executable_command
1566
1565
. strip_prefix ( "python" )
1567
- . map_or ( false , |version| version. len ( ) == 0 || is_valid_python_version ( version) )
1566
+ . is_some_and ( |version| {
1567
+ version. is_empty ( ) || is_valid_python_version ( version)
1568
+ } )
1568
1569
}
1569
1570
1570
1571
/// Checks if a version string is a valid Python major.minor.patch version.
1571
1572
fn is_valid_python_version ( version : & str ) -> bool {
1572
- PythonVersion :: from_str ( version)
1573
- . map_or ( false ,
1574
- |ver|
1575
- ver. is_stable ( ) &&
1573
+ PythonVersion :: from_str ( version) . is_ok_and ( |ver| {
1574
+ ver. is_stable ( ) &&
1576
1575
// Should not contain post info. E.g. "3.12b3"
1577
1576
!ver. is_post ( )
1578
- )
1579
- } #[ cfg( test) ]
1577
+ } )
1578
+ }
1579
+ #[ cfg( test) ]
1580
1580
mod tests {
1581
1581
use super :: { is_python_executable, is_valid_python_version} ;
1582
1582
1583
1583
/// Helper function for asserting test cases.
1584
1584
/// - If `expected_result` is `true`, it expects the function to return `true` (valid cases).
1585
1585
/// - If `expected_result` is `false`, it expects the function to return `false` (invalid cases).
1586
- fn assert_cases < F : Fn ( & str ) -> bool > ( cases : & [ & str ] , func : F , test_name : & str , expected_result : bool ) {
1586
+ fn assert_cases < F : Fn ( & str ) -> bool > (
1587
+ cases : & [ & str ] ,
1588
+ func : F ,
1589
+ test_name : & str ,
1590
+ expected_result : bool ,
1591
+ ) {
1587
1592
for & case in cases {
1593
+ let result = func ( case) ;
1588
1594
assert_eq ! (
1589
- func ( case ) ,
1595
+ result ,
1590
1596
expected_result,
1591
- "{}: Expected {} but failed on case: {}" ,
1592
- test_name,
1593
- expected_result,
1594
- case
1597
+ "{test_name}: Expected `{expected_result}`, but got `{result}` for case `{case}`"
1595
1598
) ;
1596
1599
}
1597
1600
}
1598
1601
1599
1602
#[ test]
1600
1603
fn valid_is_python_executable ( ) {
1601
1604
let valid_cases = [
1602
- "python3" , "python3.9" , "python3.10" , "python4" , "python" ,
1605
+ "python3" ,
1606
+ "python3.9" ,
1607
+ "python3.10" ,
1608
+ "python4" ,
1609
+ "python" ,
1603
1610
"python3.11.3" ,
1604
1611
"python39" , // Still a valid executable, although likely a typo
1605
1612
] ;
1606
- assert_cases ( & valid_cases, is_python_executable, "valid_is_python_executable" , true ) ;
1613
+ assert_cases (
1614
+ & valid_cases,
1615
+ is_python_executable,
1616
+ "valid_is_python_executable" ,
1617
+ true ,
1618
+ ) ;
1607
1619
}
1608
1620
1609
1621
#[ test]
1610
1622
fn invalid_is_python_executable ( ) {
1611
1623
let invalid_cases = [
1612
- "python-foo" , "python3abc" , "python3.12b3" ,
1613
- "pyth0n3" , "" , "Python3.9" , "python.3.9"
1624
+ "python-foo" ,
1625
+ "python3abc" ,
1626
+ "python3.12b3" ,
1627
+ "pyth0n3" ,
1628
+ "" ,
1629
+ "Python3.9" ,
1630
+ "python.3.9" ,
1614
1631
] ;
1615
- assert_cases ( & invalid_cases, is_python_executable, "invalid_is_python_executable" , false ) ;
1632
+ assert_cases (
1633
+ & invalid_cases,
1634
+ is_python_executable,
1635
+ "invalid_is_python_executable" ,
1636
+ false ,
1637
+ ) ;
1616
1638
}
1617
1639
1618
1640
#[ test]
1619
1641
fn valid_python_versions ( ) {
1620
1642
let valid_cases = [ "3" , "3.9" , "4" , "3.10" , "49" , "3.11.3" ] ;
1621
- assert_cases ( & valid_cases, is_valid_python_version, "valid_python_versions" , true ) ;
1643
+ assert_cases (
1644
+ & valid_cases,
1645
+ is_valid_python_version,
1646
+ "valid_python_versions" ,
1647
+ true ,
1648
+ ) ;
1622
1649
}
1623
1650
1624
1651
#[ test]
1625
1652
fn invalid_python_versions ( ) {
1626
1653
let invalid_cases = [
1627
- "3.12b3" , "3.12rc1" , "3.12a1" ,
1628
- "3.12.post1" , "3.12.1-foo" , "3abc" , ".." , ""
1654
+ "3.12b3" ,
1655
+ "3.12rc1" ,
1656
+ "3.12a1" ,
1657
+ "3.12.post1" ,
1658
+ "3.12.1-foo" ,
1659
+ "3abc" ,
1660
+ ".." ,
1661
+ "" ,
1629
1662
] ;
1630
- assert_cases ( & invalid_cases, is_valid_python_version, "invalid_python_versions" , false ) ;
1663
+ assert_cases (
1664
+ & invalid_cases,
1665
+ is_valid_python_version,
1666
+ "invalid_python_versions" ,
1667
+ false ,
1668
+ ) ;
1631
1669
}
1632
1670
}
0 commit comments