mirror of
https://github.com/esphome/esphome.git
synced 2026-10-05 10:31:29 +00:00
[core] Strip \\?\ prefix from sys.executable for PlatformIO subprocess (#16158)
This commit is contained in:
@@ -311,6 +311,105 @@ def test_run_platformio_cli_sets_environment_variables(
|
||||
assert "arg" in args
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("platform", "input_path", "expected"),
|
||||
[
|
||||
# win32: drive-letter extended-length prefix is stripped
|
||||
(
|
||||
"win32",
|
||||
"\\\\?\\C:\\Users\\jesse\\AppData\\Local\\ESPHome Builder\\python\\python.exe",
|
||||
"C:\\Users\\jesse\\AppData\\Local\\ESPHome Builder\\python\\python.exe",
|
||||
),
|
||||
# win32: UNC extended-length prefix is translated to a regular UNC path
|
||||
(
|
||||
"win32",
|
||||
"\\\\?\\UNC\\server\\share\\python.exe",
|
||||
"\\\\server\\share\\python.exe",
|
||||
),
|
||||
# win32: paths without the prefix are returned unchanged
|
||||
(
|
||||
"win32",
|
||||
"C:\\Users\\jesse\\AppData\\Local\\ESPHome Builder\\python\\python.exe",
|
||||
"C:\\Users\\jesse\\AppData\\Local\\ESPHome Builder\\python\\python.exe",
|
||||
),
|
||||
# non-win32: prefix is left alone (no-op)
|
||||
("linux", "\\\\?\\C:\\python.exe", "\\\\?\\C:\\python.exe"),
|
||||
("darwin", "/usr/bin/python3", "/usr/bin/python3"),
|
||||
],
|
||||
)
|
||||
def test_strip_win_long_path_prefix(
|
||||
platform: str, input_path: str, expected: str
|
||||
) -> None:
|
||||
r"""``\\?\`` and ``\\?\UNC\`` prefixes are stripped only on win32."""
|
||||
with patch("esphome.platformio_api.sys.platform", platform):
|
||||
assert platformio_api._strip_win_long_path_prefix(input_path) == expected
|
||||
|
||||
|
||||
def test_run_platformio_cli_strips_win_long_path_prefix(
|
||||
setup_core: Path, mock_run_external_process: Mock
|
||||
) -> None:
|
||||
r"""Windows ``\\?\`` prefix on sys.executable does not leak into the subprocess.
|
||||
|
||||
The NSIS-installed esphome.exe launcher starts Python with
|
||||
``sys.executable`` already prefixed by the extended-length path marker.
|
||||
That prefix would otherwise propagate into PlatformIO's ``PYTHONEXE`` and
|
||||
break SCons-emitted command lines run through ``cmd.exe``.
|
||||
"""
|
||||
CORE.build_path = str(setup_core / "build" / "test")
|
||||
prefixed_exe = (
|
||||
"\\\\?\\C:\\Users\\jesse\\AppData\\Local\\ESPHome Builder\\python\\python.exe"
|
||||
)
|
||||
stripped_exe = (
|
||||
"C:\\Users\\jesse\\AppData\\Local\\ESPHome Builder\\python\\python.exe"
|
||||
)
|
||||
|
||||
with (
|
||||
patch.dict(os.environ, {}, clear=False),
|
||||
patch("esphome.platformio_api.sys.platform", "win32"),
|
||||
patch("esphome.platformio_api.sys.executable", prefixed_exe),
|
||||
):
|
||||
# Pop any pre-existing PYTHONEXEPATH so the assertion below reflects
|
||||
# what run_platformio_cli set, not whatever the test runner's
|
||||
# environment happened to contain.
|
||||
os.environ.pop("PYTHONEXEPATH", None)
|
||||
mock_run_external_process.return_value = 0
|
||||
platformio_api.run_platformio_cli("test", "arg")
|
||||
|
||||
# The subprocess is invoked with the stripped executable path.
|
||||
mock_run_external_process.assert_called_once()
|
||||
args = mock_run_external_process.call_args[0]
|
||||
assert args[0] == stripped_exe
|
||||
# PYTHONEXEPATH is exported with the stripped path so PlatformIO's
|
||||
# get_pythonexe_path() picks it up in the subprocess.
|
||||
assert os.environ["PYTHONEXEPATH"] == stripped_exe
|
||||
|
||||
|
||||
def test_run_platformio_cli_does_not_set_pythonexepath_without_strip(
|
||||
setup_core: Path, mock_run_external_process: Mock
|
||||
) -> None:
|
||||
r"""PYTHONEXEPATH is not touched when sys.executable has no ``\\?\`` prefix.
|
||||
|
||||
Setting it unconditionally would clobber a user-provided value (or
|
||||
interfere with non-Windows tooling that has no prefix to strip).
|
||||
"""
|
||||
CORE.build_path = str(setup_core / "build" / "test")
|
||||
plain_exe = "/usr/bin/python3"
|
||||
|
||||
with (
|
||||
patch.dict(os.environ, {}, clear=False),
|
||||
patch("esphome.platformio_api.sys.platform", "linux"),
|
||||
patch("esphome.platformio_api.sys.executable", plain_exe),
|
||||
):
|
||||
os.environ.pop("PYTHONEXEPATH", None)
|
||||
mock_run_external_process.return_value = 0
|
||||
platformio_api.run_platformio_cli("test", "arg")
|
||||
|
||||
mock_run_external_process.assert_called_once()
|
||||
args = mock_run_external_process.call_args[0]
|
||||
assert args[0] == plain_exe
|
||||
assert "PYTHONEXEPATH" not in os.environ
|
||||
|
||||
|
||||
def test_run_platformio_cli_run_builds_command(
|
||||
setup_core: Path, mock_run_platformio_cli: Mock
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user