Skip to content

Commit

Permalink
Deprecate lambdex for building FaaS packages
Browse files Browse the repository at this point in the history
  • Loading branch information
huonw committed May 20, 2023
1 parent 2bc078b commit 6255594
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 11 deletions.
5 changes: 3 additions & 2 deletions src/python/pants/backend/awslambda/python/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from pants.core.util_rules.environments import EnvironmentField
from pants.engine.rules import Get, collect_rules, rule
from pants.engine.unions import UnionRule
from pants.option.global_options import GlobalOptions
from pants.util.logging import LogLevel

logger = logging.getLogger(__name__)
Expand All @@ -44,9 +45,9 @@ class PythonAwsLambdaFieldSet(PackageFieldSet):

@rule(desc="Create Python AWS Lambda", level=LogLevel.DEBUG)
async def package_python_awslambda(
field_set: PythonAwsLambdaFieldSet,
field_set: PythonAwsLambdaFieldSet, global_options: GlobalOptions
) -> BuiltPackage:
layout = PythonFaaSLayout(field_set.layout.value)
layout = field_set.layout.resolve_value(field_set.address, global_options)

if layout is PythonFaaSLayout.LAMBDEX:
return await Get(
Expand Down
12 changes: 12 additions & 0 deletions src/python/pants/backend/awslambda/python/rules_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ def complete_platform(rule_runner: PythonRuleRunner) -> bytes:
).stdout


_first_run_of_deprecated_warning = True


@pytest.mark.platform_specific_behavior
@pytest.mark.parametrize(
"major_minor_interpreter",
Expand Down Expand Up @@ -180,6 +183,15 @@ def handler(event, context):
if sys.platform == "darwin":
assert "`python_awslambda` targets built on macOS may fail to build." in caplog.text

global _first_run_of_deprecated_warning
if _first_run_of_deprecated_warning:
_first_run_of_deprecated_warning = False
assert (
'DEPRECATED: use of `layout="lambdex"` (set by default) in target src/python/foo/bar:lambda is scheduled to be removed'
in caplog.text
)
assert "use_deprecated_lambdex_layout" in caplog.text

zip_file_relpath, content = create_python_awslambda(
rule_runner,
Address("src/python/foo/bar", target_name="slimlambda"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from pants.core.util_rules.environments import EnvironmentField
from pants.engine.rules import Get, collect_rules, rule
from pants.engine.unions import UnionRule
from pants.option.global_options import GlobalOptions
from pants.util.logging import LogLevel

logger = logging.getLogger(__name__)
Expand All @@ -44,9 +45,10 @@ class PythonGoogleCloudFunctionFieldSet(PackageFieldSet):

@rule(desc="Create Python Google Cloud Function", level=LogLevel.DEBUG)
async def package_python_google_cloud_function(
field_set: PythonGoogleCloudFunctionFieldSet,
field_set: PythonGoogleCloudFunctionFieldSet, global_options: GlobalOptions
) -> BuiltPackage:
layout = PythonFaaSLayout(field_set.layout.value)
layout = field_set.layout.resolve_value(field_set.address, global_options)

if layout is PythonFaaSLayout.LAMBDEX:
return await Get(
BuiltPackage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ def complete_platform(rule_runner: PythonRuleRunner) -> bytes:
).stdout


_first_run_of_deprecated_warning = True


@pytest.mark.platform_specific_behavior
@pytest.mark.parametrize(
"major_minor_interpreter",
Expand Down Expand Up @@ -180,6 +183,15 @@ def handler(event, context):
in caplog.text
)

global _first_run_of_deprecated_warning
if _first_run_of_deprecated_warning:
_first_run_of_deprecated_warning = False
assert (
'DEPRECATED: use of `layout="lambdex"` (set by default) in target src/python/foo/bar:lambda is scheduled to be removed'
in caplog.text
)
assert "use_deprecated_lambdex_layout" in caplog.text


def test_warn_files_targets(rule_runner: PythonRuleRunner, caplog) -> None:
rule_runner.write_files(
Expand Down
24 changes: 23 additions & 1 deletion src/python/pants/backend/python/subsystems/lambdex.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,33 @@
from pants.backend.python.subsystems.python_tool_base import LockfileRules, PythonToolBase
from pants.backend.python.target_types import ConsoleScript
from pants.engine.rules import collect_rules
from pants.util.strutil import softwrap


class Lambdex(PythonToolBase):
# these aren't read automatically, but are defined for use in faas.py
removal_hint = softwrap(
"""
Either use `layout=\"zip\"` in `python_awslambda` or `python_google_cloud_function` targets
to build flat packages without dynamic PEX start-up (recommended), or use `pex_binary` if dependency
selection is required on start-up (for instance, one package is deployed to multiple
runtimes). For a `pex_binary`, add `__pex__` to the import path for the handler: for
example, if the handler function `func` is defined in `foo/bar.py`, configure
`__pex__.foo.bar.func` as the handler.
"""
)
removal_version = "2.19.0.dev0"

options_scope = "lambdex"
help = "A tool for turning .pex files into Function-as-a-Service artifacts (https://github.com/pantsbuild/lambdex)."
help = softwrap(
f"""
A tool for turning .pex files into Function-as-a-Service artifacts (https://github.com/pantsbuild/lambdex).
Lambdex is no longer necessary: {removal_hint}
This will be removed in Pants {removal_version}.
"""
)

default_version = "lambdex>=0.1.9"
default_main = ConsoleScript("lambdex")
Expand Down
46 changes: 40 additions & 6 deletions src/python/pants/backend/python/util_rules/faas.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from pants.backend.python.util_rules.pex_from_targets import rules as pex_from_targets_rules
from pants.backend.python.util_rules.pex_venv import PexVenv, PexVenvLayout, PexVenvRequest
from pants.backend.python.util_rules.pex_venv import rules as pex_venv_rules
from pants.base.deprecated import warn_or_error
from pants.core.goals.package import BuiltPackage, BuiltPackageArtifact, OutputPathField
from pants.core.target_types import FileSourceField
from pants.engine.addresses import Address, UnparsedAddressInputs
Expand Down Expand Up @@ -72,10 +73,11 @@
targets_with_sources_types,
)
from pants.engine.unions import UnionMembership, UnionRule
from pants.option.global_options import GlobalOptions
from pants.source.filespec import Filespec
from pants.source.source_root import SourceRoot, SourceRootRequest
from pants.util.docutil import bin_name, doc_url
from pants.util.strutil import help_text
from pants.util.strutil import help_text, softwrap

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -284,23 +286,23 @@ def to_platform_string(self) -> None | str:


class PythonFaaSLayout(Enum):
# TODO: deprecate lambdex layout, since PEX can be used directly now
LAMBDEX = "lambdex"
ZIP = "zip"


class PythonFaaSLayoutField(StringField):
alias = "layout"
valid_choices = PythonFaaSLayout
default = PythonFaaSLayout.LAMBDEX.value
default = None

help = help_text(
"""
The layout used for the function artifact.
With the `lambdex` layout (default), the artifact is created as a Lambdex, which is a normal
PEX that's been adjusted to include a shim file for the handler. This requires dynamically
choosing dependencies on start-up.
With the `lambdex` layout (default unless `[GLOBAL].use_deprecated_lambdex_layout` is set to
`false`, deprecated), the artifact is created as a Lambdex, which is a normal PEX that's
been adjusted to include a shim file for the handler. This requires dynamically choosing
dependencies on start-up.
With the `zip` layout (recommended), the artifact contains first and third party code at the
top level, similar to building with `pip install --target=...`. This layout chooses the
Expand All @@ -311,6 +313,38 @@ class PythonFaaSLayoutField(StringField):
"""
)

# TODO: this whole function can disappear once the LAMBDEX option is removed
def resolve_value(self, address: Address, global_options: GlobalOptions) -> PythonFaaSLayout:
if self.value is None:
layout = (
PythonFaaSLayout.LAMBDEX
if global_options.use_deprecated_lambdex_layout
else PythonFaaSLayout.ZIP
)
else:
layout = PythonFaaSLayout(self.value)

if layout is PythonFaaSLayout.LAMBDEX and global_options.options.is_default(
"use_deprecated_lambdex_layout"
):
from_default = " (set by default)" if self.value is None else ""
warn_or_error(
Lambdex.removal_version,
f'use of `layout="lambdex"`{from_default} in target {address}',
softwrap(
f"""
{Lambdex.removal_hint}
Set `use_deprecated_lambdex_layout = false` in the `[GLOBAL]` section of
`pants.toml` to switch to using `layout="zip"` by default everywhere, or set it
to `true` to temporarily continue with the old behaviour and silence this
warning.
"""
),
)

return layout


@rule
async def digest_complete_platforms(
Expand Down
14 changes: 14 additions & 0 deletions src/python/pants/option/global_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -1752,6 +1752,20 @@ class GlobalOptions(BootstrapOptions, Subsystem):
default=[],
)

use_deprecated_lambdex_layout = BoolOption(
default=True,
help=softwrap(
"""
If `true`, any `python_awslambda` and `python_google_cloud_function` targets use the
`layout="lambdex"` by default. This is the older behaviour of these targets, and is
being removed.
If `false`, they will instead use the improved `layout="zip"` by default, which will
become the default in future.
"""
),
)

@classmethod
def validate_instance(cls, opts):
"""Validates an instance of global options for cases that are not prohibited via
Expand Down

0 comments on commit 6255594

Please sign in to comment.