@intelligems/sst 2.49.6-ig.2 → 2.49.6-ig.4

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.
@@ -108,6 +108,15 @@ while (true) {
108
108
  method: "GET",
109
109
  headers: {}
110
110
  });
111
+ const sstFunctionId = result.headers["lambda-runtime-sst-function-id"];
112
+ if (sstFunctionId) {
113
+ process.env.SST_FUNCTION_ID = sstFunctionId;
114
+ }
115
+ const parsed = JSON.parse(result.body);
116
+ const invocationEnv = parsed.env;
117
+ if (invocationEnv && typeof invocationEnv === "object") {
118
+ Object.assign(process.env, invocationEnv);
119
+ }
111
120
  context = {
112
121
  awsRequestId: result.headers["lambda-runtime-aws-request-id"],
113
122
  invokedFunctionArn: result.headers["lambda-runtime-invoked-function-arn"],
@@ -150,7 +159,7 @@ while (true) {
150
159
  );
151
160
  }
152
161
  };
153
- request = JSON.parse(result.body);
162
+ request = parsed.event;
154
163
  } catch {
155
164
  continue;
156
165
  }
@@ -4,8 +4,9 @@ import json
4
4
  import logging
5
5
  import argparse
6
6
  import traceback
7
- from urllib import request, parse
8
- from time import strftime, time
7
+ import signal
8
+ from urllib import request
9
+ from time import time
9
10
  from importlib import import_module
10
11
 
11
12
  class Identity(object):
@@ -22,15 +23,15 @@ class ClientContext(object):
22
23
 
23
24
  class Context(object):
24
25
  def __init__(self, invoked_function_arn, aws_request_id, deadline_ms, identity, client_context, log_group_name, log_stream_name):
25
- self.function_name = os.environ['AWS_LAMBDA_FUNCTION_NAME']
26
+ self.function_name = os.environ.get('AWS_LAMBDA_FUNCTION_NAME', 'local')
26
27
  self.invoked_function_arn = invoked_function_arn
27
28
  self.aws_request_id = aws_request_id
28
- self.memory_limit_in_mb = os.environ['AWS_LAMBDA_FUNCTION_MEMORY_SIZE']
29
+ self.memory_limit_in_mb = os.environ.get('AWS_LAMBDA_FUNCTION_MEMORY_SIZE', '128')
29
30
  self.deadline_ms = deadline_ms
30
31
  # If identity is null, we want to mimick AWS behavior and return an object with None values
31
- self.identity = Identity(**json.loads(identity)) if identity != 'null' else Identity(cognito_identity_id = None, cognito_identity_pool_id = None)
32
+ self.identity = Identity(**json.loads(identity)) if identity and identity != 'null' else Identity(cognito_identity_id=None, cognito_identity_pool_id=None)
32
33
  # If client_context is null, we want to mimick AWS behavior and return None
33
- self.client_context = ClientContext(**json.loads(client_context)) if client_context != 'null' else None
34
+ self.client_context = ClientContext(**json.loads(client_context)) if client_context and client_context != 'null' else None
34
35
  self.log_group_name = log_group_name
35
36
  self.log_stream_name = log_stream_name
36
37
 
@@ -42,15 +43,17 @@ class Context(object):
42
43
 
43
44
 
44
45
  def handleUnserializable(obj):
45
- print(
46
- "Unserializable {}: {} when returning result {!r}".format(
47
- type(obj), repr(obj), result
48
- )
49
- )
50
-
51
46
  raise TypeError("Unserializable {}: {!r}".format(type(obj), obj))
52
47
 
53
48
 
49
+ # Idle timeout handler
50
+ class IdleTimeoutError(Exception):
51
+ pass
52
+
53
+ def idle_timeout_handler(signum, frame):
54
+ raise IdleTimeoutError("Worker idle timeout")
55
+
56
+
54
57
  logging.basicConfig()
55
58
 
56
59
  parser = argparse.ArgumentParser(
@@ -64,64 +67,132 @@ parser.add_argument('handler_module',
64
67
  parser.add_argument('src_path', help='SrcPath of the handler function')
65
68
  parser.add_argument('handler_name', help='Name of the handler function')
66
69
 
70
+ # Idle timeout in seconds (15 minutes, matching Node.js runtime)
71
+ IDLE_TIMEOUT = 15 * 60
72
+
67
73
  if __name__ == '__main__':
68
74
  args = parser.parse_args()
69
75
 
70
76
  # this is needed because you need to import from where you've executed sst
71
77
  sys.path.append('.')
72
78
 
73
- # fetch request
74
- url = "http://{}/2018-06-01/runtime/invocation/next".format(os.environ['AWS_LAMBDA_RUNTIME_API'])
75
- r = request.urlopen(url)
76
- event = json.loads(r.read())
77
- context = Context(
78
- r.getheader('Lambda-Runtime-Invoked-Function-Arn'),
79
- r.getheader('Lambda-Runtime-Aws-Request-Id'),
80
- r.getheader('Lambda-Runtime-Deadline-Ms'),
81
- r.getheader('Lambda-Runtime-Cognito-Identity'),
82
- r.getheader('Lambda-Runtime-Client-Context'),
83
- r.getheader('Lambda-Runtime-Log-Group-Name'),
84
- r.getheader('Lambda-Runtime-Log-Stream-Name')
85
- )
86
-
87
- # invoke handler
88
- has_error = False
89
- try:
90
- # set the sys.path to the src_path. Other wise importing a local file
91
- # would fail with error ModuleNotFoundError
92
- sys.path.append(args.src_path)
79
+ # set the sys.path to the src_path. Otherwise importing a local file
80
+ # would fail with error ModuleNotFoundError
81
+ sys.path.append(args.src_path)
93
82
 
83
+ # Import handler module once at startup (warm start optimization)
84
+ handler = None
85
+ try:
94
86
  # remove leading zeros for relative imports
95
87
  if args.handler_module.startswith('.'):
96
- module = import_module(args.handler_module[1:])
97
- else:
98
- module = import_module(args.handler_module)
88
+ module = import_module(args.handler_module[1:])
89
+ else:
90
+ module = import_module(args.handler_module)
99
91
 
100
92
  handler = getattr(module, args.handler_name)
101
- result = handler(event, context)
102
- data = json.dumps(result, default=handleUnserializable).encode("utf-8")
103
-
104
93
  except Exception as e:
105
- has_error = True
106
- # print error in bootstrap because we won't be able to print the Python
107
- # stack trace in the correct format in NodeJS
94
+ # Report init error and exit
108
95
  traceback.print_exc()
109
- # build error response
110
- ex_type, ex_value, ex_traceback = sys.exc_info()
111
- result = {
112
- "errorType": ex_type.__name__,
113
- "errorMessage": str(ex_value),
114
- "trace": traceback.format_tb(ex_traceback),
115
- }
116
- data = json.dumps(result).encode("utf-8")
117
-
118
- # send response
119
- if has_error == False:
120
- url_destination = '/response'
121
- else:
122
- url_destination = '/error'
123
- url = "http://{}/2018-06-01/runtime/invocation/{}{}".format(os.environ['AWS_LAMBDA_RUNTIME_API'], context.aws_request_id, url_destination)
124
- req = request.Request(url, method="POST", data=data)
125
- req.add_header('Content-Type', 'application/json')
126
- r = request.urlopen(req, data=data)
127
-
96
+ try:
97
+ init_error_url = "http://{}/2018-06-01/runtime/init/error".format(
98
+ os.environ['AWS_LAMBDA_RUNTIME_API']
99
+ )
100
+ ex_type, ex_value, ex_traceback = sys.exc_info()
101
+ error_data = json.dumps({
102
+ "errorType": ex_type.__name__ if ex_type else "ImportError",
103
+ "errorMessage": str(ex_value),
104
+ "trace": traceback.format_tb(ex_traceback) if ex_traceback else [],
105
+ }).encode("utf-8")
106
+ req = request.Request(init_error_url, method="POST", data=error_data)
107
+ req.add_header('Content-Type', 'application/json')
108
+ request.urlopen(req)
109
+ except Exception:
110
+ pass
111
+ sys.exit(1)
112
+
113
+ # Main event loop - handle multiple invocations
114
+ while True:
115
+ context = None
116
+
117
+ try:
118
+ # Set up idle timeout using SIGALRM (Unix only)
119
+ if hasattr(signal, 'SIGALRM'):
120
+ signal.signal(signal.SIGALRM, idle_timeout_handler)
121
+ signal.alarm(IDLE_TIMEOUT)
122
+
123
+ # Fetch next invocation (blocks until one is available)
124
+ next_url = "http://{}/2018-06-01/runtime/invocation/next".format(
125
+ os.environ['AWS_LAMBDA_RUNTIME_API']
126
+ )
127
+ r = request.urlopen(next_url)
128
+
129
+ # Cancel idle timeout once we have work
130
+ if hasattr(signal, 'SIGALRM'):
131
+ signal.alarm(0)
132
+
133
+ event = json.loads(r.read())
134
+ context = Context(
135
+ r.getheader('Lambda-Runtime-Invoked-Function-Arn'),
136
+ r.getheader('Lambda-Runtime-Aws-Request-Id'),
137
+ r.getheader('Lambda-Runtime-Deadline-Ms'),
138
+ r.getheader('Lambda-Runtime-Cognito-Identity'),
139
+ r.getheader('Lambda-Runtime-Client-Context'),
140
+ r.getheader('Lambda-Runtime-Log-Group-Name'),
141
+ r.getheader('Lambda-Runtime-Log-Stream-Name')
142
+ )
143
+
144
+ # Invoke handler
145
+ try:
146
+ result = handler(event, context)
147
+ data = json.dumps(result, default=handleUnserializable).encode("utf-8")
148
+ url_destination = '/response'
149
+ except Exception as e:
150
+ # Handler error - report but keep worker alive
151
+ traceback.print_exc()
152
+ ex_type, ex_value, ex_traceback = sys.exc_info()
153
+ error_result = {
154
+ "errorType": ex_type.__name__ if ex_type else "Error",
155
+ "errorMessage": str(ex_value),
156
+ "trace": traceback.format_tb(ex_traceback) if ex_traceback else [],
157
+ }
158
+ data = json.dumps(error_result).encode("utf-8")
159
+ url_destination = '/error'
160
+
161
+ # Send response
162
+ response_url = "http://{}/2018-06-01/runtime/invocation/{}{}".format(
163
+ os.environ['AWS_LAMBDA_RUNTIME_API'],
164
+ context.aws_request_id,
165
+ url_destination
166
+ )
167
+ req = request.Request(response_url, method="POST", data=data)
168
+ req.add_header('Content-Type', 'application/json')
169
+
170
+ # Retry sending response (matching Node.js behavior)
171
+ max_retries = 3
172
+ for attempt in range(max_retries):
173
+ try:
174
+ request.urlopen(req)
175
+ break
176
+ except Exception as e:
177
+ if attempt < max_retries - 1:
178
+ import time as time_module
179
+ time_module.sleep(0.5)
180
+ else:
181
+ print(f"Failed to send response after {max_retries} attempts: {e}", file=sys.stderr)
182
+
183
+ except IdleTimeoutError:
184
+ # Idle timeout - exit gracefully
185
+ print("Worker idle timeout, exiting", file=sys.stderr)
186
+ sys.exit(0)
187
+
188
+ except KeyboardInterrupt:
189
+ # Graceful shutdown
190
+ print("Worker interrupted, exiting", file=sys.stderr)
191
+ sys.exit(0)
192
+
193
+ except Exception as e:
194
+ # Unexpected error in the runtime loop itself
195
+ print(f"Runtime error: {e}", file=sys.stderr)
196
+ traceback.print_exc()
197
+ # Continue to next iteration - don't crash the worker
198
+ continue