Skip to content
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

Add startup route setup #2832

Merged
merged 10 commits into from Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).

## Added

- [#2832](https://github.com/plotly/dash/pull/2832) Add dash startup route setup on Dash init.
- [#2819](https://github.com/plotly/dash/pull/2819) Add dash subcomponents receive additional parameters passed by the parent component. Fixes [#2814](https://github.com/plotly/dash/issues/2814).
- [#2826](https://github.com/plotly/dash/pull/2826) When using Pages, allows for `app.title` and (new) `app.description` to be used as defaults for the page title and description. Fixes [#2811](https://github.com/plotly/dash/issues/2811).
- [#2795](https://github.com/plotly/dash/pull/2795) Allow list of components to be passed as layout.
Expand Down
34 changes: 34 additions & 0 deletions dash/dash.py
Expand Up @@ -372,6 +372,7 @@ class Dash:
"""

_plotlyjs_url: str
STARTUP_ROUTES: list = []

def __init__( # pylint: disable=too-many-statements
self,
Expand Down Expand Up @@ -556,6 +557,7 @@ def __init__( # pylint: disable=too-many-statements
"JupyterDash is deprecated, use Dash instead.\n"
"See https://dash.plotly.com/dash-in-jupyter for more details."
)
self.setup_startup_routes()

def init_app(self, app=None, **kwargs):
"""Initialize the parts of Dash that require a flask app."""
Expand Down Expand Up @@ -1626,6 +1628,38 @@ def display_content(path):
self.config.requests_pathname_prefix, path
)

@staticmethod
def add_startup_route(name, view_func, methods):
"""
Add a route to the app to be initialized at the end of Dash initialization.
Use this if the package requires a route to be added to the app, and you will not need to worry about at what point to add it.

:param name: The name of the route. eg "my-new-url/path".
:param view_func: The function to call when the route is requested. The function should return a JSON serializable object.
:param methods: The HTTP methods that the route should respond to. eg ["GET", "POST"] or either one.
"""
if not isinstance(name, str) or name.startswith("/"):
raise ValueError("name must be a string and should not start with '/'")

if not callable(view_func):
raise ValueError("view_func must be callable")

valid_methods = {"POST", "GET"}
if not set(methods).issubset(valid_methods):
raise ValueError(f"methods should only contain {valid_methods}")

if any(route[0] == name for route in Dash.STARTUP_ROUTES):
raise ValueError(f"Route name '{name}' is already in use.")

Dash.STARTUP_ROUTES.append((name, view_func, methods))

def setup_startup_routes(self):
"""
Initialize the startup routes stored in STARTUP_ROUTES.
"""
for _name, _view_func, _methods in self.STARTUP_ROUTES:
self._add_url(f"_dash_startup_route/{_name}", _view_func, _methods)
danton267 marked this conversation as resolved.
Show resolved Hide resolved

def _setup_dev_tools(self, **kwargs):
debug = kwargs.get("debug", False)
dev_tools = self._dev_tools = AttributeDict()
Expand Down
19 changes: 19 additions & 0 deletions tests/integration/test_integration.py
Expand Up @@ -511,3 +511,22 @@ def on_nested_click(n_clicks):

dash_duo.wait_for_element("#nested").click()
dash_duo.wait_for_text_to_equal("#nested-output", "Clicked 1 times")


def test_inin030_add_startup_route(dash_duo):
url = "my-new-route"
def my_route_f():
return "hello"
Dash.add_startup_route(url, my_route_f, ["POST"])

import requests

app = Dash(__name__)
Dash.STARTUP_ROUTES = []
app.layout = html.Div("Hello World")
dash_duo.start_server(app)

url = f"{dash_duo.server_url}{app.config.requests_pathname_prefix}_dash_startup_route/{url}"
response = requests.post(url)
assert response.status_code == 200
assert response.text == "hello"
danton267 marked this conversation as resolved.
Show resolved Hide resolved