@@ -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,111 @@ 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| version. is_empty ( ) || is_valid_python_version ( version) )
1568
1567
}
1569
1568
1570
1569
/// Checks if a version string is a valid Python major.minor.patch version.
1571
1570
fn is_valid_python_version ( version : & str ) -> bool {
1572
- PythonVersion :: from_str ( version)
1573
- . map_or ( false ,
1574
- |ver|
1575
- ver. is_stable ( ) &&
1571
+ PythonVersion :: from_str ( version) . is_ok_and ( |ver| {
1572
+ ver. is_stable ( ) &&
1576
1573
// Should not contain post info. E.g. "3.12b3"
1577
1574
!ver. is_post ( )
1578
- )
1579
- } #[ cfg( test) ]
1575
+ } )
1576
+ }
1577
+ #[ cfg( test) ]
1580
1578
mod tests {
1581
1579
use super :: { is_python_executable, is_valid_python_version} ;
1582
1580
1583
1581
/// Helper function for asserting test cases.
1584
1582
/// - If `expected_result` is `true`, it expects the function to return `true` (valid cases).
1585
1583
/// - 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 ) {
1584
+ fn assert_cases < F : Fn ( & str ) -> bool > (
1585
+ cases : & [ & str ] ,
1586
+ func : F ,
1587
+ test_name : & str ,
1588
+ expected_result : bool ,
1589
+ ) {
1587
1590
for & case in cases {
1591
+ let result = func ( case) ;
1588
1592
assert_eq ! (
1589
- func( case) ,
1590
- expected_result,
1591
- "{}: Expected {} but failed on case: {}" ,
1592
- test_name,
1593
- expected_result,
1594
- case
1593
+ result, expected_result,
1594
+ "{test_name}: Expected `{expected_result}`, but got `{result}` for case `{case}`"
1595
1595
) ;
1596
1596
}
1597
1597
}
1598
1598
1599
1599
#[ test]
1600
1600
fn valid_is_python_executable ( ) {
1601
1601
let valid_cases = [
1602
- "python3" , "python3.9" , "python3.10" , "python4" , "python" ,
1602
+ "python3" ,
1603
+ "python3.9" ,
1604
+ "python3.10" ,
1605
+ "python4" ,
1606
+ "python" ,
1603
1607
"python3.11.3" ,
1604
1608
"python39" , // Still a valid executable, although likely a typo
1605
1609
] ;
1606
- assert_cases ( & valid_cases, is_python_executable, "valid_is_python_executable" , true ) ;
1610
+ assert_cases (
1611
+ & valid_cases,
1612
+ is_python_executable,
1613
+ "valid_is_python_executable" ,
1614
+ true ,
1615
+ ) ;
1607
1616
}
1608
1617
1609
1618
#[ test]
1610
1619
fn invalid_is_python_executable ( ) {
1611
1620
let invalid_cases = [
1612
- "python-foo" , "python3abc" , "python3.12b3" ,
1613
- "pyth0n3" , "" , "Python3.9" , "python.3.9"
1621
+ "python-foo" ,
1622
+ "python3abc" ,
1623
+ "python3.12b3" ,
1624
+ "pyth0n3" ,
1625
+ "" ,
1626
+ "Python3.9" ,
1627
+ "python.3.9" ,
1614
1628
] ;
1615
- assert_cases ( & invalid_cases, is_python_executable, "invalid_is_python_executable" , false ) ;
1629
+ assert_cases (
1630
+ & invalid_cases,
1631
+ is_python_executable,
1632
+ "invalid_is_python_executable" ,
1633
+ false ,
1634
+ ) ;
1616
1635
}
1617
1636
1618
1637
#[ test]
1619
1638
fn valid_python_versions ( ) {
1620
1639
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 ) ;
1640
+ assert_cases (
1641
+ & valid_cases,
1642
+ is_valid_python_version,
1643
+ "valid_python_versions" ,
1644
+ true ,
1645
+ ) ;
1622
1646
}
1623
1647
1624
1648
#[ test]
1625
1649
fn invalid_python_versions ( ) {
1626
1650
let invalid_cases = [
1627
- "3.12b3" , "3.12rc1" , "3.12a1" ,
1628
- "3.12.post1" , "3.12.1-foo" , "3abc" , ".." , ""
1651
+ "3.12b3" ,
1652
+ "3.12rc1" ,
1653
+ "3.12a1" ,
1654
+ "3.12.post1" ,
1655
+ "3.12.1-foo" ,
1656
+ "3abc" ,
1657
+ ".." ,
1658
+ "" ,
1629
1659
] ;
1630
- assert_cases ( & invalid_cases, is_valid_python_version, "invalid_python_versions" , false ) ;
1660
+ assert_cases (
1661
+ & invalid_cases,
1662
+ is_valid_python_version,
1663
+ "invalid_python_versions" ,
1664
+ false ,
1665
+ ) ;
1631
1666
}
1632
1667
}
0 commit comments