Skip to content

Commit 5b201a1

Browse files
committed
Merge branch 'dev' into support-async-callbacks
2 parents 68320f4 + 7c03187 commit 5b201a1

File tree

6 files changed

+90
-25
lines changed

6 files changed

+90
-25
lines changed

.editorconfig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ charset = utf-8
1212
indent_style = space
1313
indent_size = 4
1414

15-
# Matches the exact files either package.json or .travis.yml
15+
# Matches the exact files either package.json or .circleci/config.yml
1616
[{package.json,.circleci/config.yml}]
1717
indent_style = space
1818
indent_size = 2
19+

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
77
## Fixed
88

99
- [#3080](https://github.com/plotly/dash/pull/3080) Fix docstring generation for components using single-line or nonstandard-indent leading comments
10+
- [#3103](https://github.com/plotly/dash/pull/3103) Fix Graph component becomes unresponsive if an invalid figure is passed
1011

1112
## [2.18.2] - 2024-11-04
1213

CITATION.cff

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
cff-version: 1.2.0
2+
message: "If you use this software, please cite it as below."
3+
authors:
4+
- family-names: "Parmer"
5+
given-names: "Chris"
6+
- family-names: "Duval"
7+
given-names: "Philippe"
8+
- family-names: "Johnson"
9+
given-names: "Alex"
10+
orcid: https://orcid.org/0000-0003-4623-4147
11+
title: "A data and analytics web app framework for Python, no JavaScript required."
12+
version: 2.18.2
13+
doi: 10.5281/zenodo.14182630
14+
date-released: 2024-11-04
15+
url: https://github.com/plotly/dash

components/dash-core-components/src/fragments/Graph.react.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,9 @@ class PlotlyGraph extends Component {
173173
configClone.typesetMath = mathjax;
174174

175175
const figureClone = {
176-
data: figure.data,
177-
layout: this.getLayout(figure.layout, responsive),
178-
frames: figure.frames,
176+
data: figure?.data,
177+
layout: this.getLayout(figure?.layout, responsive),
178+
frames: figure?.frames,
179179
config: configClone,
180180
};
181181

components/dash-core-components/tests/integration/graph/test_graph_basics.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,3 +370,44 @@ def handleClick(clickData):
370370
data = json.loads(data)
371371
assert "customdata" in data["points"][0], "graph clickData must contain customdata"
372372
assert data["points"][0]["customdata"][0] == expected_value
373+
374+
375+
def test_grbs008_graph_with_empty_figure(dash_dcc):
376+
app = Dash(__name__)
377+
app.layout = html.Div(
378+
[
379+
html.Button("Toggle graph", id="btn"),
380+
dcc.Graph(
381+
id="graph",
382+
figure=None,
383+
),
384+
]
385+
)
386+
387+
@app.callback(Output("graph", "figure"), [Input("btn", "n_clicks")])
388+
def toggle_figure(n_clicks):
389+
if int(n_clicks or 0) % 2 == 0:
390+
# a valid figure
391+
return go.Figure([], layout=go.Layout(title="Valid Figure"))
392+
else:
393+
# an invalid figure
394+
return None
395+
396+
dash_dcc.start_server(app)
397+
398+
# Click the toggle button a couple of times and expect the graph to change between the
399+
# valid and invalid figures, using the "title" as the indicator.
400+
dash_dcc.wait_for_element("#graph")
401+
wait.until(
402+
lambda: dash_dcc.find_element(".gtitle").text == "Valid Figure", timeout=2
403+
)
404+
405+
dash_dcc.find_element("#btn").click()
406+
wait.until(lambda: len(dash_dcc.find_elements(".gtitle")) == 0, timeout=2)
407+
408+
dash_dcc.find_element("#btn").click()
409+
wait.until(
410+
lambda: dash_dcc.find_element(".gtitle").text == "Valid Figure", timeout=2
411+
)
412+
413+
assert dash_dcc.get_logs() == []

dash/dash.py

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2060,25 +2060,26 @@ def delete_resource(resources):
20602060
# pylint: disable=protected-access
20612061
delete_resource(self.css._resources._resources)
20622062

2063+
# pylint: disable=too-many-branches
20632064
def run(
20642065
self,
2065-
host="127.0.0.1",
2066-
port="8050",
2067-
proxy=None,
2068-
debug=None,
2066+
host: Optional[str] = None,
2067+
port: Optional[Union[str, int]] = None,
2068+
proxy: Optional[str] = None,
2069+
debug: Optional[bool] = None,
20692070
jupyter_mode: Optional[JupyterDisplayMode] = None,
2070-
jupyter_width="100%",
2071-
jupyter_height=650,
2072-
jupyter_server_url=None,
2073-
dev_tools_ui=None,
2074-
dev_tools_props_check=None,
2075-
dev_tools_serve_dev_bundles=None,
2076-
dev_tools_hot_reload=None,
2077-
dev_tools_hot_reload_interval=None,
2078-
dev_tools_hot_reload_watch_interval=None,
2079-
dev_tools_hot_reload_max_retry=None,
2080-
dev_tools_silence_routes_logging=None,
2081-
dev_tools_prune_errors=None,
2071+
jupyter_width: str = "100%",
2072+
jupyter_height: int = 650,
2073+
jupyter_server_url: Optional[str] = None,
2074+
dev_tools_ui: Optional[bool] = None,
2075+
dev_tools_props_check: Optional[bool] = None,
2076+
dev_tools_serve_dev_bundles: Optional[bool] = None,
2077+
dev_tools_hot_reload: Optional[bool] = None,
2078+
dev_tools_hot_reload_interval: Optional[int] = None,
2079+
dev_tools_hot_reload_watch_interval: Optional[int] = None,
2080+
dev_tools_hot_reload_max_retry: Optional[int] = None,
2081+
dev_tools_silence_routes_logging: Optional[bool] = None,
2082+
dev_tools_prune_errors: Optional[bool] = None,
20822083
**flask_run_options,
20832084
):
20842085
"""Start the flask server in local mode, you should not run this on a
@@ -2087,11 +2088,11 @@ def run(
20872088
If a parameter can be set by an environment variable, that is listed
20882089
too. Values provided here take precedence over environment variables.
20892090
2090-
:param host: Host IP used to serve the application
2091+
:param host: Host IP used to serve the application, default to "127.0.0.1"
20912092
env: ``HOST``
20922093
:type host: string
20932094
2094-
:param port: Port used to serve the application
2095+
:param port: Port used to serve the application, default to "8050"
20952096
env: ``PORT``
20962097
:type port: int
20972098
@@ -2192,9 +2193,15 @@ def run(
21922193

21932194
# Evaluate the env variables at runtime
21942195

2195-
host = os.getenv("HOST", host)
2196-
port = os.getenv("PORT", port)
2197-
proxy = os.getenv("DASH_PROXY", proxy)
2196+
if "CONDA_PREFIX" in os.environ:
2197+
# Some conda systems has issue with setting the host environment
2198+
# to an invalid hostname.
2199+
# Related issue: https://github.com/plotly/dash/issues/3069
2200+
host = host or "127.0.0.1"
2201+
else:
2202+
host = host or os.getenv("HOST", "127.0.0.1")
2203+
port = port or os.getenv("PORT", "8050")
2204+
proxy = proxy or os.getenv("DASH_PROXY")
21982205

21992206
# Verify port value
22002207
try:

0 commit comments

Comments
 (0)