Skip to content

Commit

Permalink
Allow invoking decorated endpoints from sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
emdoyle committed Jul 23, 2024
1 parent 640d33b commit 0de07ee
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 34 deletions.
24 changes: 15 additions & 9 deletions gauge/cli/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@
import requests
from rich.console import Console

from gauge import settings
from gauge.cli.console import log_error, log_task

API_URL = os.environ.get("GAUGE_API_URL", "http://localhost:8000")


class DeployType(TypedDict):
module: str | None
Expand All @@ -30,8 +29,19 @@ class DeployType(TypedDict):


class DeployHandler:
def __init__(self, file_paths: list[str]) -> None:
def __init__(
self,
file_paths: list[str],
api_url: str | None = None,
client_secret: str | None = None
) -> None:
self.file_paths = {Path(file_path) for file_path in file_paths}
self.deploy_url = (api_url or settings.GAUGE_API_URL) + "/deploy"
self.client_secret = client_secret or settings.CLIENT_SECRET

@property
def headers(self) -> dict[str, str]:
return {"X-Client-Secret": self.client_secret}

def validate_file_paths(self) -> None:
errored = False
Expand Down Expand Up @@ -59,21 +69,17 @@ def bundle(self, temp_dir: str) -> Path:
return zip_path

def upload(self, zip_path: Path, deployments: DeployConfigType) -> None:
gauge_client_id = os.environ.get("GAUGE_CLIENT_ID") or input(
"Input your GAUGE_CLIENT_ID: "
)
with log_task(
start_message="Uploading bundle...", end_message="Bundle uploaded"
):
with open(zip_path, "rb") as zip_file:
files = {"file": zip_file, "json_data": (None, json.dumps(deployments))}
resp = requests.post(
API_URL + "/v0.1/deploy/",
headers={"X-Client-Secret": gauge_client_id},
self.deploy_url,
headers=self.headers,
files=files,
)
if resp.status_code != 200:
print(resp.status_code, resp.content)
log_error("Failed to trigger the deploy")
sys.exit(1)

Expand Down
6 changes: 6 additions & 0 deletions gauge/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class GaugeError(Exception):
...


class GaugeInvokeError(GaugeError):
...
40 changes: 37 additions & 3 deletions gauge/sdk/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,35 @@
from __future__ import annotations
from dataclasses import dataclass, field, asdict
import json

import requests

from typing import Any, Callable

from gauge import settings, errors


@dataclass
class RemoteInvocationArguments:
args: list[Any] = field(default_factory=list)
kwargs: dict[Any, Any] = field(default_factory=dict)


def invoke_endpoint(function_name: str, arguments: RemoteInvocationArguments) -> Any:
try:
response = requests.post(
f"{settings.GAUGE_API_URL}/invoke/{function_name}/",
headers={"X-Client-Secret": settings.CLIENT_SECRET},
json=json.dumps(asdict(arguments))
)
response.raise_for_status()
return response.json()
except requests.HTTPError as e:
raise errors.GaugeInvokeError(f"Function invocation for '{function_name}' failed with status: {e.response.status_code}")
except Exception as e:
raise errors.GaugeInvokeError(f"Could not invoke function: '{function_name}' due to error:\n{e}")



def endpoint(
name: str, python_version: str = "3.12", dependencies: list[str] = []
Expand All @@ -18,21 +46,27 @@ def _gauge_register() -> tuple[str, dict[str, str | list[str]]]:

function._gauge_register = _gauge_register # pyright: ignore[reportFunctionMemberAccess]

def as_lambda_function_url_handler() -> Callable[[Any, Any], Any]:
def _as_lambda_handler() -> Callable[[Any, Any], Any]:
def _lambda_handler(event: Any, context: Any) -> Any:
if not isinstance(event, dict):
return {
"status": 400,
"detail": "Could not parse incoming data. The request body must be JSON.",
}
try:
return {"status": 200, "result": function(**event)}
return {"status": 200, "result": function(*event["args"], **event["kwargs"])}
except Exception as e:
return {"status": 500, "detail": str(e)}

return _lambda_handler

function.as_lambda_function_url_handler = as_lambda_function_url_handler # pyright: ignore[reportFunctionMemberAccess]
function.as_lambda_function_url_handler = _as_lambda_handler # pyright: ignore[reportFunctionMemberAccess]

def _invoke_fn(*args, **kwargs) -> Callable[..., Any]: # type: ignore
return invoke_endpoint(name, RemoteInvocationArguments(args=args, kwargs=kwargs)) # type: ignore

function.invoke = _invoke_fn # pyright: ignore[reportFunctionMemberAccess]

return function

return endpoint_decorator
10 changes: 10 additions & 0 deletions gauge/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# type: ignore
from __future__ import annotations

from environs import Env

env = Env()
env.read_env()

GAUGE_API_URL: str = env.str("GAUGE_API_URL", "http://localhost:8000/v0.1")
CLIENT_SECRET: str = env.str("CLIENT_SECRET", "")
30 changes: 8 additions & 22 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,45 +30,31 @@ dependencies = [
"pydantic~=2.0",
"stdlib-list>=0.10.0; python_version < '3.10'",
"eval-type-backport>=0.2.0; python_version < '3.10'",
"boto3~=1.34.145",
"requests~=2.32.3",

"environs~=11.0",
]
keywords = ['python', 'lambda', 'aws', 'serverless', 'fastapi']
[project.optional-dependencies]
dev = [
# Core deps (pinned)
"pyyaml==6.0.1",
"pydantic==2.8.2; python_version > '3.7'",
"pydantic==2.8.2",
"rich==13.7.1",
"fastapi==0.111.1",
"boto3-stubs-lite==1.34.145",
"boto3-stubs==1.34.145",
"tach==0.8.1",
# Setup
"pip==24.0",
# Code Quality
"pyright==1.1.372",
"ruff==0.5.2",
# Build/Release
"setuptools==69.5.1; python_version > '3.7'",
"twine==5.1.1; python_version > '3.7'",
"build==1.2.1; python_version > '3.7'",
"setuptools==69.5.1",
"twine==5.1.1",
"build==1.2.1",
# Tests
"pytest==8.2.2; python_version > '3.7'",
"pytest-mock==3.14.0; python_version > '3.7'",
"coverage==7.6.0; python_version > '3.7'",
# python version 3.7 pinned dependencies
"botocore==1.33.13; python_version == '3.7'",
"boto3-stubs-lite==1.33.13; python_version == '3.7'",
"boto3-stubs==1.33.13; python_version == '3.7'",
"pydantic==2.5.3; python_version == '3.7'",
"setuptools==47.1.0; python_version == '3.7'",
"twine==4.0.2; python_version == '3.7'",
"build==1.1.1; python_version == '3.7'",
"pytest==7.4.4; python_version == '3.7'",
"pytest-mock==3.11.1; python_version == '3.7'",
"coverage==7.2.7; python_version == '3.7'",
"pytest==8.2.2",
"pytest-mock==3.14.0",
"coverage==7.6.0",
]


Expand Down

0 comments on commit 0de07ee

Please sign in to comment.