Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mesop/features/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from .page import page as page
from .serve import serve as serve
17 changes: 17 additions & 0 deletions mesop/features/serve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from typing import Any, Callable

from mesop.runtime import runtime


def serve(
*,
rule: str,
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
runtime().register_handler(
rule=rule,
handler=func,
)
return func

return decorator
23 changes: 22 additions & 1 deletion mesop/runtime/runtime.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from copy import deepcopy
from dataclasses import dataclass
from typing import Any, Callable, Generator, Type, TypeVar, cast
from typing import Any, Callable, Dict, Generator, Tuple, Type, TypeVar, cast

from flask import g, request

Expand Down Expand Up @@ -49,6 +49,9 @@ def __init__(self):
# clients polling whether to request a hot reload.
self.hot_reload_counter = 0
self._path_to_page_config: dict[str, PageConfig] = {}
self._rule_to_handler: dict[
str, Callable[[], Tuple[str, int, Dict[str, str]]]
] = {}
self.component_fns: set[Callable[..., Any]] = set()
self.event_mappers: dict[Type[Any], Callable[[pb.UserEvent, Key], Any]] = {}
self._state_classes: list[type[Any]] = []
Expand Down Expand Up @@ -131,9 +134,27 @@ def register_page(self, *, path: str, page_config: PageConfig) -> None:
)
self._path_to_page_config[path] = page_config

def register_handler(
self, *, rule: str, handler: Callable[[], Tuple[str, int, Dict[str, str]]]
):
if self._has_served_traffic:
raise MesopDeveloperException(
"Cannot register a handler after traffic has been served. You must register all handlers upon server startup before any traffic has been served. This prevents security issues."
)
if rule in self._rule_to_handler:
raise MesopDeveloperException(
f"Handler for rule '{rule}' already registered."
)
self._rule_to_handler[rule] = handler

def get_page_config(self, *, path: str) -> PageConfig | None:
return self._path_to_page_config.get(path)

def get_rule_to_handlers(
self,
) -> dict[str, Callable[[], Tuple[str, int, Dict[str, str]]]]:
return deepcopy(self._rule_to_handler)

def get_path_to_page_configs(self) -> dict[str, PageConfig]:
return deepcopy(self._path_to_page_config)

Expand Down
4 changes: 4 additions & 0 deletions mesop/server/static_file_serving.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ def configure_static_file_serving(
disable_gzip_cache: bool = False,
default_allowed_iframe_parents: str = "'self'",
):
# Register @me.serve handlers.
for rule, handler_func in runtime().get_rule_to_handlers().items():
app.add_url_rule(rule, view_func=handler_func)

def get_path(path: str):
safe_path = safe_join(static_file_runfiles_base, path)
assert safe_path
Expand Down
Loading