Skip to content

Commit

Permalink
synthesise a handler module
Browse files Browse the repository at this point in the history
  • Loading branch information
huonw committed May 17, 2023
1 parent 0346958 commit 8087903
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 19 deletions.
3 changes: 3 additions & 0 deletions src/python/pants/backend/awslambda/python/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ async def package_python_awslambda(
layout=layout,
output_path=field_set.output_path,
include_requirements=field_set.include_requirements.value,
# this isn't built or run via lambdex, but the name is arbitrary, so we stick with the
# 'tradition' to reduce churn.
reexported_handler_module="lambdex_handler",
),
)

Expand Down
14 changes: 8 additions & 6 deletions src/python/pants/backend/awslambda/python/rules_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,27 +296,29 @@ def handler(event, context):
zip_file_relpath, content = create_python_awslambda(
rule_runner,
Address("src/python/foo/bar", target_name="lambda"),
expected_extra_log_lines=(
" Handler: foo.bar.hello_world.handler",
),
expected_extra_log_lines=(" Handler: lambdex_handler.handler",),
)
assert "src.python.foo.bar/lambda.zip" == zip_file_relpath

zipfile = ZipFile(BytesIO(content))
names = set(zipfile.namelist())
assert "mureq/__init__.py" in names
assert "foo/bar/hello_world.py" in names
assert (
zipfile.read("lambdex_handler.py") == b"from foo.bar.hello_world import handler as handler"
)

zip_file_relpath, content = create_python_awslambda(
rule_runner,
Address("src/python/foo/bar", target_name="slimlambda"),
expected_extra_log_lines=(
" Handler: foo.bar.hello_world.handler",
),
expected_extra_log_lines=(" Handler: lambdex_handler.handler",),
)
assert "src.python.foo.bar/slimlambda.zip" == zip_file_relpath

zipfile = ZipFile(BytesIO(content))
names = set(zipfile.namelist())
assert "mureq/__init__.py" not in names
assert "foo/bar/hello_world.py" in names
assert (
zipfile.read("lambdex_handler.py") == b"from foo.bar.hello_world import handler as handler"
)
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ async def package_python_google_cloud_function(
layout=layout,
output_path=field_set.output_path,
include_requirements=True,
# GCP requires "Your main file must be named main.py"
# https://cloud.google.com/functions/docs/writing#directory-structure-python
# setting a `GOOGLE_FUNCTION_SOURCE` Google Cloud build environment variable; e.g.:
# `gcloud functions deploy {--build-env-vars-file,--set-build-env-vars}`, but it's non-trivial
# to do this right or with intended effect) and the handler name you configure GCF with is just
# the unqualified function name, which we log here.
reexported_handler_module="main",
log_only_reexported_handler_func=True,
),
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,13 +276,12 @@ def handler(event, context):
zip_file_relpath, content = create_python_google_cloud_function(
rule_runner,
Address("src/python/foo/bar", target_name="gcf"),
expected_extra_log_lines=(
" Handler: foo.bar.hello_world.handler",
),
expected_extra_log_lines=(" Handler: handler",),
)
assert "src.python.foo.bar/gcf.zip" == zip_file_relpath

zipfile = ZipFile(BytesIO(content))
names = set(zipfile.namelist())
assert "mureq/__init__.py" in names
assert "foo/bar/hello_world.py" in names
assert zipfile.read("main.py") == b"from foo.bar.hello_world import handler as handler"
46 changes: 36 additions & 10 deletions src/python/pants/backend/python/util_rules/faas.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,14 @@
from pants.core.goals.package import BuiltPackage, BuiltPackageArtifact, OutputPathField
from pants.core.target_types import FileSourceField
from pants.engine.addresses import Address, UnparsedAddressInputs
from pants.engine.fs import GlobMatchErrorBehavior, PathGlobs, Paths
from pants.engine.fs import (
CreateDigest,
Digest,
FileContent,
GlobMatchErrorBehavior,
PathGlobs,
Paths,
)
from pants.engine.platform import Platform
from pants.engine.process import ProcessResult
from pants.engine.rules import Get, MultiGet, collect_rules, rule
Expand Down Expand Up @@ -450,6 +457,9 @@ class BuildPythonFaaSRequest:

include_requirements: bool

reexported_handler_module: str
log_only_reexported_handler_func: bool = False


@rule
async def build_python_faas(
Expand All @@ -466,12 +476,27 @@ async def build_python_faas(
"--resolve-local-platforms",
)

complete_platforms = await Get(
CompletePlatforms, PythonFaaSCompletePlatforms, request.complete_platforms
complete_platforms, handler = await MultiGet(
Get(CompletePlatforms, PythonFaaSCompletePlatforms, request.complete_platforms),
Get(ResolvedPythonFaaSHandler, ResolvePythonFaaSHandlerRequest(request.handler)),
)

# TODO: improve diagnostics if there's more than one platform/complete_platform

# synthesise a source file that gives a fixed handler path, no matter what the entry point is:
# some platforms require a certain name (e.g. GCF), and even on others, giving a fixed name
# means users don't need to duplicate the entry_point config in both the pants BUILD file and
# infrastructure definitions (the latter can always use the same names, for every lambda).
reexported_handler_file = f"{request.reexported_handler_module}.py"
reexported_handler_func = "handler"
reexported_handler_content = (
f"from {handler.module} import {handler.func} as {reexported_handler_func}"
)
additional_sources = await Get(
Digest,
CreateDigest([FileContent(reexported_handler_file, reexported_handler_content.encode())]),
)

repository_filename = "faas_repository.pex"
pex_request = PexFromTargetsRequest(
addresses=[request.address],
Expand All @@ -483,12 +508,10 @@ async def build_python_faas(
layout=PexLayout.PACKED,
additional_args=additional_pex_args,
additional_lockfile_args=additional_pex_args,
additional_sources=additional_sources,
)

pex_result, handler = await MultiGet(
Get(Pex, PexFromTargetsRequest, pex_request),
Get(ResolvedPythonFaaSHandler, ResolvePythonFaaSHandlerRequest(request.handler)),
)
pex_result = await Get(Pex, PexFromTargetsRequest, pex_request)

output_filename = request.output_path.value_or_default(file_ending="zip")

Expand All @@ -504,11 +527,14 @@ async def build_python_faas(
),
)

if request.log_only_reexported_handler_func:
handler_text = reexported_handler_func
else:
handler_text = f"{request.reexported_handler_module}.{reexported_handler_func}"

artifact = BuiltPackageArtifact(
output_filename,
extra_log_lines=(
f" Handler: {handler.module}.{handler.func}",
),
extra_log_lines=(f" Handler: {handler_text}",),
)
return BuiltPackage(digest=result.digest, artifacts=(artifact,))

Expand Down

0 comments on commit 8087903

Please sign in to comment.