Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lambda functions built using pipenv now require >= 2022.8.13 #2588

Closed
wants to merge 1 commit into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ This can be done by including the ``dockerize_pip`` configuration option which c

Payloads are uploaded to either the |cfngin_bucket| or an explicitly specified bucket, with the object key containing it's checksum to allow repeated uploads to be skipped in subsequent runs.

.. versionchanged:: 2.8.0
Use of pipenv now requires version ``>= 2022.8.13``.
This is the version that changed how ``requirements.txt`` files are generated.


****
Expand Down
4 changes: 4 additions & 0 deletions docs/source/cfngin/hooks/awslambda.PythonFunction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ It also ensures that binary files built during the install process are compatibl

.. versionadded:: 2.5.0

.. versionchanged:: 2.8.0
Use of pipenv now requires version ``>= 2022.8.13``.
This is the version that changed how ``requirements.txt`` files are generated.



****
Expand Down
3 changes: 3 additions & 0 deletions docs/source/cfngin/hooks/awslambda.PythonLayer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ It also ensures that binary files built during the install process are compatibl

.. versionadded:: 2.5.0

.. versionchanged:: 2.8.0
Use of pipenv now requires version ``>= 2022.8.13``.
This is the version that changed how ``requirements.txt`` files are generated.


****
Expand Down
79 changes: 28 additions & 51 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ gitpython = "*"
igittigitt = ">=2.0.5"
jinja2 = ">=2.7" # used in runway.cfngin.blueprints.raw
packaging = "*" # component of setuptools needed for version compare
pipenv = "2022.1.8"
pyOpenSSL = "*" # For embedded hook & associated script usage
pydantic = "^2.8.0"
pyhcl = "^0.4" # does not support HCL2, possibly move to extras_require in the future
Expand Down Expand Up @@ -82,7 +81,7 @@ ruff = "^0.6.4"
[tool.poetry.group.test.dependencies]
coverage = {extras = ["toml"], version = "^7.6.1"}
moto = {extras = ["ec2", "ecs", "iam", "s3", "ssm"], version = "^5.0.14"}
pipenv = "^2022.1.8" # only used in tests
pipenv = "^2024.0.1" # only used in tests
pytest = "^8.3.3"
pytest-cov = "^5.0.0"
pytest-mock = "^3.14.0"
Expand Down
11 changes: 9 additions & 2 deletions runway/cfngin/hooks/aws_lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,14 +354,21 @@ def _handle_use_pipenv(
sys.exit(1)
LOGGER.info("creating requirements.txt from Pipfile...")
req_path = os.path.join(dest_path, "requirements.txt") # noqa: PTH118
cmd = ["pipenv", "lock", "--requirements", "--keep-outdated"]
cmd = ["pipenv", "requirements"]
environ = os.environ.copy()
environ["PIPENV_IGNORE_VIRTUALENVS"] = "1"

if not (Path(package_root) / "Pipfile.lock").is_file():
LOGGER.warning("Pipfile.lock does not exist! creating it...")
subprocess.check_call(["pipenv", "lock"], cwd=package_root, env=environ)

if python_path:
cmd.insert(0, python_path)
cmd.insert(1, "-m")
with (
open(req_path, "w", encoding="utf-8") as requirements, # noqa: PTH123
subprocess.Popen(
cmd, cwd=package_root, stdout=requirements, stderr=subprocess.PIPE
cmd, cwd=package_root, env=environ, stdout=requirements, stderr=subprocess.PIPE
) as pipenv_process,
):
_stdout, stderr = pipenv_process.communicate(timeout=timeout)
Expand Down
14 changes: 8 additions & 6 deletions runway/dependency_managers/_pipenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,23 @@ def export(self, *, dev: bool = False, output: StrPath) -> Path:
"""Export the lock file to other formats (requirements.txt only).

The underlying command being executed by this method is
``pipenv lock --requirements``.
``pipenv requirements``.

Args:
dev: Include development dependencies.
output: Path to the output file.

"""
environ = self.ctx.env.vars.copy()
environ["PIPENV_IGNORE_VIRTUALENVS"] = "1"
output = Path(output)
if not (self.cwd / "Pipfile.lock").is_file():
LOGGER.warning("Pipfile.lock does not exist! creating it...")
self._run_command(self.generate_command("lock", quiet=True), env=environ)
try:
result = self._run_command(
self.generate_command(
"lock",
dev=dev,
requirements=True,
),
self.generate_command("requirements", dev=dev),
env=environ,
suppress_output=True,
)
except subprocess.CalledProcessError as exc:
Expand Down
39 changes: 5 additions & 34 deletions tests/unit/cfngin/hooks/test_awslambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -694,8 +694,9 @@ class TestHandleRequirements:
"""Test handle_requirements."""

PIPFILE = (
'[[source]]\nurl = "https://pypi.org/simple"\nverify_ssl = true\nname = "pypi"\n'
"[packages]\n[dev-packages]"
'[[source]]\nurl = "https://pypi.org/simple"\nverify_ssl = false\n'
'name = "pip_conf_index_global"\n'
'[packages]\nurllib3 = "~=2.2"\n[dev-packages]'
)
REQUIREMENTS = "-i https://pypi.org/simple\n\n"

Expand Down Expand Up @@ -733,22 +734,7 @@ def test_explicit_pipenv(self, tmp_path: Path) -> None:
assert req_path == str(requirements_txt)
assert (tmp_path / "Pipfile.lock").is_file()

expected_text = [
"#",
"# These requirements were autogenerated by pipenv",
"# To regenerate from the project's Pipfile, run:",
"#",
"# pipenv lock --requirements",
"#",
"",
"-i https://pypi.org/simple",
"",
]
if platform.system() == "Windows":
expected_text.append("")
assert requirements_txt.read_text() == "\n".join(expected_text)
else:
assert requirements_txt.read_text() == "\n".join(expected_text) + "\n"
assert "urllib3==" in requirements_txt.read_text()

def test_frozen_pipenv(
self, caplog: pytest.LogCaptureFixture, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
Expand Down Expand Up @@ -785,22 +771,7 @@ def test_implicit_pipenv(self, tmp_path: Path) -> None:
assert req_path == str(requirements_txt)
assert (tmp_path / "Pipfile.lock").is_file()

expected_text = [
"#",
"# These requirements were autogenerated by pipenv",
"# To regenerate from the project's Pipfile, run:",
"#",
"# pipenv lock --requirements",
"#",
"",
"-i https://pypi.org/simple",
"",
]
if platform.system() == "Windows":
expected_text.append("")
assert requirements_txt.read_text() == "\n".join(expected_text)
else:
assert requirements_txt.read_text() == "\n".join(expected_text) + "\n"
assert "urllib3==" in requirements_txt.read_text()

def test_raise_not_implimented(self) -> None:
"""Test NotImplimentedError is raised when no requirements file."""
Expand Down
48 changes: 31 additions & 17 deletions tests/unit/dependency_managers/test__pipenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import logging
import subprocess
from typing import TYPE_CHECKING, Any
from unittest.mock import Mock
from unittest.mock import Mock, call

import pytest

Expand All @@ -16,6 +16,8 @@

from pytest_mock import MockerFixture

from ..factories import MockCfnginContext

MODULE = "runway.dependency_managers._pipenv"


Expand Down Expand Up @@ -61,48 +63,60 @@ def test_dir_is_project(
def test_export(
self,
export_kwargs: dict[str, Any],
cfngin_context: MockCfnginContext,
mocker: MockerFixture,
tmp_path: Path,
) -> None:
"""Test export."""
expected = tmp_path / "expected" / "test.requirements.txt"
mock_generate_command = mocker.patch.object(
Pipenv, "generate_command", return_value="generate_command"
Pipenv, "generate_command", side_effect=["lock", "requirements"]
)
mock_run_command = mocker.patch.object(Pipenv, "_run_command", return_value="_run_command")
obj = Pipenv(Mock(), tmp_path)
obj = Pipenv(cfngin_context, tmp_path)
assert obj.export(output=expected, **export_kwargs) == expected
assert expected.is_file()
export_kwargs.setdefault("dev", False)
export_kwargs["requirements"] = True # hardcoded in the method
mock_generate_command.assert_called_once_with("lock", **export_kwargs)
mock_run_command.assert_called_once_with(
mock_generate_command.return_value, suppress_output=True
mock_generate_command.assert_has_calls(
[call("lock", quiet=True), call("requirements", **export_kwargs)]
)
cfngin_context.env.vars["PIPENV_IGNORE_VIRTUALENVS"] = "1"
mock_run_command.assert_has_calls(
[
call("lock", env=cfngin_context.env.vars),
call("requirements", env=cfngin_context.env.vars, suppress_output=True),
]
)

def test_export_raise_from_called_process_error(
self,
cfngin_context: MockCfnginContext,
mocker: MockerFixture,
tmp_path: Path,
) -> None:
"""Test export raise PoetryExportFailedError from CalledProcessError."""
output = tmp_path / "expected" / "test.requirements.txt"
mock_generate_command = mocker.patch.object(
Pipenv, "generate_command", return_value="generate_command"
)
mocker.patch.object(Pipenv, "generate_command", side_effect=["lock", "requirements"])
mock_run_command = mocker.patch.object(
Pipenv,
"_run_command",
side_effect=subprocess.CalledProcessError(
returncode=1,
cmd=mock_generate_command.return_value,
),
side_effect=[
None,
subprocess.CalledProcessError(
returncode=1,
cmd="pipenv requirements",
),
],
)

with pytest.raises(PipenvExportFailedError):
assert Pipenv(Mock(), tmp_path).export(output=output)
mock_run_command.assert_called_once_with(
mock_generate_command.return_value, suppress_output=True
assert Pipenv(cfngin_context, tmp_path).export(output=output)
cfngin_context.env.vars["PIPENV_IGNORE_VIRTUALENVS"] = "1"
mock_run_command.assert_has_calls(
[
call("lock", env=cfngin_context.env.vars),
call("requirements", env=cfngin_context.env.vars, suppress_output=True),
]
)

@pytest.mark.parametrize(
Expand Down
Loading