Skip to content

Commit 9159d8b

Browse files
authored
pin setuptools<82 in pip packaging command to prevent pkg_resources removal (#278)
* fix: pin setuptools<82 to prevent pkg_resources removal breaking contract tests * fix: quote shell-special chars in Windows pip command to prevent CMD redirection * fix: black formatting in codegen_test.py
1 parent 8c1494e commit 9159d8b

4 files changed

Lines changed: 46 additions & 3 deletions

File tree

python/rpdk/python/codegen.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ def _make_pip_command(base_path):
322322
str(base_path / "requirements.txt"),
323323
"--target",
324324
str(base_path / "build"),
325+
"setuptools<82",
325326
]
326327

327328
@staticmethod
@@ -399,9 +400,15 @@ def _pip_build(cls, base_path):
399400
LOG.warning("Starting pip build.")
400401
try:
401402
# On windows run pip command through the default shell (CMD)
403+
# Build a quoted string so CMD doesn't misinterpret '<' or '>'
404+
# in version specifiers (e.g. 'setuptools<82') as redirection.
402405
if os.name == "nt":
406+
cmd_str = " ".join(
407+
f'"{a}"' if any(c in a for c in ("<", ">", "&", "|")) else a
408+
for a in command
409+
)
403410
completed_proc = subprocess_run( # nosec
404-
command,
411+
cmd_str,
405412
stdout=PIPE,
406413
stderr=PIPE,
407414
cwd=base_path,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
{{ support_lib_name }}>=2.1.9
2+
setuptools<82

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def find_version(*file_paths):
4040
install_requires=[
4141
"cloudformation-cli>=0.2.26",
4242
"types-dataclasses>=0.1.5",
43-
"setuptools",
43+
"setuptools<82",
4444
],
4545
entry_points={
4646
"rpdk.v1.languages": [

tests/plugin/codegen_test.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,27 @@ 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 misinterpret them."""
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(
558+
cmd_arg, str
559+
), "Windows branch must pass a string to subprocess_run"
560+
assert "setuptools<82" in cmd_arg
561+
assert call_args[1]["shell"] is True
562+
563+
543564
def test__build_pip(plugin):
544565
plugin._use_docker = False
545566
plugin._no_docker = True
@@ -553,6 +574,17 @@ def test__build_pip(plugin):
553574
mock_pip.assert_called_once_with(sentinel.base_path)
554575

555576

577+
def test__make_pip_command_pins_setuptools_lt_82(tmp_path):
578+
"""setuptools<82 must be in the pip command to prevent pkg_resources removal."""
579+
cmd = PythonLanguagePlugin._make_pip_command(tmp_path)
580+
assert "setuptools<82" in cmd, (
581+
"setuptools<82 must be pinned in pip command — setuptools 82+ removes "
582+
"pkg_resources which breaks cloudformation-cli-python-lib at runtime"
583+
)
584+
assert str(tmp_path / "requirements.txt") in cmd
585+
assert str(tmp_path / "build") in cmd
586+
587+
556588
def test__build_pip_posix(plugin):
557589
patch_os_name = patch("rpdk.python.codegen.os.name", "posix")
558590
patch_subproc = patch("rpdk.python.codegen.subprocess_run")
@@ -581,7 +613,10 @@ def test__build_pip_windows(plugin):
581613
plugin._pip_build(temppath)
582614

583615
mock_subproc.assert_called_once_with(
584-
plugin._make_pip_command(temppath),
616+
" ".join(
617+
f'"{a}"' if any(c in a for c in ("<", ">", "&", "|")) else a
618+
for a in plugin._make_pip_command(temppath)
619+
),
585620
stdout=ANY,
586621
stderr=ANY,
587622
cwd=temppath,

0 commit comments

Comments
 (0)