-
Notifications
You must be signed in to change notification settings - Fork 3
feat: add dev command #83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
7e2ee3f
59e63a0
d36af4f
27f6301
dff7582
aa7687e
c2f62d2
6326155
d273c2e
ffb3dd6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,8 @@ | |
import click | ||
from scaleway import ScalewayException | ||
|
||
from scw_serverless import deployment, loader, logger | ||
import scw_serverless | ||
from scw_serverless import app, deployment, loader, local_app, logger | ||
from scw_serverless.dependencies_manager import DependenciesManager | ||
from scw_serverless.gateway.gateway_manager import GatewayManager | ||
|
||
|
@@ -145,3 +146,31 @@ def deploy( | |
sdk_client=client, | ||
) | ||
manager.update_routes() | ||
|
||
|
||
@cli.command() | ||
@CLICK_ARG_FILE | ||
@click.option( | ||
"--port", | ||
"-p", | ||
"port", | ||
default=8080, | ||
show_default=True, | ||
help="Set port to listen on.", | ||
) | ||
@click.option( | ||
"--debug", | ||
"-d", | ||
"debug", | ||
default=True, | ||
show_default=True, | ||
help="Run Flask in debug mode.", | ||
) | ||
def dev(file: Path, port: int, debug: bool) -> None: | ||
"""Run functions locally with Serverless Local Testing.""" | ||
app.Serverless = local_app.ServerlessLocal | ||
scw_serverless.Serverless = local_app.ServerlessLocal | ||
Comment on lines
+170
to
+171
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is of course very hacky but it's hard to find a better way as:
My first approach was to add a handler parameter to the functions which would store the python handler as an object. This doesn't work well with multiprocessing (can't pickle a Python function) and I don't like it (may introduce side effects when deploying). Ideally, local testing should not modify the deploy command. The second approach could have been a global Python variable So finally we override the class directly with a derived class. This is not ideal but it's quite clean compared to those alternatives. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm yes, a bit of black magic but it works for now 🧙 |
||
app_instance = cast( | ||
local_app.ServerlessLocal, loader.load_app_instance(file.resolve()) | ||
) | ||
app_instance.local_server.serve(port=port, debug=debug) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
from typing import Any, Callable | ||
|
||
from scaleway_functions_python import local | ||
|
||
from scw_serverless.app import Serverless | ||
from scw_serverless.config.function import FunctionKwargs | ||
|
||
try: | ||
from typing import Unpack | ||
except ImportError: | ||
from typing_extensions import Unpack | ||
# pylint: disable=wrong-import-position # Conditional import considered a statement | ||
|
||
|
||
class ServerlessLocal(Serverless): | ||
"""Serverless class that is used when testing locally. | ||
|
||
Crate a local testing framework server and inject the handlers to it. | ||
""" | ||
|
||
def __init__( | ||
self, | ||
service_name: str, | ||
env: dict[str, Any] | None = None, | ||
secret: dict[str, Any] | None = None, | ||
): | ||
super().__init__(service_name, env, secret) | ||
self.local_server = local.LocalFunctionServer() | ||
|
||
def func( | ||
self, | ||
**kwargs: "Unpack[FunctionKwargs]", | ||
) -> Callable: | ||
decorator = super().func(**kwargs) | ||
|
||
def _decorator(handler: Callable): | ||
decorator(handler) | ||
http_methods = None | ||
if methods := kwargs.get("http_methods"): | ||
http_methods = [method.value for method in methods] | ||
self.local_server.add_handler( | ||
handler=handler, | ||
relative_url=kwargs.get("relative_url"), | ||
http_methods=http_methods, | ||
) | ||
|
||
return _decorator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this deserves its own subsection, it's really cool!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks☺️ ! So happy to see both projects being combined 🎉