functualize-lambda 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,171 @@
1
+ """Functualize Lambda Adapter Plugin — AWS Lambda delivery for FunctualizeApp.
2
+
3
+ Implements the AdapterPlugin Protocol with adapter_type="lambda".
4
+ Supports two deployment patterns:
5
+
6
+ 1. Fat Lambda (internal routing):
7
+ - Single Lambda function handling multiple jobs
8
+ - Event contains {"job": "job_name", "kwargs": {...}}
9
+ - Adapter routes to the correct job via app.execute()
10
+
11
+ 2. Thin Lambda (per-job handler):
12
+ - One Lambda function per job, routing handled by infrastructure
13
+ - Use make_handler(job_name) to create a bound handler
14
+
15
+ Both patterns work with the static wiring fast path for <5ms cold start
16
+ when using JobSources(functions=[...]) with fully-explicit config.
17
+
18
+ Usage (fat Lambda):
19
+ app = FunctualizeApp("my-app", job_sources=JobSources(functions=[deploy, rollback]))
20
+ adapter = LambdaAdapter()
21
+ adapter(app)
22
+
23
+ def handler(event, context):
24
+ return adapter.run(event, context)
25
+
26
+ Usage (thin Lambda):
27
+ app = FunctualizeApp("my-app", job_sources=JobSources(functions=[deploy]))
28
+ adapter = LambdaAdapter()
29
+ adapter(app)
30
+
31
+ handler = adapter.make_handler("deploy")
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ from collections.abc import Callable
37
+ from typing import TYPE_CHECKING, Any
38
+
39
+ if TYPE_CHECKING:
40
+ from functualize.app.core import FunctualizeApp
41
+
42
+
43
+ class LambdaAdapter:
44
+ """AWS Lambda delivery adapter.
45
+
46
+ Satisfies the AdapterPlugin Protocol. Supports fat-Lambda (internal
47
+ routing via event["job"]) and thin-Lambda (per-job handler via
48
+ make_handler()) deployment patterns.
49
+
50
+ PluginMetadata attributes:
51
+ name: "functualize-lambda"
52
+ version: "1.0.0"
53
+ description: "AWS Lambda adapter supporting fat and thin Lambda patterns"
54
+ """
55
+
56
+ name: str = "functualize-lambda"
57
+ version: str = "1.0.0"
58
+ description: str = "AWS Lambda adapter supporting fat and thin Lambda patterns"
59
+ adapter_type: str = "lambda"
60
+
61
+ def __init__(self) -> None:
62
+ self._app: FunctualizeApp | None = None
63
+
64
+ def __call__(self, app: FunctualizeApp) -> None:
65
+ """Setup phase — store app reference.
66
+
67
+ Args:
68
+ app: The FunctualizeApp kernel instance.
69
+ """
70
+ self._app = app
71
+
72
+ def run(self, *args: Any, **kwargs: Any) -> Any:
73
+ """Fat Lambda entrypoint — route event to the correct job.
74
+
75
+ Parses the event to determine which job to execute and with
76
+ what arguments, then delegates to the kernel's execute method.
77
+
78
+ Args:
79
+ *args: Expected to be (event, context) where event is a dict
80
+ containing "job" (required) and "kwargs" (optional).
81
+
82
+ Returns:
83
+ Dict with "statusCode" (200 or 500) and "body" (result or
84
+ error message).
85
+
86
+ Raises:
87
+ RuntimeError: If run() is called before __call__(app).
88
+ """
89
+ if self._app is None:
90
+ raise RuntimeError("LambdaAdapter.run() called before __call__(app)")
91
+
92
+ # Extract event and context from positional args
93
+ event: dict[str, Any] = args[0] if args else kwargs.get("event", {})
94
+ # context is available but not used by the adapter itself
95
+ # _context = args[1] if len(args) > 1 else kwargs.get("context")
96
+
97
+ return self._handle_event(event)
98
+
99
+ def make_handler(self, job_name: str) -> Callable[..., Any]:
100
+ """Create a thin-Lambda handler bound to a specific job.
101
+
102
+ Returns a callable with the standard Lambda signature
103
+ (event, context) that always executes the specified job.
104
+ Event kwargs can still be provided via event.get("kwargs", {}).
105
+
106
+ Args:
107
+ job_name: The name of the job this handler will execute.
108
+
109
+ Returns:
110
+ A callable(event, context) -> dict suitable as a Lambda handler.
111
+
112
+ Raises:
113
+ RuntimeError: If make_handler() is called before __call__(app).
114
+ """
115
+ if self._app is None:
116
+ raise RuntimeError(
117
+ "LambdaAdapter.make_handler() called before __call__(app)"
118
+ )
119
+
120
+ app = self._app
121
+
122
+ def handler(event: dict[str, Any], context: Any) -> dict[str, Any]:
123
+ """Thin Lambda handler for job '{job_name}'."""
124
+ job_kwargs = event.get("kwargs", {})
125
+ try:
126
+ result = app.execute(job_name, **job_kwargs)
127
+ return {"statusCode": 200, "body": result.return_value}
128
+ except Exception as exc:
129
+ return {"statusCode": 500, "body": str(exc)}
130
+
131
+ # Set a useful name for debugging/logging
132
+ handler.__name__ = f"lambda_handler_{job_name}"
133
+ handler.__qualname__ = (
134
+ f"LambdaAdapter.make_handler.<locals>.handler[{job_name}]"
135
+ )
136
+
137
+ return handler
138
+
139
+ def shutdown(self) -> None:
140
+ """No-op shutdown. Lambda functions are stateless."""
141
+ pass
142
+
143
+ def _handle_event(self, event: dict[str, Any]) -> dict[str, Any]:
144
+ """Internal: parse event and execute the job.
145
+
146
+ Args:
147
+ event: Lambda event dict with "job" and optional "kwargs".
148
+
149
+ Returns:
150
+ Dict with "statusCode" and "body".
151
+ """
152
+ assert self._app is not None
153
+
154
+ try:
155
+ job_name = event["job"]
156
+ except (KeyError, TypeError) as exc:
157
+ return {
158
+ "statusCode": 400,
159
+ "body": f"Missing required field 'job' in event: {exc}",
160
+ }
161
+
162
+ job_kwargs = event.get("kwargs", {})
163
+
164
+ try:
165
+ result = self._app.execute(job_name, **job_kwargs)
166
+ return {"statusCode": 200, "body": result.return_value}
167
+ except Exception as exc:
168
+ return {"statusCode": 500, "body": str(exc)}
169
+
170
+
171
+ __all__ = ["LambdaAdapter"]
File without changes
@@ -0,0 +1,66 @@
1
+ Metadata-Version: 2.4
2
+ Name: functualize-lambda
3
+ Version: 0.1.0
4
+ Summary: AWS Lambda adapter plugin for functualize - supports fat and thin Lambda deployment patterns
5
+ Author-email: Mohammad Hakim Adiprasetya <viltohmyst@gmail.com>
6
+ License-Expression: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Typing :: Typed
13
+ Requires-Python: >=3.11
14
+ Requires-Dist: functualize<1.0.0,>=0.1.0
15
+ Provides-Extra: dev
16
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
17
+ Requires-Dist: pytest>=7.4.0; extra == 'dev'
18
+ Description-Content-Type: text/markdown
19
+
20
+ # functualize-lambda
21
+
22
+ > **Status: Published** — Independently installable from PyPI.
23
+
24
+ AWS Lambda adapter plugin for [functualize](https://github.com/raicing-ai/functualize).
25
+
26
+ Supports two deployment patterns:
27
+
28
+ 1. **Fat Lambda** — Single Lambda function with internal routing via `event["job"]`
29
+ 2. **Thin Lambda** — One Lambda per job via `make_handler(job_name)`
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ pip install functualize-lambda
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ ### Fat Lambda (internal routing)
40
+
41
+ ```python
42
+ from functualize.app import FunctualizeApp, JobSources
43
+ from functualize_lambda import LambdaAdapter
44
+
45
+ app = FunctualizeApp("my-app", job_sources=JobSources(functions=[deploy, rollback]))
46
+ adapter = LambdaAdapter()
47
+ adapter(app)
48
+
49
+ def handler(event, context):
50
+ return adapter.run(event, context)
51
+ ```
52
+
53
+ Events should have the shape: `{"job": "job_name", "kwargs": {"key": "value"}}`
54
+
55
+ ### Thin Lambda (per-job handler)
56
+
57
+ ```python
58
+ from functualize.app import FunctualizeApp, JobSources
59
+ from functualize_lambda import LambdaAdapter
60
+
61
+ app = FunctualizeApp("my-app", job_sources=JobSources(functions=[deploy]))
62
+ adapter = LambdaAdapter()
63
+ adapter(app)
64
+
65
+ handler = adapter.make_handler("deploy")
66
+ ```
@@ -0,0 +1,6 @@
1
+ functualize_lambda/__init__.py,sha256=TSn9ClHmglDhZIp4hdSFSG9XtVl2LwYLfKM4ZudLREo,5701
2
+ functualize_lambda/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ functualize_lambda-0.1.0.dist-info/METADATA,sha256=jHSOSlr3i7uRUbzyteecFwWYQfaK1wTF-6ViAF5ohYU,1917
4
+ functualize_lambda-0.1.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
5
+ functualize_lambda-0.1.0.dist-info/entry_points.txt,sha256=bSyXWEPfxQ3wPPDVYr5knz8NMvGwJmWO7BgdkQYZrXU,64
6
+ functualize_lambda-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.31.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [functualize.plugins]
2
+ lambda = functualize_lambda:LambdaAdapter