16
16
import base64
17
17
import traceback
18
18
from urllib .parse import urlparse
19
+ from typing import Union
19
20
20
21
import flask
21
22
52
53
to_json ,
53
54
convert_to_AttributeDict ,
54
55
gen_salt ,
56
+ hooks_to_js_object ,
55
57
)
56
58
from . import _callback
57
59
from . import _get_paths
70
72
_import_layouts_from_pages ,
71
73
)
72
74
from ._jupyter import jupyter_dash , JupyterDisplayMode
75
+ from .types import RendererHooks
73
76
74
77
# Add explicit mapping for map files
75
78
mimetypes .add_type ("application/json" , ".map" , True )
134
137
135
138
136
139
def _get_traceback (secret , error : Exception ):
137
-
138
140
try :
139
141
# pylint: disable=import-outside-toplevel
140
142
from werkzeug .debug import tbtools
@@ -339,6 +341,10 @@ class Dash:
339
341
340
342
:param add_log_handler: Automatically add a StreamHandler to the app logger
341
343
if not added previously.
344
+
345
+ :param hooks: Extend Dash renderer functionality by passing a dictionary of
346
+ javascript functions. To hook into the layout, use dict keys "layout_pre" and
347
+ "layout_post". To hook into the callbacks, use keys "request_pre" and "request_post"
342
348
"""
343
349
344
350
_plotlyjs_url : str
@@ -375,6 +381,7 @@ def __init__( # pylint: disable=too-many-statements
375
381
long_callback_manager = None ,
376
382
background_callback_manager = None ,
377
383
add_log_handler = True ,
384
+ hooks : Union [RendererHooks , None ] = None ,
378
385
** obsolete ,
379
386
):
380
387
_validate .check_obsolete (obsolete )
@@ -468,7 +475,7 @@ def __init__( # pylint: disable=too-many-statements
468
475
self ._favicon = None
469
476
470
477
# default renderer string
471
- self .renderer = "var renderer = new DashRenderer();"
478
+ self .renderer = f "var renderer = new DashRenderer({ hooks_to_js_object ( hooks ) } );"
472
479
473
480
# static files from the packages
474
481
self .css = Css (serve_locally )
@@ -1327,7 +1334,6 @@ def _setup_server(self):
1327
1334
1328
1335
# Copy over global callback data structures assigned with `dash.callback`
1329
1336
for k in list (_callback .GLOBAL_CALLBACK_MAP ):
1330
-
1331
1337
if k in self .callback_map :
1332
1338
raise DuplicateCallback (
1333
1339
f"The callback `{ k } ` provided with `dash.callback` was already "
@@ -1354,7 +1360,6 @@ def _setup_server(self):
1354
1360
1355
1361
if cancels :
1356
1362
for cancel_input , manager in cancels .items ():
1357
-
1358
1363
# pylint: disable=cell-var-from-loop
1359
1364
@self .callback (
1360
1365
Output (cancel_input .component_id , "id" ),
@@ -1745,7 +1750,6 @@ def enable_dev_tools(
1745
1750
_reload .watch_thread .start ()
1746
1751
1747
1752
if debug :
1748
-
1749
1753
if jupyter_dash .active :
1750
1754
jupyter_dash .configure_callback_exception_handling (
1751
1755
self , dev_tools .prune_errors
@@ -1779,7 +1783,6 @@ def _after_request(response):
1779
1783
dash_total ["dur" ] = round ((time .time () - dash_total ["dur" ]) * 1000 )
1780
1784
1781
1785
for name , info in timing_information .items ():
1782
-
1783
1786
value = name
1784
1787
if info .get ("desc" ) is not None :
1785
1788
value += f';desc="{ info ["desc" ]} "'
0 commit comments