From 1b3f328689c80a8de1132a405947f6cf0410fee9 Mon Sep 17 00:00:00 2001 From: leeagustin Date: Sat, 22 Jun 2024 13:27:20 -0700 Subject: [PATCH 1/8] Outputs DeprecationWarning if LogoutButton is used in layout --- dash/_validate.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dash/_validate.py b/dash/_validate.py index b5e21d9eb5..d89a6806a8 100644 --- a/dash/_validate.py +++ b/dash/_validate.py @@ -1,6 +1,7 @@ import sys from collections.abc import MutableSequence import re +import warnings from textwrap import dedent from keyword import iskeyword import flask @@ -422,6 +423,15 @@ def validate_layout(layout, layout_value): component_ids = set() def _validate(value): + def _validate_type(comp): + deprecated_types = ["LogoutButton"] + component_type = getattr(comp, "_type", None) + if component_type and component_type in deprecated_types: + warnings.warn( + f"{component_type} is deprecated, use a different component type instead", + DeprecationWarning, + ) + def _validate_id(comp): component_id = stringify_id(getattr(comp, "id", None)) if component_id and component_id in component_ids: @@ -432,9 +442,11 @@ def _validate_id(comp): ) component_ids.add(component_id) + _validate_type(value) _validate_id(value) for component in value._traverse(): # pylint: disable=protected-access + _validate_type(component) _validate_id(component) if isinstance(layout_value, (list, tuple)): From d6e1b58f607fcb1d4480522b3b290722e1e336f8 Mon Sep 17 00:00:00 2001 From: leeagustin Date: Sat, 22 Jun 2024 13:29:10 -0700 Subject: [PATCH 2/8] Modifies test to check if LogoutButton use outputs DeprecationWarning --- .../location/test_location_logout.py | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/components/dash-core-components/tests/integration/location/test_location_logout.py b/components/dash-core-components/tests/integration/location/test_location_logout.py index a557bcc72a..b62d1db357 100644 --- a/components/dash-core-components/tests/integration/location/test_location_logout.py +++ b/components/dash-core-components/tests/integration/location/test_location_logout.py @@ -1,6 +1,7 @@ from dash.exceptions import PreventUpdate from dash import Dash, Input, Output, dcc, html import flask +import pytest import time @@ -33,15 +34,19 @@ def _insert_cookie(rep): return dcc.LogoutButton(id="logout-btn", logout_url="/_logout") - dash_dcc.start_server(app) - time.sleep(1) - dash_dcc.percy_snapshot("Core Logout button") + with pytest.warns( + DeprecationWarning, + match="LogoutButton is deprecated, use a different component type instead", + ): + dash_dcc.start_server(app) + time.sleep(1) + dash_dcc.percy_snapshot("Core Logout button") - assert dash_dcc.driver.get_cookie("logout-cookie")["value"] == "logged-in" + assert dash_dcc.driver.get_cookie("logout-cookie")["value"] == "logged-in" - dash_dcc.wait_for_element("#logout-btn").click() - dash_dcc.wait_for_text_to_equal("#content", "Logged out") + dash_dcc.wait_for_element("#logout-btn").click() + dash_dcc.wait_for_text_to_equal("#content", "Logged out") - assert not dash_dcc.driver.get_cookie("logout-cookie") + assert not dash_dcc.driver.get_cookie("logout-cookie") - assert dash_dcc.get_logs() == [] + assert dash_dcc.get_logs() == [] From c54ae9135ac39b11309a990f0241f968377a43be Mon Sep 17 00:00:00 2001 From: leeagustin Date: Sat, 22 Jun 2024 13:52:58 -0700 Subject: [PATCH 3/8] Modifies test to show that DeprecationWarning is being output when LogoutButton is used in initial page layout --- .../integration/location/test_location_logout.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/components/dash-core-components/tests/integration/location/test_location_logout.py b/components/dash-core-components/tests/integration/location/test_location_logout.py index b62d1db357..06fa48fc25 100644 --- a/components/dash-core-components/tests/integration/location/test_location_logout.py +++ b/components/dash-core-components/tests/integration/location/test_location_logout.py @@ -5,7 +5,8 @@ import time -def test_llgo001_location_logout(dash_dcc): +@pytest.mark.parametrize("add_initial_logout_button", [False, True]) +def test_llgo001_location_logout(dash_dcc, add_initial_logout_button): app = Dash(__name__) @app.server.route("/_logout", methods=["POST"]) @@ -14,9 +15,14 @@ def on_logout(): rep.set_cookie("logout-cookie", "", 0) return rep - app.layout = html.Div( - [html.H2("Logout test"), dcc.Location(id="location"), html.Div(id="content")] - ) + layout_children = [ + html.H2("Logout test"), + dcc.Location(id="location"), + html.Div(id="content"), + ] + if add_initial_logout_button: + layout_children.append(dcc.LogoutButton()) + app.layout = html.Div(layout_children) @app.callback(Output("content", "children"), [Input("location", "pathname")]) def on_location(location_path): From 87385a94da1ac2922dd96bb2133b7b476cfd0178 Mon Sep 17 00:00:00 2001 From: leeagustin Date: Thu, 27 Jun 2024 17:27:25 -0700 Subject: [PATCH 4/8] Refactors deprecated_types to deprecated_components - Checks for namespace on top of the type - Modifies warning message - Modifies test to capture new warning message --- .../location/test_location_logout.py | 2 +- dash/_validate.py | 21 ++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/components/dash-core-components/tests/integration/location/test_location_logout.py b/components/dash-core-components/tests/integration/location/test_location_logout.py index 06fa48fc25..a71d8e4cb1 100644 --- a/components/dash-core-components/tests/integration/location/test_location_logout.py +++ b/components/dash-core-components/tests/integration/location/test_location_logout.py @@ -42,7 +42,7 @@ def _insert_cookie(rep): with pytest.warns( DeprecationWarning, - match="LogoutButton is deprecated, use a different component type instead", + match="The Logout Button is no longer used with Dash Enterprise and can be replaced with a html.Button or html.A.", ): dash_dcc.start_server(app) time.sleep(1) diff --git a/dash/_validate.py b/dash/_validate.py index d89a6806a8..1500a3c649 100644 --- a/dash/_validate.py +++ b/dash/_validate.py @@ -1,4 +1,5 @@ import sys +from collections import defaultdict from collections.abc import MutableSequence import re import warnings @@ -424,13 +425,19 @@ def validate_layout(layout, layout_value): def _validate(value): def _validate_type(comp): - deprecated_types = ["LogoutButton"] - component_type = getattr(comp, "_type", None) - if component_type and component_type in deprecated_types: - warnings.warn( - f"{component_type} is deprecated, use a different component type instead", - DeprecationWarning, - ) + deprecated_components = defaultdict(lambda: defaultdict(dict)) + deprecated_components["dash_core_components"][ + "LogoutButton" + ] = """ + The Logout Button is no longer used with Dash Enterprise and can be replaced with a html.Button or html.A. + eg: html.A(href=os.getenv('DASH_LOGOUT_URL')) + """ + + _type = getattr(comp, "_type", "") + _ns = getattr(comp, "_namespace", "") + deprecation_message = deprecated_components[_ns][_type] + if deprecation_message: + warnings.warn(dedent(deprecation_message), DeprecationWarning) def _validate_id(comp): component_id = stringify_id(getattr(comp, "id", None)) From bd11aa0df75c613240b4928ceb40e3646980a11f Mon Sep 17 00:00:00 2001 From: philippe Date: Wed, 4 Sep 2024 11:41:30 -0400 Subject: [PATCH 5/8] Deprecate dynamic component loader --- dash/development/component_loader.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dash/development/component_loader.py b/dash/development/component_loader.py index 518c7638a4..4075d598ac 100644 --- a/dash/development/component_loader.py +++ b/dash/development/component_loader.py @@ -1,6 +1,8 @@ import collections import json import os +import warnings + from ._py_components_generation import ( generate_class_file, @@ -34,7 +36,13 @@ def load_components(metadata_path, namespace="default_namespace"): components -- a list of component objects with keys `type`, `valid_kwargs`, and `setup`. """ - + warnings.warn( + DeprecationWarning( + "Dynamic components loading has been deprecated and will be removed" + " in dash 3.0.\n" + f"Update {namespace} to generate components with dash-generate-components" + ) + ) # Register the component lib for index include. ComponentRegistry.registry.add(namespace) components = [] From 376599e8150e2d3d82313dd98ca8e0e66edb420b Mon Sep 17 00:00:00 2001 From: philippe Date: Wed, 4 Sep 2024 12:57:37 -0400 Subject: [PATCH 6/8] Deprecate run_server --- dash/dash.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dash/dash.py b/dash/dash.py index 4e38059aa1..707b803775 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -2256,4 +2256,9 @@ def run_server(self, *args, **kwargs): See `app.run` for usage information. """ + warnings.warn( + DeprecationWarning( + "Dash.run_server is deprecated and will be removed in Dash 3.0" + ) + ) self.run(*args, **kwargs) From df2aeea7a6c45bd1bac3bdc78ed6780deff62db7 Mon Sep 17 00:00:00 2001 From: philippe Date: Thu, 5 Sep 2024 11:39:03 -0400 Subject: [PATCH 7/8] Deprate components on init. --- .../location/test_location_logout.py | 56 +++++-------------- dash/_validate.py | 19 ------- dash/development/base_component.py | 21 +++++++ 3 files changed, 35 insertions(+), 61 deletions(-) diff --git a/components/dash-core-components/tests/integration/location/test_location_logout.py b/components/dash-core-components/tests/integration/location/test_location_logout.py index a71d8e4cb1..12ceda2a35 100644 --- a/components/dash-core-components/tests/integration/location/test_location_logout.py +++ b/components/dash-core-components/tests/integration/location/test_location_logout.py @@ -1,58 +1,30 @@ -from dash.exceptions import PreventUpdate from dash import Dash, Input, Output, dcc, html -import flask import pytest import time @pytest.mark.parametrize("add_initial_logout_button", [False, True]) def test_llgo001_location_logout(dash_dcc, add_initial_logout_button): + # FIXME: Logout button is deprecated, remove this test for dash 3.0 app = Dash(__name__) - @app.server.route("/_logout", methods=["POST"]) - def on_logout(): - rep = flask.redirect("/logged-out") - rep.set_cookie("logout-cookie", "", 0) - return rep - - layout_children = [ - html.H2("Logout test"), - dcc.Location(id="location"), - html.Div(id="content"), - ] - if add_initial_logout_button: - layout_children.append(dcc.LogoutButton()) - app.layout = html.Div(layout_children) - - @app.callback(Output("content", "children"), [Input("location", "pathname")]) - def on_location(location_path): - if location_path is None: - raise PreventUpdate - - if "logged-out" in location_path: - return "Logged out" - else: - - @flask.after_this_request - def _insert_cookie(rep): - rep.set_cookie("logout-cookie", "logged-in") - return rep - - return dcc.LogoutButton(id="logout-btn", logout_url="/_logout") - with pytest.warns( DeprecationWarning, match="The Logout Button is no longer used with Dash Enterprise and can be replaced with a html.Button or html.A.", ): - dash_dcc.start_server(app) - time.sleep(1) - dash_dcc.percy_snapshot("Core Logout button") - - assert dash_dcc.driver.get_cookie("logout-cookie")["value"] == "logged-in" + app.layout = [ + html.H2("Logout test"), + html.Div(id="content"), + ] + if add_initial_logout_button: + app.layout.append(dcc.LogoutButton()) + else: - dash_dcc.wait_for_element("#logout-btn").click() - dash_dcc.wait_for_text_to_equal("#content", "Logged out") + @app.callback(Output("content", "children"), Input("content", "id")) + def on_location(location_path): + return dcc.LogoutButton(id="logout-btn", logout_url="/_logout") - assert not dash_dcc.driver.get_cookie("logout-cookie") + dash_dcc.start_server(app) + time.sleep(1) - assert dash_dcc.get_logs() == [] + assert dash_dcc.get_logs() == [] diff --git a/dash/_validate.py b/dash/_validate.py index d14db6bc96..a26fd0f73b 100644 --- a/dash/_validate.py +++ b/dash/_validate.py @@ -1,8 +1,6 @@ import sys -from collections import defaultdict from collections.abc import MutableSequence import re -import warnings from textwrap import dedent from keyword import iskeyword import flask @@ -424,21 +422,6 @@ def validate_layout(layout, layout_value): component_ids = set() def _validate(value): - def _validate_type(comp): - deprecated_components = defaultdict(lambda: defaultdict(dict)) - deprecated_components["dash_core_components"][ - "LogoutButton" - ] = """ - The Logout Button is no longer used with Dash Enterprise and can be replaced with a html.Button or html.A. - eg: html.A(href=os.getenv('DASH_LOGOUT_URL')) - """ - - _type = getattr(comp, "_type", "") - _ns = getattr(comp, "_namespace", "") - deprecation_message = deprecated_components[_ns][_type] - if deprecation_message: - warnings.warn(dedent(deprecation_message), DeprecationWarning) - def _validate_id(comp): component_id = stringify_id(getattr(comp, "id", None)) if component_id and component_id in component_ids: @@ -449,11 +432,9 @@ def _validate_id(comp): ) component_ids.add(component_id) - _validate_type(value) _validate_id(value) for component in value._traverse(): # pylint: disable=protected-access - _validate_type(component) _validate_id(component) if isinstance(layout_value, (list, tuple)): diff --git a/dash/development/base_component.py b/dash/development/base_component.py index 9bf8a18931..1b5d844517 100644 --- a/dash/development/base_component.py +++ b/dash/development/base_component.py @@ -4,6 +4,8 @@ import sys import uuid import random +import warnings +import textwrap from .._utils import patch_collections_abc, stringify_id, OrderedSet @@ -11,6 +13,17 @@ rd = random.Random(0) +_deprecated_components = { + "dash_core_components": { + "LogoutButton": textwrap.dedent( + """ + The Logout Button is no longer used with Dash Enterprise and can be replaced with a html.Button or html.A. + eg: html.A(href=os.getenv('DASH_LOGOUT_URL')) + """ + ) + } +} + # pylint: disable=no-init,too-few-public-methods class ComponentRegistry: @@ -95,6 +108,7 @@ def __str__(self): REQUIRED = _REQUIRED() def __init__(self, **kwargs): + self._validate_deprecation() import dash # pylint: disable=import-outside-toplevel, cyclic-import # pylint: disable=super-init-not-called @@ -405,6 +419,13 @@ def __repr__(self): props_string = repr(getattr(self, "children", None)) return f"{self._type}({props_string})" + def _validate_deprecation(self): + _type = getattr(self, "_type", "") + _ns = getattr(self, "_namespace", "") + deprecation_message = _deprecated_components.get(_ns, {}).get(_type) + if deprecation_message: + warnings.warn(DeprecationWarning(textwrap.dedent(deprecation_message))) + def _explicitize_args(func): # Python 2 From e6b0c365d5d1e47faa99a4b15cdc7c4766cef8e1 Mon Sep 17 00:00:00 2001 From: philippe Date: Thu, 5 Sep 2024 11:44:17 -0400 Subject: [PATCH 8/8] Add deprecations to changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd1b7b2a51..8f96e4ca99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to `dash` will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/). +## [UNRELEASED] + +## Deprecated + +- [#2985](https://github.com/plotly/dash/pull/2985) Deprecate dynamic component loader. +- [#2985](https://github.com/plotly/dash/pull/2985) Deprecate `run_server`, use `run` instead. +- [#2899](https://github.com/plotly/dash/pull/2899) Deprecate `dcc.LogoutButton`, can be replaced with a `html.Button` or `html.A`. eg: `html.A(href=os.getenv('DASH_LOGOUT_URL'))` on a Dash Enterprise instance. + ## [2.18.0] - 2024-09-04 ## Added