diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 45120a46f..c05a0c707 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -1,6 +1,6 @@ name: Bug report description: Something is not working correctly -labels: "Status: Needs triage :mag:, Issue: Bug Report :bug:" +labels: "Needs triage :mag:, Bug Report :bug:" body: - type: markdown diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml index a310531f3..ec145b11f 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -1,6 +1,6 @@ name: Feature request description: Suggest an idea for this project -labels: "Status: Needs triage :mag:, Issue: Feature Request :nerd_face:" +labels: "Needs triage :mag:, Feature Request :nerd_face:" body: - type: markdown diff --git a/.github/ISSUE_TEMPLATE/general-question.yml b/.github/ISSUE_TEMPLATE/general-question.yml index 29e6a76ba..3f7baee39 100644 --- a/.github/ISSUE_TEMPLATE/general-question.yml +++ b/.github/ISSUE_TEMPLATE/general-question.yml @@ -1,6 +1,6 @@ name: General question description: General questions around Vizro -labels: "Status: Needs triage :mag:, Issue: General Question :question:" +labels: "Needs triage :mag:, General Question :question:" body: - type: markdown diff --git a/.github/labeler.yml b/.github/labeler.yml index e56592345..88111bc0c 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,3 +1,3 @@ -"Package: Vizro-AI :robot:": +"Vizro-AI :robot:": - changed-files: - any-glob-to-any-file: ["vizro-ai/**/*"] diff --git a/tools/check_branch_name.sh b/tools/check_branch_name.sh index 33ff6bcec..561b34d29 100755 --- a/tools/check_branch_name.sh +++ b/tools/check_branch_name.sh @@ -2,7 +2,7 @@ BRANCH_LOCAL=$(git symbolic-ref --short HEAD) BRANCH_CI=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}} -REGEX="^(main|(release|feat|bug|docs|qa|dev|demo|ci|tidy|dependabot)\/[^/]+(/[^/]+)*)$" +REGEX="^(main|(release|feat|bug|docs|qa|dev|demo|ci|tidy|dependabot|poc)\/[^/]+(/[^/]+)*)$" if ! [[ $BRANCH_LOCAL =~ $REGEX ]] && [[ $BRANCH_CI =~ $REGEX ]]; then echo "Branch name is invalid - please rename your branch following this regex syntax: $REGEX" diff --git a/vizro-core/changelog.d/20240625_114610_antony.milne_make_vizro_callable.md b/vizro-core/changelog.d/20240625_114610_antony.milne_make_vizro_callable.md new file mode 100644 index 000000000..e35846d9f --- /dev/null +++ b/vizro-core/changelog.d/20240625_114610_antony.milne_make_vizro_callable.md @@ -0,0 +1,47 @@ + + + + + +### Added + +- Vizro app itself implements WSGI interface as a shortcut to `app.dash.server`. ([#580](https://github.com/mckinsey/vizro/pull/580)) + + + + + diff --git a/vizro-core/docs/pages/user-guides/run.md b/vizro-core/docs/pages/user-guides/run.md index 11f2236c1..81ee62c9a 100644 --- a/vizro-core/docs/pages/user-guides/run.md +++ b/vizro-core/docs/pages/user-guides/run.md @@ -105,19 +105,18 @@ The dashboard application can be launched in a Jupyter environment in `inline`, ) dashboard = vm.Dashboard(pages=[page]) - app = Vizro().build(dashboard) - server = app.dash.server # (1)! + app = Vizro().build(dashboard) # (1)! if __name__ == "__main__": # (2)! app.run() ``` - 1. Expose the underlying Flask app through `app.dash.server`; this will be used by Gunicorn. + 1. The Vizro `app` object is a WSGI application that exposes the underlying Flask app; this will be used by Gunicorn. 2. Enable the same app to still be run using the built-in Flask server with `python app.py` for development purposes. To run using Gunicorn with four worker processes, execute ```bash -gunicorn app:server --workers 4 +gunicorn app:app --workers 4 ``` in the command line. For more Gunicorn configuration options, refer to [Gunicorn documentation](https://docs.gunicorn.org/). @@ -132,6 +131,6 @@ A Vizro app wraps a Dash app, which itself wraps a Flask app. Hence to deploy a - [Flask deployment documentation](https://flask.palletsprojects.com/en/2.0.x/deploying/) - [Dash deployment documentation](https://dash.plotly.com/deployment) -In particular, `app = Vizro()` exposes the Flask app through `app.dash.server`. As in the [above example with Gunicorn](#gunicorn), this provides the application instance to a [WSGI](https://werkzeug.palletsprojects.com/en/3.0.x/terms/#wsgi) server. +Internally, `app = Vizro()` contains a Flask app in `app.dash.server`. However, as a convenience, the Vizro `app` itself implements the [WSGI application interface](https://werkzeug.palletsprojects.com/en/3.0.x/terms/#wsgi) as a shortcut to the underlying Flask app. This means that, as in the [above example with Gunicorn](#gunicorn), the Vizro `app` object can be directly supplied to the WSGI server. [`Vizro`][vizro.Vizro] accepts `**kwargs` that are passed through to `Dash`. This enables you to configure the underlying Dash app using the same [arguments that are available](https://dash.plotly.com/reference#dash.dash) in `Dash`. For example, in a deployment context, you might like to specify a custom `url_base_pathname` to serve your Vizro app at a specific URL rather than at your domain root. diff --git a/vizro-core/src/vizro/_vizro.py b/vizro-core/src/vizro/_vizro.py index 890f0674a..78f20f378 100644 --- a/vizro-core/src/vizro/_vizro.py +++ b/vizro-core/src/vizro/_vizro.py @@ -1,7 +1,9 @@ +from __future__ import annotations + import logging import warnings from pathlib import Path -from typing import List +from typing import TYPE_CHECKING, Iterable, List import dash import flask @@ -13,6 +15,10 @@ logger = logging.getLogger(__name__) +if TYPE_CHECKING: + # These are built into wsgiref.types for Python 3.11 onwards. + from _typeshed.wsgi import StartResponse, WSGIEnvironment + class Vizro: """The main class of the `vizro` package.""" @@ -118,6 +124,13 @@ def _pre_build(): if hasattr(model, "pre_build"): model.pre_build() + def __call__(self, environ: WSGIEnvironment, start_response: StartResponse) -> Iterable[bytes]: + """Implements WSGI application interface. + + This means you can do e.g. gunicorn app:app without needing to manually define server = app.dash.server. + """ + return self.dash.server(environ, start_response) + @staticmethod def _reset(): """Private method that clears all state in the `Vizro` app.