#!/usr/bin/env python3 # Изменено: 2026-03-08 # HTTP-обёртка для serverless функций на Python 3.11. # Загружает модуль из SLESS_ENTRYPOINT или handler.py по умолчанию. # Формат SLESS_ENTRYPOINT: "module_name.func_name" (например: handler.handle) # Почему importlib: нужно загружать модуль из фиксированного пути вне sys.path. import sys import os import json import importlib.util from http.server import HTTPServer, BaseHTTPRequestHandler PORT = 8080 # Разбираем SLESS_ENTRYPOINT="module.func" → файл и имя функции. # Fallback: handler.py + handle — для обратной совместимости. _entrypoint = os.environ.get("SLESS_ENTRYPOINT", "handler.handle") _dot_idx = _entrypoint.rfind(".") _module_name = _entrypoint[:_dot_idx] if _dot_idx >= 0 else _entrypoint _func_name = _entrypoint[_dot_idx + 1:] if _dot_idx >= 0 else "handle" HANDLER_PATH = f"/app/function/{_module_name}.py" def load_handler(): # Загружаем модуль пользователя динамически — путь известен только в runtime. spec = importlib.util.spec_from_file_location(_module_name, HANDLER_PATH) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) if not hasattr(module, _func_name): raise AttributeError(f"{HANDLER_PATH} must define a '{_func_name}(event)' function") return getattr(module, _func_name) _handle = load_handler() class FunctionHandler(BaseHTTPRequestHandler): def log_message(self, format, *args): # Логи в stderr чтобы не мешать stdout sys.stderr.write("%s - [%s] %s\n" % ( self.address_string(), self.log_date_time_string(), format % args, )) def do_GET(self): if self.path == "/health": self._respond(200, {"status": "ok"}) else: self._respond(200, _handle({})) def do_POST(self): length = int(self.headers.get("Content-Length", 0)) body = self.rfile.read(length) try: event = json.loads(body) if body else {} except json.JSONDecodeError: # Если тело не JSON — передаём как строку, не ломаем вызов event = {"body": body.decode("utf-8", errors="replace")} result = _handle(event) self._respond(200, result) def _respond(self, status, data): body = json.dumps(data).encode("utf-8") self.send_response(status) self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) if __name__ == "__main__": server = HTTPServer(("0.0.0.0", PORT), FunctionHandler) print(f"sless runtime (python3.11) listening on :{PORT}", flush=True) server.serve_forever()