@@ -540,6 +540,25 @@ def test__pip_build_called_process_error(tmp_path):
540540 assert isinstance (excinfo .value .__cause__ , (FileNotFoundError , CalledProcessError ))
541541
542542
543+ def test__pip_build_windows_quotes_version_specifiers (tmp_path ):
544+ """On Windows, args with '<' must be quoted so CMD doesn't treat them as redirection."""
545+ command = ["pip" , "install" , "setuptools<82" ]
546+ patch_cmd = patch .object (
547+ PythonLanguagePlugin , "_make_pip_command" , return_value = command
548+ )
549+ patch_os = patch ("rpdk.python.codegen.os.name" , "nt" )
550+ patch_run = patch ("rpdk.python.codegen.subprocess_run" , autospec = True )
551+
552+ with patch_cmd , patch_os , patch_run as mock_run :
553+ PythonLanguagePlugin ._pip_build (tmp_path )
554+
555+ call_args = mock_run .call_args
556+ cmd_arg = call_args [0 ][0 ]
557+ assert isinstance (cmd_arg , str ), "Windows branch must pass a string to subprocess_run"
558+ assert "setuptools<82" in cmd_arg
559+ assert call_args [1 ]["shell" ] is True
560+
561+
543562def test__build_pip (plugin ):
544563 plugin ._use_docker = False
545564 plugin ._no_docker = True
@@ -592,7 +611,10 @@ def test__build_pip_windows(plugin):
592611 plugin ._pip_build (temppath )
593612
594613 mock_subproc .assert_called_once_with (
595- plugin ._make_pip_command (temppath ),
614+ " " .join (
615+ f'"{ a } "' if any (c in a for c in ("<" , ">" , "&" , "|" )) else a
616+ for a in plugin ._make_pip_command (temppath )
617+ ),
596618 stdout = ANY ,
597619 stderr = ANY ,
598620 cwd = temppath ,
0 commit comments