Skip to content

Commit 62d6c51

Browse files
authored
Merge pull request #3432 from plotly/dev
Sync `v4` with latest `dev`
2 parents 45cfb0a + 4b8917e commit 62d6c51

File tree

16 files changed

+1486
-72
lines changed

16 files changed

+1486
-72
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@ This project adheres to [Semantic Versioning](https://semver.org/).
1212
- [#3395](https://github.com/plotly/dash/pull/3396) Add position argument to hooks.devtool
1313
- [#3403](https://github.com/plotly/dash/pull/3403) Add app_context to get_app, allowing to get the current app in routes.
1414
- [#3407](https://github.com/plotly/dash/pull/3407) Add `hidden` to callback arguments, hiding the callback from appearing in the devtool callback graph.
15+
- [#3424](https://github.com/plotly/dash/pull/3424) Adds support for `Patch` on clientside callbacks class `dash_clientside.Patch`, as well as supporting side updates, eg: (Running, SetProps).
16+
- [#3347](https://github.com/plotly/dash/pull/3347) Added 'api_endpoint' to `callback` to expose api endpoints at the provided path for use to be executed directly without dash.
1517

1618
## Fixed
1719
- [#3395](https://github.com/plotly/dash/pull/3395) Fix Components added through set_props() cannot trigger related callback functions. Fix [#3316](https://github.com/plotly/dash/issues/3316)
1820
- [#3397](https://github.com/plotly/dash/pull/3397) Add optional callbacks, suppressing callback warning for missing component ids for a single callback.
21+
- [#3415](https://github.com/plotly/dash/pull/3415) Fix the error triggered when only a single no_update is returned for client-side callback functions with multiple Outputs. Fix [#3366](https://github.com/plotly/dash/issues/3366)
22+
- [#3416](https://github.com/plotly/dash/issues/3416) Fix DeprecationWarning in dash/_jupyter.py by migrating from deprecated ipykernel.comm.Comm to comm module
1923

2024
## [3.2.0] - 2025-07-31
2125

CITATION.cff

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ authors:
99
given-names: "Alex"
1010
orcid: https://orcid.org/0000-0003-4623-4147
1111
title: "A data and analytics web app framework for Python, no JavaScript required."
12-
version: 2.18.2
12+
version: 3.2.0
1313
doi: 10.5281/zenodo.14182630
14-
date-released: 2024-11-04
14+
date-released: 2025-07-31
1515
url: https://github.com/plotly/dash

components/dash-html-components/scripts/data/attributes.json

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -226,11 +226,8 @@
226226
"button",
227227
"fieldset",
228228
"input",
229-
"label",
230-
"meter",
231229
"object",
232230
"output",
233-
"progress",
234231
"select",
235232
"textarea"
236233
],
@@ -993,28 +990,13 @@
993990
"target"
994991
],
995992
"label": [
996-
"htmlFor",
997-
"form"
993+
"htmlFor"
998994
],
999995
"output": [
1000996
"htmlFor",
1001997
"form",
1002998
"name"
1003999
],
1004-
"meter": [
1005-
"form",
1006-
"high",
1007-
"low",
1008-
"max",
1009-
"min",
1010-
"optimum",
1011-
"value"
1012-
],
1013-
"progress": [
1014-
"form",
1015-
"max",
1016-
"value"
1017-
],
10181000
"canvas": [
10191001
"height",
10201002
"width"
@@ -1025,6 +1007,14 @@
10251007
"type",
10261008
"width"
10271009
],
1010+
"meter": [
1011+
"high",
1012+
"low",
1013+
"max",
1014+
"min",
1015+
"optimum",
1016+
"value"
1017+
],
10281018
"base": [
10291019
"href",
10301020
"target"
@@ -1035,6 +1025,10 @@
10351025
"marquee": [
10361026
"loop"
10371027
],
1028+
"progress": [
1029+
"max",
1030+
"value"
1031+
],
10381032
"source": [
10391033
"media",
10401034
"sizes",

dash/_callback.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ def _invoke_callback(func, *args, **kwargs): # used to mark the frame for the d
6262
GLOBAL_CALLBACK_LIST = []
6363
GLOBAL_CALLBACK_MAP = {}
6464
GLOBAL_INLINE_SCRIPTS = []
65+
GLOBAL_API_PATHS = {}
6566

6667

6768
# pylint: disable=too-many-locals,too-many-arguments
@@ -77,6 +78,7 @@ def callback(
7778
cache_args_to_ignore: Optional[list] = None,
7879
cache_ignore_triggered=True,
7980
on_error: Optional[Callable[[Exception], Any]] = None,
81+
api_endpoint: Optional[str] = None,
8082
optional: Optional[bool] = False,
8183
hidden: Optional[bool] = False,
8284
**_kwargs,
@@ -165,6 +167,14 @@ def callback(
165167
Mark all dependencies as not required on the initial layout checks.
166168
:param hidden:
167169
Hide the callback from the devtools callbacks tab.
170+
:param api_endpoint:
171+
If provided, the callback will be available at the given API endpoint.
172+
This allows you to call the callback directly through HTTP requests
173+
instead of through the Dash front-end. The endpoint should be a string
174+
that starts with a forward slash (e.g. `/my_callback`).
175+
The endpoint is relative to the Dash app's base URL.
176+
Note that the endpoint will not appear in the list of registered
177+
callbacks in the Dash devtools.
168178
"""
169179

170180
background_spec = None
@@ -219,6 +229,7 @@ def callback(
219229
manager=manager,
220230
running=running,
221231
on_error=on_error,
232+
api_endpoint=api_endpoint,
222233
optional=optional,
223234
hidden=hidden,
224235
)
@@ -587,7 +598,11 @@ def _prepare_response(
587598

588599
# pylint: disable=too-many-branches,too-many-statements
589600
def register_callback(
590-
callback_list, callback_map, config_prevent_initial_callbacks, *_args, **_kwargs
601+
callback_list,
602+
callback_map,
603+
config_prevent_initial_callbacks,
604+
*_args,
605+
**_kwargs,
591606
):
592607
(
593608
output,
@@ -642,6 +657,10 @@ def register_callback(
642657

643658
# pylint: disable=too-many-locals
644659
def wrap_func(func):
660+
if _kwargs.get("api_endpoint"):
661+
api_endpoint = _kwargs.get("api_endpoint")
662+
GLOBAL_API_PATHS[api_endpoint] = func
663+
645664
if background is None:
646665
background_key = None
647666
else:

dash/_get_app.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ def with_app_context_async(func):
2323
async def wrap(self, *args, **kwargs):
2424
app_context.set(self)
2525
ctx = copy_context()
26-
print("copied and set")
2726
return await ctx.run(func, self, *args, **kwargs)
2827

2928
return wrap

dash/_jupyter.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@
2222
from IPython.core.display import HTML
2323
from IPython.core.ultratb import FormattedTB
2424
from retrying import retry
25-
from ipykernel.comm import Comm
25+
from comm import create_comm
2626
import nest_asyncio
2727

2828
import requests
2929

30-
_dash_comm = Comm(target_name="dash")
30+
_dash_comm = create_comm(target_name="dash")
3131
_dep_installed = True
3232
except ImportError:
3333
_dep_installed = False
@@ -97,10 +97,14 @@ def convert(name, locals=locals, formatarg=formatarg, formatvalue=formatvalue):
9797
def _send_jupyter_config_comm_request():
9898
# If running in an ipython kernel,
9999
# request that the front end extension send us the notebook server base URL
100-
if get_ipython() is not None:
101-
if _dash_comm.kernel is not None:
102-
_caller["parent"] = _dash_comm.kernel.get_parent()
103-
_dash_comm.send({"type": "base_url_request"})
100+
ipython = get_ipython()
101+
if (
102+
ipython is not None
103+
and hasattr(ipython, "kernel")
104+
and ipython.kernel is not None
105+
):
106+
_caller["parent"] = ipython.kernel.get_parent()
107+
_dash_comm.send({"type": "base_url_request"})
104108

105109

106110
def _jupyter_comm_response_received():
@@ -109,7 +113,8 @@ def _jupyter_comm_response_received():
109113

110114
def _request_jupyter_config(timeout=2):
111115
# Heavily inspired by implementation of CaptureExecution in the
112-
if _dash_comm.kernel is None:
116+
ipython = get_ipython()
117+
if ipython is None or not hasattr(ipython, "kernel") or ipython.kernel is None:
113118
# Not in jupyter setting
114119
return
115120

@@ -215,8 +220,15 @@ def __init__(self):
215220
@_dash_comm.on_msg
216221
def _receive_message(msg):
217222
prev_parent = _caller.get("parent")
218-
if prev_parent and prev_parent != _dash_comm.kernel.get_parent():
219-
_dash_comm.kernel.set_parent(
223+
ipython = get_ipython()
224+
if (
225+
prev_parent
226+
and ipython is not None
227+
and hasattr(ipython, "kernel")
228+
and ipython.kernel is not None
229+
and prev_parent != ipython.kernel.get_parent()
230+
):
231+
ipython.kernel.set_parent(
220232
[prev_parent["header"]["session"]], prev_parent
221233
)
222234
del _caller["parent"]

dash/dash-renderer/package-lock.json

Lines changed: 18 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)