emilhe / dash-extensions

The dash-extensions package is a collection of utility functions, syntax extensions, and Dash components that aim to improve the Dash development experience
https://www.dash-extensions.com/
MIT License
409 stars 58 forks source link

adding LogTransform to app causes Duplicate callback output error #280

Closed matt-sd-watson closed 5 months ago

matt-sd-watson commented 11 months ago

When adding the LogTransform as an app transformation:

transforms=[ServersideOutputTransform(backends=[backend_dir]),
                                    LogTransform()]

Upon opening, my app now has a Duplicate callback outputs error, despite not having the error without the transform. The particular output in question uses a allow_duplicate=True flag as needed.

emilhe commented 11 months ago

It is probably related to the recent change in callback structure when allow_duplicate=True flag was added. Could you post a small example demonstrating the issue?

connorferster commented 8 months ago

I have the same issue. It came up as I upgraded my project dependencies from 0.1.13 to 1.0.4. It seems that there is an undocumented breaking change in 1.0.0? (I tried using 1.0.0 as a dependency instead of 1.0.4 but I still get the same error)

Interestingly, I just tried deploying my app with the LogTransform added using 0.1.13 and, while it works on my localhost, I do not see any logging messages on my deploy server.

connorferster commented 8 months ago

I have tried replicating the issue with a small example but I am not able to replicate it on a small example. Here is the code I used in an attempt to replicate it:

import secrets
from dash_extensions.enrich import (
    DashProxy,
    LogTransform,
    Input,
    Output,
    State,
    DashLogger
)
from dash import dcc, html, no_update
import flask

server = flask.Flask(__name__)
app = DashProxy(
    transforms=[
        LogTransform(),
    ],
    external_stylesheets=[],
    server=server,
)

app.server.secret_key = secrets.token_urlsafe(16)

app.layout = html.Div([
    dcc.Input(id='text_input'),
    html.Button("Click me", id="input_button"),
    html.Button("Click me 2", id="input_button2"),
    html.H2(id="output_region")
])

@app.callback(
        Output("output_region", "children", allow_duplicate=True),
        State('text_input', 'value'),
        Input("input_button", "n_clicks"),
        prevent_initial_call=True,
        log=True,
    )
def callback1(
    input_text: str,
    button_clicks: int,
    dash_logger: DashLogger
    ):
    if not input_text:
        dash_logger.warning("Please input some text value before clicking")
        return no_update
    return input_text

@app.callback(
        Output("output_region", "children", allow_duplicate=True),
        State('text_input', 'value'),
        Input("input_button2", "n_clicks"),
        prevent_initial_call=True,
        log=True,
    )
def callback2(
    input_text: str,
    button_clicks: int,
    dash_logger: DashLogger
    ):
    if not input_text:
        dash_logger.warning("Please input some text value before clicking")
        return no_update
    return input_text

# Run App
if __name__ == "__main__":
    app.run(debug=True)
connorferster commented 8 months ago

The app that I am running is a fairly large multi-page application. The outputs that triggered the error were not, in fact, duplicate outputs. Still, I added the allow_duplicate=True argument but it has no effect.

The other thing that I found interesting about the callbacks that triggered the error is that they are on two of my sub-pages which are fairly deep into the application. I thought that the error would have been triggered by one of my earlier callbacks.

EDIT:

I "fixed" the issue by just removing the logging behaviour from the offending callbacks. For me, it was on two non-critical functions. To confirm, though, the duplicate callbacks were on the dash mantine components notifications, not any of my Output objects. I used the default dmc notifications on the included DashLogger. So, for some reason, the notification outputs on those two callback functions seemed to be creating duplicate IDs on the notification component...that is, I am assuming that how the LoggingTransform works is by adding additional Output objects to the function. Since, I do not have access to those Output objects, it seems that I am not able to add the allow_duplicate=True on the Output objects that are actually causing the error.

emilhe commented 5 months ago

Should be fixed in the 1.0.12 release. Please comment/re-open if you still see any issues.

matt-sd-watson commented 5 months ago

The issue still appears to occur, I will try to develop another MRE to indicate

mykhalaida commented 4 months ago

@emilhe, I might be mistaken but I think adding allow_duplicate=True to Outputs in 1.0.12 didn’t help for reasons well described in this issue: https://github.com/plotly/dash/issues/2486

In my particular situation, I have two callbacks that share the same Inputs but had different Outputs, which works fine. However, after adding log=True to those callbacks, they now share the notification_provider Output with the same uid, which now does cause the “Duplicate callback outputs” issue.

I've been trying to make the log_id unique inside setup_notifications_log_config() and wasn't successful yet, but maybe that's a step in right direction?