Debugging long callbacks

I just need to add a progress bar to my app, and reading Dash’s docs, it seems it is mandatory to use long callbacks (and use a callback manager). So, I did it, but I can’t debug anymore the code of my callback since my print outputs aren’t displayed anymore. How come?

My code is the following:

@callback(
    output=[Output(component_id="generated-file", component_property="data")],
    inputs=[Input(component_id="generate", component_property="n_clicks")],
    state=[State(component_id="description", component_property="value"), ...],
    running=[(Output("generate", "disabled"), True, False), (Output("cancel", "disabled"), False, True)],
    progress=[Output("progress-bar", "value"), Output("progress-bar", "max")],
    cancel=[Input("cancel", "n_clicks")],
    prevent_initial_call=True,
    background=True,
    manager=long_callback_manager
)
def generate_callback(set_progress, n_clicks, description, expectations, style):
    print(f"Description: {description}") # Not displayed
    ...

    total = 10
    for i in range(10):
        time.sleep(0.5)
        set_progress((str(i + 1), str(total)))

    return ""

The main issue is that print statements might not be captured correctly in background processes. Using logging , explicitly printing to sys.stdout , or using a debugger are more reliable ways to debug your long callbacks in Dash.

1. Using logging

import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@callback(
    output=[Output(component_id="generated-file", component_property="data")],
    inputs=[Input(component_id="generate", component_property="n_clicks")],
    state=[State(component_id="description", component_property="value"), ...],
    running=[(Output("generate", "disabled"), True, False), (Output("cancel", "disabled"), False, True)],
    progress=[Output("progress-bar", "value"), Output("progress-bar", "max")],
    cancel=[Input("cancel", "n_clicks")],
    prevent_initial_call=True,
    background=True,
    manager=long_callback_manager
)
def generate_callback(set_progress, n_clicks, description, expectations, style):
    logger.info(f"Description: {description}")  # This will be logged
    ...

    total = 10
    for i in range(10):
        time.sleep(0.5)
        set_progress((str(i + 1), str(total)))

    return ""

2. Use sys.stdout or sys.stderr

import sys

@callback(
    output=[Output(component_id="generated-file", component_property="data")],
    inputs=[Input(component_id="generate", component_property="n_clicks")],
    state=[State(component_id="description", component_property="value"), ...],
    running=[(Output("generate", "disabled"), True, False), (Output("cancel", "disabled"), False, True)],
    progress=[Output("progress-bar", "value"), Output("progress-bar", "max")],
    cancel=[Input("cancel", "n_clicks")],
    prevent_initial_call=True,
    background=True,
    manager=long_callback_manager
)
def generate_callback(set_progress, n_clicks, description, expectations, style):
    print(f"Description: {description}", file=sys.stdout)  # Explicitly print to stdout
    ...

    total = 10
    for i in range(10):
        time.sleep(0.5)
        set_progress((str(i + 1), str(total)))

    return ""

Hello, I used sys.stdout but it didn’t change anything (as far as I know, print writes to stdout by default). But when I deactivated the “running” parameter in my callback, I could see the issue printed on my console: all imported packages like sys or time become undefined. It’s as if Dash was trying to execute my function is another python process (without importing all the packages). Maybe it’s what it does in the background.

LongCallbackError: An error occurred inside a long callback: name ‘time’ is not defined

Well, communicating with a progress bar seems to be a real pain with Dash, isn’t there a simpler way to do it?

Hi @Mark531

For a basic progress bar that runs whenever a callback is triggered, use GitHub - snehilvj/dash.nprogress: Loading indication in dash apps using nprogress.js library

It works well, but is also visible with Dash’s polling requests in debug mode or background callbacks.

Maybe this solves it already.

Beside that, are you running the background callback with a celery server ?