Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/changelog/3151.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix Windows debug build ``venvlauncher_d.exe`` substitution never triggering because ``executables()`` compared the
source executable name instead of the target name, and fix ``AttributeError`` on ``debug_build`` attribute for
interpreter info objects missing the field - by :user:`gaborbernat`.
14 changes: 5 additions & 9 deletions docs/explanation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,27 +97,23 @@ it can discover on the system, provided a matching creator exists.

.. list-table::
:header-rows: 1
:widths: 20 15 15 50
:widths: 20 15 65

- - Implementation
- Platforms
- Free-threaded
- Notes
- - CPython
- Linux, macOS, Windows
- 3.13+
- Full support including macOS framework builds, Homebrew, Microsoft Store, and Windows debug builds.
- 3.8+. Free-threaded builds supported on 3.13+. Includes macOS framework, Homebrew, Microsoft Store, and Windows
debug build support.
- - PyPy
- Linux, macOS, Windows
- No
- PyPy 3.8+.
- 3.8+.
- - GraalPy
- Linux, macOS, Windows
- No
- GraalPy 24.1+. Minimal test coverage, marked experimental.
- 24.1+. Minimal test coverage, marked experimental.
- - RustPython
- Linux, macOS, Windows
- No
- Minimal test coverage, marked experimental.

Seed packages (``pip``, ``setuptools``) are bundled for CPython 3.8 through 3.16. Target interpreters outside this range
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,18 +106,19 @@ def sources(cls, interpreter: PythonInfo) -> Generator[PathRef]: # ty: ignore[i

@classmethod
def _debug_suffix(cls, interpreter: PythonInfo) -> str:
return "_d" if interpreter.debug_build else ""
return "_d" if getattr(interpreter, "debug_build", False) else ""

@classmethod
def executables(cls, interpreter: PythonInfo) -> list[PathRef] | Generator[PathRef]:
sources = super().sources(interpreter)
if interpreter.version_info >= (3, 13):
host = cls.host_python(interpreter)
t_suffix = "t" if interpreter.free_threaded else ""
d_suffix = cls._debug_suffix(interpreter)
updated_sources: list[PathRef] = []
for ref in sources:
if ref.src.name == "python.exe":
launcher_path = ref.src.with_name(f"venvlauncher{t_suffix}{d_suffix}.exe")
if ref.base == "python.exe":
launcher_path = host.with_name(f"venvlauncher{t_suffix}{d_suffix}.exe")
if launcher_path.exists():
new_ref = ExePathRefToDest(
launcher_path, dest=ref.dest, targets=[ref.base, *ref.aliases], must=ref.must, when=ref.when
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"platform": "win32",
"implementation": "CPython",
"version_info": {
"major": 3,
"minor": 13,
"micro": 0,
"releaselevel": "final",
"serial": 0
},
"architecture": 64,
"version_nodot": "313",
"version": "3.13.0 (main, Oct 07 2024, 00:00:00) [MSC v.1941 64 bit (AMD64)]",
"os": "nt",
"prefix": "c:\\path\\to\\python",
"base_prefix": "c:\\path\\to\\python",
"real_prefix": null,
"base_exec_prefix": "c:\\path\\to\\python",
"exec_prefix": "c:\\path\\to\\python",
"executable": "c:\\path\\to\\python\\python_d.exe",
"original_executable": "c:\\path\\to\\python\\python_d.exe",
"system_executable": "c:\\path\\to\\python\\python_d.exe",
"has_venv": false,
"path": [
"c:\\path\\to\\python\\Scripts\\virtualenv.exe",
"c:\\path\\to\\python\\python313.zip",
"c:\\path\\to\\python",
"c:\\path\\to\\python\\Lib\\site-packages"
],
"file_system_encoding": "utf-8",
"stdout_encoding": "utf-8",
"sysconfig_scheme": null,
"sysconfig_paths": {
"stdlib": "{installed_base}/Lib",
"platstdlib": "{base}/Lib",
"purelib": "{base}/Lib/site-packages",
"platlib": "{base}/Lib/site-packages",
"include": "{installed_base}/Include",
"scripts": "{base}/Scripts",
"data": "{base}"
},
"distutils_install": {
"purelib": "Lib\\site-packages",
"platlib": "Lib\\site-packages",
"headers": "Include\\UNKNOWN",
"scripts": "Scripts",
"data": ""
},
"sysconfig": {
"makefile_filename": "c:\\path\\to\\python\\Lib\\config\\Makefile"
},
"sysconfig_vars": {
"PYTHONFRAMEWORK": "",
"installed_base": "c:\\path\\to\\python",
"base": "c:\\path\\to\\python"
},
"system_stdlib": "c:\\path\\to\\python\\Lib",
"system_stdlib_platform": "c:\\path\\to\\python\\Lib",
"max_size": 9223372036854775807,
"_creators": null,
"free_threaded": false,
"debug_build": true
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,6 @@
"system_stdlib_platform": "c:\\path\\to\\python\\Lib",
"max_size": 9223372036854775807,
"_creators": null,
"free_threaded": false
"free_threaded": false,
"debug_build": false
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,6 @@
"system_stdlib_platform": "c:\\path\\to\\python\\Lib",
"max_size": 9223372036854775807,
"_creators": null,
"free_threaded": true
"free_threaded": true,
"debug_build": false
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,25 @@ def test_free_threaded_exe_naming(py_info, mock_files) -> None:
assert "pythonw3.13t.exe" in pythonw_refs[0].aliases


@pytest.mark.parametrize("py_info_name", ["cpython3_win_debug"])
def test_debug_build_shim(py_info, mock_files) -> None:
shim = path(py_info.system_stdlib, "venv\\scripts\\nt\\venvlauncher_d.exe")
mock_files(CPYTHON3_PATH, [shim])
assert CPython3Windows.has_shim(interpreter=py_info)
sources = tuple(CPython3Windows.sources(interpreter=py_info))
assert contains_exe(sources, shim)


@pytest.mark.parametrize("py_info_name", ["cpython3_win_debug"])
def test_debug_build_uses_venvlauncher(py_info, mock_files) -> None:
launcher = path(py_info.prefix, "venvlauncher_d.exe")
w_launcher = path(py_info.prefix, "venvwlauncher_d.exe")
mock_files(CPYTHON3_PATH, [py_info.system_executable, launcher, w_launcher])
sources = tuple(CPython3Windows.sources(interpreter=py_info))
assert contains_exe(sources, launcher, "python.exe")
assert contains_exe(sources, w_launcher, "pythonw.exe")


@pytest.mark.parametrize("py_info_name", ["cpython3_win_embed"])
def test_pywin32_dll_exclusion(py_info, mock_files) -> None:
"""Test that pywin32 DLLs are excluded from virtualenv creation."""
Expand Down
Loading