async-lambda-unstable 0.6.14__tar.gz → 0.6.16__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/PKG-INFO +33 -1
  2. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/README.md +32 -0
  3. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/__init__.py +1 -1
  4. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/build_config.py +6 -0
  5. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/cli.py +18 -1
  6. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/controller.py +16 -2
  7. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/models/task.py +40 -2
  8. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/.gitignore +0 -0
  9. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/client.py +0 -0
  10. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/config.py +0 -0
  11. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/defer.py +0 -0
  12. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/env.py +0 -0
  13. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/middleware.py +0 -0
  14. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/models/__init__.py +0 -0
  15. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/models/api_response.py +0 -0
  16. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/models/case_insensitive_dict.py +0 -0
  17. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/models/events/__init__.py +0 -0
  18. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/models/events/api_event.py +0 -0
  19. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/models/events/base_event.py +0 -0
  20. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/models/events/dynamodb_event.py +0 -0
  21. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/models/events/managed_sqs_batch_event.py +0 -0
  22. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/models/events/managed_sqs_event.py +0 -0
  23. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/models/events/scheduled_event.py +0 -0
  24. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/models/events/unmanaged_sqs_event.py +0 -0
  25. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/models/mock/mock_context.py +0 -0
  26. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/models/mock/mock_event.py +0 -0
  27. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/payload_encoder.py +0 -0
  28. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/py.typed +0 -0
  29. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/async_lambda/util.py +0 -0
  30. {async_lambda_unstable-0.6.14 → async_lambda_unstable-0.6.16}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: async-lambda-unstable
3
- Version: 0.6.14
3
+ Version: 0.6.16
4
4
  Summary: A framework for creating AWS Lambda Async Workflows. - Unstable Branch
5
5
  Author-email: "Nuclei, Inc" <engineering@nuclei.ai>
6
6
  Requires-Dist: click>=8.0.0
@@ -249,6 +249,38 @@ and merging is done thru concatenation.
249
249
 
250
250
  [The value is passed to the `Layers` property on `SAM::Serverless::Function`](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html#sam-function-layers)
251
251
 
252
+ ### image_uri
253
+
254
+ ```
255
+ "ACCOUNT.dkr.ecr.REGION.amazonaws.com/REPOSITORY:TAG"
256
+ ```
257
+
258
+ Package the functions as a Lambda container image instead of a zip bundle, which raises the
259
+ package size limit from 250 MB (unzipped) to 10 GB.
260
+
261
+ When set, every function gets `PackageType: Image`, `ImageUri`, and `ImageConfig.Command` set to
262
+ `<module>.lambda_handler` in place of `CodeUri`, `Handler`, and `Runtime`, and `async-lambda build`
263
+ writes only the template (no `deployment.zip`). The image is built and pushed separately; it must
264
+ be based on an AWS Lambda Python base image (or implement the Lambda runtime interface) and contain
265
+ the app module, `src/`, and its dependencies.
266
+
267
+ `async-lambda build app --image-uri <uri>` sets this for a single build, overriding the config (including
268
+ stage-level values), so a CI pipeline can pin the image it just pushed.
269
+
270
+ Image functions are named `<name>-<task_id>-image`. Lambda cannot change an existing function's package
271
+ type, so adding or removing `image_uri` replaces each function, and CloudFormation cannot replace a
272
+ function under the same custom name. The suffix lets the new function be created before the old one is
273
+ deleted, so switching needs no manual deletion or downtime. Queue and schedule names are unchanged.
274
+
275
+ Lambda layers are not supported on container images, so `layers` raises an error at build time when
276
+ `image_uri` is set. SnapStart (`snap_start=True`) is supported on container images that use an AWS
277
+ base image for Python 3.12+, Java 11+, or .NET 8+.
278
+
279
+ SnapStart only applies to published versions, never `$LATEST`. A task with `snap_start=True` sets
280
+ `AutoPublishAlias: live` and `AutoPublishAliasAllProperties: true`, so each deploy that changes any
281
+ function property (such as `Timeout` or `EphemeralStorage`) publishes a version. SAM points the task's event sources
282
+ (API Gateway, SQS, schedules) at the `live` alias. This works for ZIP and image functions.
283
+
252
284
  ### subnet_ids
253
285
 
254
286
  ```
@@ -237,6 +237,38 @@ and merging is done thru concatenation.
237
237
 
238
238
  [The value is passed to the `Layers` property on `SAM::Serverless::Function`](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html#sam-function-layers)
239
239
 
240
+ ### image_uri
241
+
242
+ ```
243
+ "ACCOUNT.dkr.ecr.REGION.amazonaws.com/REPOSITORY:TAG"
244
+ ```
245
+
246
+ Package the functions as a Lambda container image instead of a zip bundle, which raises the
247
+ package size limit from 250 MB (unzipped) to 10 GB.
248
+
249
+ When set, every function gets `PackageType: Image`, `ImageUri`, and `ImageConfig.Command` set to
250
+ `<module>.lambda_handler` in place of `CodeUri`, `Handler`, and `Runtime`, and `async-lambda build`
251
+ writes only the template (no `deployment.zip`). The image is built and pushed separately; it must
252
+ be based on an AWS Lambda Python base image (or implement the Lambda runtime interface) and contain
253
+ the app module, `src/`, and its dependencies.
254
+
255
+ `async-lambda build app --image-uri <uri>` sets this for a single build, overriding the config (including
256
+ stage-level values), so a CI pipeline can pin the image it just pushed.
257
+
258
+ Image functions are named `<name>-<task_id>-image`. Lambda cannot change an existing function's package
259
+ type, so adding or removing `image_uri` replaces each function, and CloudFormation cannot replace a
260
+ function under the same custom name. The suffix lets the new function be created before the old one is
261
+ deleted, so switching needs no manual deletion or downtime. Queue and schedule names are unchanged.
262
+
263
+ Lambda layers are not supported on container images, so `layers` raises an error at build time when
264
+ `image_uri` is set. SnapStart (`snap_start=True`) is supported on container images that use an AWS
265
+ base image for Python 3.12+, Java 11+, or .NET 8+.
266
+
267
+ SnapStart only applies to published versions, never `$LATEST`. A task with `snap_start=True` sets
268
+ `AutoPublishAlias: live` and `AutoPublishAliasAllProperties: true`, so each deploy that changes any
269
+ function property (such as `Timeout` or `EphemeralStorage`) publishes a version. SAM points the task's event sources
270
+ (API Gateway, SQS, schedules) at the `live` alias. This works for ZIP and image functions.
271
+
240
272
  ### subnet_ids
241
273
 
242
274
  ```
@@ -22,4 +22,4 @@ from .models.events.managed_sqs_event import ManagedSQSEvent as ManagedSQSEvent
22
22
  from .models.events.scheduled_event import ScheduledEvent as ScheduledEvent
23
23
  from .models.events.unmanaged_sqs_event import UnmanagedSQSEvent as UnmanagedSQSEvent
24
24
 
25
- __version__ = "0.6.14"
25
+ __version__ = "0.6.16"
@@ -75,6 +75,7 @@ class AsyncLambdaBuildConfig:
75
75
  certificate_arn (Optional[str]): ARN of the SSL certificate for the domain.
76
76
  hosted_zone_id (Optional[str]): Hosted zone name for the Lambda/API.
77
77
  auto_create_acm_certificate (bool): Whether or not to auto create a certificate in ACM for the domain name. Must be used in conjunction with hosted_zone_id and domain_name to be used.
78
+ image_uri (Optional[str]): URI of a Lambda container image. When set, functions are packaged as container images instead of a zip bundle.
78
79
 
79
80
  Methods:
80
81
  new(config: dict) -> AsyncLambdaBuildConfig:
@@ -99,6 +100,7 @@ class AsyncLambdaBuildConfig:
99
100
  hosted_zone_id: Optional[str] = None
100
101
  auto_create_acm_certificate: Optional[bool] = None
101
102
  queue_age_alarm: Optional[QueueAgeAlarmConfig] = None
103
+ image_uri: Optional[str] = None
102
104
 
103
105
  @classmethod
104
106
  def new(cls, config: dict) -> "AsyncLambdaBuildConfig":
@@ -121,6 +123,7 @@ class AsyncLambdaBuildConfig:
121
123
  - tls_version (str, optional): TLS version to use.
122
124
  - certificate_arn (str, optional): ARN of the SSL certificate.
123
125
  - hosted_zone_id (str, optional): Hosted zone ID for the Lambda/API.
126
+ - image_uri (str, optional): URI of the Lambda container image.
124
127
 
125
128
  Returns:
126
129
  AsyncLambdaBuildConfig: A new instance configured with the provided options.
@@ -145,6 +148,7 @@ class AsyncLambdaBuildConfig:
145
148
  certificate_arn=config.get("certificate_arn"),
146
149
  hosted_zone_id=config.get("hosted_zone_id"),
147
150
  auto_create_acm_certificate=config.get("auto_create_acm_certificate"),
151
+ image_uri=config.get("image_uri"),
148
152
  )
149
153
 
150
154
  def merge(self, other: "AsyncLambdaBuildConfig"):
@@ -168,6 +172,8 @@ class AsyncLambdaBuildConfig:
168
172
  self.auto_create_acm_certificate = other.auto_create_acm_certificate
169
173
  if other.queue_age_alarm is not None:
170
174
  self.queue_age_alarm = other.queue_age_alarm
175
+ if other.image_uri is not None:
176
+ self.image_uri = other.image_uri
171
177
 
172
178
  @property
173
179
  def function_properties(self):
@@ -108,8 +108,16 @@ def import_module_get_controller(module_name: str, config, stage: Optional[str])
108
108
  @click.option(
109
109
  "-e", "--extras", help="The path to a json file to merge the output template with."
110
110
  )
111
+ @click.option(
112
+ "--image-uri",
113
+ help="Package the functions as this Lambda container image instead of a zip bundle. Overrides `image_uri` in the config.",
114
+ )
111
115
  def build(
112
- module: str, output: str, stage: Optional[str] = None, extras: Optional[str] = None
116
+ module: str,
117
+ output: str,
118
+ stage: Optional[str] = None,
119
+ extras: Optional[str] = None,
120
+ image_uri: Optional[str] = None,
113
121
  ):
114
122
  """
115
123
  Builds/generates the SAM template for the given module.
@@ -119,6 +127,10 @@ def build(
119
127
  config_file = dir.joinpath(".async_lambda/config.json")
120
128
  if config_file.exists():
121
129
  config = json.loads(config_file.read_bytes())
130
+ if image_uri is not None:
131
+ config["image_uri"] = image_uri
132
+ for stage_config in config.get("stages", {}).values():
133
+ stage_config.pop("image_uri", None)
122
134
  controller = import_module_get_controller(
123
135
  module_name=module, config=config, stage=stage
124
136
  )
@@ -157,6 +169,11 @@ def build(
157
169
 
158
170
  if os.path.exists(".async_lambda/build"):
159
171
  shutil.rmtree(".async_lambda/build")
172
+
173
+ if get_build_config_for_stage(config, stage).image_uri is not None:
174
+ click.echo("Container image configured, skipping zip bundle.")
175
+ return
176
+
160
177
  os.makedirs(".async_lambda/build/packages", exist_ok=True)
161
178
 
162
179
  if dir.joinpath("requirements.txt").exists():
@@ -620,6 +620,20 @@ class AsyncLambdaController:
620
620
  Generates the SAM Template for this project.
621
621
  """
622
622
  build_config = get_build_config_for_stage(config_dict, stage)
623
+ if build_config.image_uri is not None and len(build_config.layers) > 0:
624
+ raise Exception(
625
+ "Lambda layers cannot be used with container images (image_uri is set)."
626
+ )
627
+ # SAM Globals does not accept PackageType or ImageConfig, so image functions
628
+ # carry their package properties individually (see AsyncLambdaTask.get_sam_template).
629
+ package_properties = (
630
+ {}
631
+ if build_config.image_uri is not None
632
+ else {
633
+ "Handler": f"{module}.lambda_handler",
634
+ "Runtime": config.runtime,
635
+ }
636
+ )
623
637
  s3_bucket_properties = {}
624
638
  if config.s3_payload_retention:
625
639
  s3_bucket_properties["LifecycleConfiguration"] = {
@@ -638,8 +652,7 @@ class AsyncLambdaController:
638
652
  "Globals": {
639
653
  "Function": {
640
654
  "Tags": build_config.tags,
641
- "Handler": f"{module}.lambda_handler",
642
- "Runtime": config.runtime,
655
+ **package_properties,
643
656
  "Environment": {
644
657
  "Variables": {
645
658
  "ASYNC_LAMBDA_PAYLOAD_S3_BUCKET": {
@@ -829,6 +842,7 @@ class AsyncLambdaController:
829
842
  task_ref_policy_ids=list(task_ref_policies.keys()),
830
843
  config_dict=config_dict,
831
844
  stage=stage,
845
+ module=module,
832
846
  ).items():
833
847
  template["Resources"][logical_id] = resource
834
848
 
@@ -14,7 +14,11 @@ from typing import (
14
14
  )
15
15
 
16
16
  from .. import env
17
- from ..build_config import get_build_config_for_task, get_override_build_config_for_task
17
+ from ..build_config import (
18
+ get_build_config_for_stage,
19
+ get_build_config_for_task,
20
+ get_override_build_config_for_task,
21
+ )
18
22
  from ..config import config
19
23
  from ..util import make_cf_tags
20
24
 
@@ -28,6 +32,8 @@ from .events.managed_sqs_event import ManagedSQSEvent
28
32
  from .events.scheduled_event import ScheduledEvent
29
33
  from .events.unmanaged_sqs_event import UnmanagedSQSEvent
30
34
 
35
+ IMAGE_FUNCTION_NAME_SUFFIX = "-image"
36
+
31
37
 
32
38
  class TaskTriggerType(Enum):
33
39
  """
@@ -548,6 +554,7 @@ class AsyncLambdaTask(Generic[EventType, RT]):
548
554
  task_ref_policy_ids: List[str],
549
555
  config_dict: dict,
550
556
  stage: Optional[str] = None,
557
+ module: Optional[str] = None,
551
558
  ) -> dict:
552
559
  """
553
560
  Generate an AWS SAM (Serverless Application Model) template for the current async lambda task.
@@ -568,6 +575,19 @@ class AsyncLambdaTask(Generic[EventType, RT]):
568
575
  override_config = get_override_build_config_for_task(
569
576
  config=config_dict, task_id=self.task_id, stage=stage
570
577
  )
578
+ if override_config.image_uri is not None:
579
+ raise Exception(
580
+ f"Task {self.task_id}: image_uri can only be set at the app or stage level, "
581
+ "because every task is packaged the same way."
582
+ )
583
+ image_uri = get_build_config_for_stage(config_dict, stage).image_uri
584
+ if image_uri is not None:
585
+ if len(build_config.layers) > 0:
586
+ raise Exception(
587
+ f"Task {self.task_id}: Lambda layers cannot be used with container images (image_uri is set)."
588
+ )
589
+ if module is None:
590
+ raise Exception("A module name is required to build image functions.")
571
591
  events = self.get_template_events()
572
592
  policy_sqs_resources = self.get_policy_sqs_resources()
573
593
 
@@ -622,8 +642,20 @@ class AsyncLambdaTask(Generic[EventType, RT]):
622
642
  "FunctionName": self.get_function_name(),
623
643
  "Timeout": self.timeout,
624
644
  "Events": events,
625
- "CodeUri": ".async_lambda/build/deployment.zip",
626
645
  }
646
+ if image_uri is not None:
647
+ # Lambda cannot change a function's package type in place, so switching between zip
648
+ # and image replaces the function, and a replacement cannot reuse the old name.
649
+ function_properties["FunctionName"] = (
650
+ f"{self.get_function_name()}{IMAGE_FUNCTION_NAME_SUFFIX}"
651
+ )
652
+ function_properties["PackageType"] = "Image"
653
+ function_properties["ImageUri"] = image_uri
654
+ function_properties["ImageConfig"] = {
655
+ "Command": [f"{module}.lambda_handler"]
656
+ }
657
+ else:
658
+ function_properties["CodeUri"] = ".async_lambda/build/deployment.zip"
627
659
 
628
660
  # cannot use shared role if the task has its own policies
629
661
  if config.shared_function_role and len(override_config.policies) == 0:
@@ -652,6 +684,12 @@ class AsyncLambdaTask(Generic[EventType, RT]):
652
684
  template[self.get_function_logical_id()]["Properties"]["SnapStart"] = {
653
685
  "ApplyOn": "PublishedVersions"
654
686
  }
687
+ template[self.get_function_logical_id()]["Properties"][
688
+ "AutoPublishAlias"
689
+ ] = "live"
690
+ template[self.get_function_logical_id()]["Properties"][
691
+ "AutoPublishAliasAllProperties"
692
+ ] = True
655
693
 
656
694
  if self.trigger_type in MANAGED_SQS_TASK_TYPES:
657
695
  dlq_task = self.get_dlq_task()