Skip to content

Commit fe875a9

Browse files
committed
Add Windows
1 parent 54359d5 commit fe875a9

File tree

2 files changed

+108
-3
lines changed

2 files changed

+108
-3
lines changed

crates/uv-virtualenv/src/virtualenv.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,14 @@ pub(crate) fn create(
277277
let targetw = scripts.join(WindowsExecutable::Pythonw.exe(interpreter));
278278
create_link_to_executable(targetw.as_path(), &executable_target)
279279
.map_err(Error::Python)?;
280+
if interpreter.gil_disabled() {
281+
let targett = scripts.join(WindowsExecutable::PythonMajorMinort.exe(interpreter));
282+
create_link_to_executable(targett.as_path(), &executable_target)
283+
.map_err(Error::Python)?;
284+
let targetwt = scripts.join(WindowsExecutable::PythonwMajorMinort.exe(interpreter));
285+
create_link_to_executable(targetwt.as_path(), &executable_target)
286+
.map_err(Error::Python)?;
287+
}
280288
} else {
281289
// Always copy `python.exe`.
282290
copy_launcher_windows(
@@ -373,6 +381,24 @@ pub(crate) fn create(
373381
&scripts,
374382
python_home,
375383
)?;
384+
385+
// If the GIL is disabled, copy `venvlaunchert.exe` and `venvwlaunchert.exe`.
386+
if interpreter.gil_disabled() {
387+
copy_launcher_windows(
388+
WindowsExecutable::PythonMajorMinort,
389+
interpreter,
390+
&base_python,
391+
&scripts,
392+
python_home,
393+
)?;
394+
copy_launcher_windows(
395+
WindowsExecutable::PythonwMajorMinort,
396+
interpreter,
397+
&base_python,
398+
&scripts,
399+
python_home,
400+
)?;
401+
}
376402
}
377403
}
378404
}
@@ -590,8 +616,12 @@ enum WindowsExecutable {
590616
PythonMajor,
591617
/// The `python3.<minor>.exe` executable (or `venvlauncher.exe` launcher shim).
592618
PythonMajorMinor,
619+
/// The `python3.<minor>t.exe` executable (or `venvlaunchert.exe` launcher shim).
620+
PythonMajorMinort,
593621
/// The `pythonw.exe` executable (or `venvwlauncher.exe` launcher shim).
594622
Pythonw,
623+
/// The `pythonw3.<minor>t.exe` executable (or `venvwlaunchert.exe` launcher shim).
624+
PythonwMajorMinort,
595625
/// The `pypy.exe` executable.
596626
PyPy,
597627
/// The `pypy3.exe` executable.
@@ -602,7 +632,7 @@ enum WindowsExecutable {
602632
PyPyw,
603633
/// The `pypy3.<minor>w.exe` executable.
604634
PyPyMajorMinorw,
605-
// The `graalpy.exe` executable
635+
/// The `graalpy.exe` executable.
606636
GraalPy,
607637
}
608638

@@ -621,7 +651,21 @@ impl WindowsExecutable {
621651
interpreter.python_minor()
622652
)
623653
}
654+
WindowsExecutable::PythonMajorMinort => {
655+
format!(
656+
"python{}.{}t.exe",
657+
interpreter.python_major(),
658+
interpreter.python_minor()
659+
)
660+
}
624661
WindowsExecutable::Pythonw => String::from("pythonw.exe"),
662+
WindowsExecutable::PythonwMajorMinort => {
663+
format!(
664+
"pythonw{}.{}t.exe",
665+
interpreter.python_major(),
666+
interpreter.python_minor()
667+
)
668+
}
625669
WindowsExecutable::PyPy => String::from("pypy.exe"),
626670
WindowsExecutable::PyPyMajor => {
627671
format!("pypy{}.exe", interpreter.python_major())
@@ -656,6 +700,8 @@ impl WindowsExecutable {
656700
Self::Python | Self::PythonMajor | Self::PythonMajorMinor => "venvlauncher.exe",
657701
Self::Pythonw if interpreter.gil_disabled() => "venvwlaunchert.exe",
658702
Self::Pythonw => "venvwlauncher.exe",
703+
Self::PythonMajorMinort => "venvlaunchert.exe",
704+
Self::PythonwMajorMinort => "venvwlaunchert.exe",
659705
// From 3.13 on these should replace the `python.exe` and `pythonw.exe` shims.
660706
// These are not relevant as of now for PyPy as it doesn't yet support Python 3.13.
661707
Self::PyPy | Self::PyPyMajor | Self::PyPyMajorMinor => "venvlauncher.exe",

crates/uv/tests/it/python_install.rs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,65 @@ fn python_install_freethreaded() {
10871087
----- stderr -----
10881088
"###);
10891089

1090+
// Create a virtual environment with the freethreaded Python
1091+
uv_snapshot!(context.filters(), context.venv().arg("--python").arg("3.13t"), @r"
1092+
success: true
1093+
exit_code: 0
1094+
----- stdout -----
1095+
1096+
----- stderr -----
1097+
Using CPython 3.13.5
1098+
Creating virtual environment at: .venv
1099+
Activate with: source .venv/[BIN]/activate
1100+
");
1101+
1102+
// `python`, `python3`, `python3.13`, and `python3.13t` should all be present
1103+
let scripts = context
1104+
.venv
1105+
.join(if cfg!(windows) { "Scripts" } else { "bin" });
1106+
assert!(
1107+
scripts
1108+
.join(format!("python{}", std::env::consts::EXE_SUFFIX))
1109+
.exists()
1110+
);
1111+
1112+
#[cfg(windows)]
1113+
assert!(
1114+
scripts
1115+
.join(format!("pythonw{}", std::env::consts::EXE_SUFFIX))
1116+
.exists()
1117+
);
1118+
1119+
#[cfg(unix)]
1120+
assert!(
1121+
scripts
1122+
.join(format!("python3{}", std::env::consts::EXE_SUFFIX))
1123+
.exists()
1124+
);
1125+
1126+
#[cfg(unix)]
1127+
assert!(
1128+
scripts
1129+
.join(format!("python3.13{}", std::env::consts::EXE_SUFFIX))
1130+
.exists()
1131+
);
1132+
1133+
assert!(
1134+
scripts
1135+
.join(format!("python3.13t{}", std::env::consts::EXE_SUFFIX))
1136+
.exists()
1137+
);
1138+
1139+
#[cfg(windows)]
1140+
assert!(
1141+
scripts
1142+
.join(format!("pythonw3.13t{}", std::env::consts::EXE_SUFFIX))
1143+
.exists()
1144+
);
1145+
1146+
// Remove the virtual environment
1147+
fs_err::remove_dir_all(&context.venv).unwrap();
1148+
10901149
// Should be distinct from 3.13
10911150
uv_snapshot!(context.filters(), context.python_install().arg("3.13"), @r"
10921151
success: true
@@ -1099,14 +1158,14 @@ fn python_install_freethreaded() {
10991158
");
11001159

11011160
// Should not work with older Python versions
1102-
uv_snapshot!(context.filters(), context.python_install().arg("3.12t"), @r###"
1161+
uv_snapshot!(context.filters(), context.python_install().arg("3.12t"), @r"
11031162
success: false
11041163
exit_code: 2
11051164
----- stdout -----
11061165
11071166
----- stderr -----
11081167
error: No download found for request: cpython-3.12t-[PLATFORM]
1109-
"###);
1168+
");
11101169

11111170
uv_snapshot!(context.filters(), context.python_uninstall().arg("--all"), @r"
11121171
success: true

0 commit comments

Comments
 (0)