|
12 | 12 |
|
13 | 13 |
|
14 | 14 | class JupyterDash(dash.Dash): |
| 15 | + """A Dash subclass for developing Dash apps interactively in Jupyter. |
| 16 | +
|
| 17 | + :param server_url: The base URL that the app will be served at, from the |
| 18 | + perspective of the client. If not specified, will default to the host argument |
| 19 | + passed to the ``run_server`` method. |
| 20 | +
|
| 21 | + See parent docstring for additional parameters |
| 22 | + """ |
| 23 | + default_mode = 'external' |
| 24 | + default_requests_pathname_prefix = None |
| 25 | + default_server_url = None |
| 26 | + |
15 | 27 | @classmethod |
16 | 28 | def infer_jupyter_config(cls): |
| 29 | + """ |
| 30 | + Infer the current Jupyter server configuration. This will detect |
| 31 | + the proper request_pathname_prefix and server_url values to use when |
| 32 | + displaying Dash apps. When the jupyter_server_proxy Python package is |
| 33 | + installed, all Dash requests will be routed through the proxy. |
| 34 | +
|
| 35 | + Requirements: |
| 36 | +
|
| 37 | + In the classic notebook, this method requires the `jupyter_dash` nbextension |
| 38 | + which should be installed automatically with the installation of the |
| 39 | + jupyter-dash Python package. You can see what notebook extensions are installed |
| 40 | + by running the following command: |
| 41 | + $ jupyter nbextension list |
| 42 | +
|
| 43 | + In JupyterLab, this method requires the `jupyterlab-dash` labextension. This |
| 44 | + extension should be installed automatically with the installation of the |
| 45 | + jupyter-dash Python package, but JupyterLab must be allowed to rebuild before |
| 46 | + the extension is activated (JupyterLab should automatically detect the |
| 47 | + extension and produce a popup dialog asking for permission to rebuild). You can |
| 48 | + see what JupyterLab extensions are installed by running the following command: |
| 49 | + $ jupyter labextension list |
| 50 | + """ |
17 | 51 | _request_jupyter_config() |
18 | 52 |
|
19 | 53 | def __init__(self, server_url=None, **kwargs): |
20 | | - |
21 | | - # Gather default jupyter properties |
22 | | - self.default_server_url = None |
23 | | - self.default_requests_pathname_prefix = None |
24 | | - self.default_mode = 'external' |
25 | | - |
| 54 | + """""" |
26 | 55 | # See if jupyter_server_proxy is installed |
27 | 56 | try: |
28 | 57 | import jupyter_server_proxy |
29 | 58 | self._server_proxy = True |
30 | 59 | except Exception: |
31 | 60 | self._server_proxy = False |
32 | 61 |
|
33 | | - if 'base_subpath' in _jupyter_config and self._server_proxy: |
34 | | - self.default_requests_pathname_prefix = ( |
| 62 | + if ('base_subpath' in _jupyter_config and self._server_proxy and |
| 63 | + JupyterDash.default_requests_pathname_prefix is None): |
| 64 | + JupyterDash.default_requests_pathname_prefix = ( |
35 | 65 | _jupyter_config['base_subpath'].rstrip('/') + '/proxy/{port}/' |
36 | 66 | ) |
37 | 67 |
|
38 | | - if 'server_url' in _jupyter_config and self._server_proxy: |
39 | | - self.default_server_url = _jupyter_config['server_url'] |
40 | | - |
41 | | - if 'frontend' in _jupyter_config: |
42 | | - if _jupyter_config['frontend'] == 'jupyterlab': |
43 | | - self.default_mode = 'jupyterlab' |
44 | | - else: |
45 | | - self.default_mode = 'external' |
| 68 | + if ('server_url' in _jupyter_config and self._server_proxy and |
| 69 | + JupyterDash.default_server_url is None): |
| 70 | + JupyterDash.default_server_url = _jupyter_config['server_url'] |
46 | 71 |
|
47 | 72 | self._input_pathname_prefix = kwargs.get('requests_pathname_prefix', None) |
48 | 73 |
|
@@ -77,15 +102,35 @@ def alive(): |
77 | 102 | def run_server( |
78 | 103 | self, |
79 | 104 | mode=None, width=800, height=650, |
80 | | - host=os.getenv("HOST", "127.0.0.1"), |
81 | | - port=os.getenv("PORT", "8050"), |
82 | 105 | **kwargs |
83 | 106 | ): |
| 107 | + """ |
| 108 | + Serve the app using flask in a background thread. You should not run this on a |
| 109 | + production server, use gunicorn/waitress instead. |
| 110 | +
|
| 111 | + :param mode: Display mode. One of: |
| 112 | + ``"external"``: The URL of the app will be displayed in the notebook |
| 113 | + output cell. Clicking this URL will open the app in the default |
| 114 | + web browser. |
| 115 | + ``"inline"``: The app will be displayed inline in the notebook output cell |
| 116 | + in an iframe. |
| 117 | + ``"jupyterlab"``: The app will be displayed in a dedicate tab in the |
| 118 | + JupyterLab interface. Requires JupyterLab and the `jupyterlab-dash` |
| 119 | + extension. |
| 120 | + :param width: Width of app when displayed using mode="inline" |
| 121 | + :param height: Height of app when displayed using mode="inline" |
| 122 | + :param kwargs: Additional keyword arguments to pass to the superclass |
| 123 | + ``Dash.run_server`` method. |
| 124 | + """ |
| 125 | + # Get host and port |
| 126 | + host = kwargs.get("host", os.getenv("HOST", "127.0.0.1")) |
| 127 | + port = kwargs.get("port", os.getenv("PORT", "8050")) |
| 128 | + |
84 | 129 | # Validate / infer display mode |
85 | 130 | valid_display_values = ["jupyterlab", "inline", "external"] |
86 | 131 |
|
87 | 132 | if mode is None: |
88 | | - mode = self.default_mode |
| 133 | + mode = JupyterDash.default_mode |
89 | 134 | elif not isinstance(mode, str): |
90 | 135 | raise ValueError( |
91 | 136 | "The mode argument must be a string\n" |
@@ -122,8 +167,8 @@ def run_server( |
122 | 167 |
|
123 | 168 | # Compute server_url url |
124 | 169 | if self.server_url is None: |
125 | | - if self.default_server_url: |
126 | | - server_url = self.default_server_url.rstrip('/') |
| 170 | + if JupyterDash.default_server_url: |
| 171 | + server_url = JupyterDash.default_server_url.rstrip('/') |
127 | 172 | else: |
128 | 173 | server_url = f'http://{host}:{port}' |
129 | 174 | else: |
|
0 commit comments