Black Lives Matter. Please consider donating to Black Girls Code today.
Dash Enterprise delivers an incredible 21x cost savings 💸Download the e-book!

Code in multiple files: Callbacks work in debug, not in gunicorn

Hello,

I’ve built a multi-tab Dash app, with code split into multiple files, which works fine with the debug server but breaks under gunicorn. Specifically, all of the callbacks defined anywhere but the first Python file are inert. I can add a button and callback in the index file and it works, regardless of whether it’s in the tab area or not. All buttons and tabs work normally when run from the Dash development server. There are no errors in the console; the Devtools network view shows normal function for buttons defined in index.py and no activity at all for other buttons. Testing with waitress produced the same outcome, of callbacks from other files not working.

I would appreciate any tips on what coding practice I missed, or the best way to restructure files if this is a permanent difference between coding layout allowable on debug vs production. Full code at Github project; the version on master works fine in debug and the version on gunicorn branch runs—with the problem described above—in gunicorn or waitress with the entry point index:server.

Thanks,

Joel

Is your gunicorn entry point the same entry point you’re using with python? eg if you write

python my_app.py

the the gunicorn command needs to be

gunicorn my_app:server

That was definitely the right clue, but I’m still stuck on an incomplete model of flask and servers. Here’s the hole I dig (omitting host addresses and imports et al):

1. python index.py
This worked fine in development.
app.py

import dash
app = dash.Dash(__name__, suppress_callback_exceptions=True)
server = app.server

index.py

…
from app import app
…
if __name__ == '__main__':
    app.run_server()

2. gunicorn index:server
Same files as above. First attempt to move to production, got Error:
Failed to find attribute 'server' in 'index'.

3. gunicorn index:server v2
Removed app.py and changed index.py to include

…
app = Dash(__name__)
server = app.server
…
if __name__ == '__main__':
    app.run_server()

This starts fine, but produces a website where only the callbacks in index.py work, not any of the callbacks defined in other files.

4. python index.py v2
When I followed your advice backwards and ran python instead of gunicorn, I found that this version of the code was broken the same way (no working callbacks from other files), even with the debug server.

So where I’m stuck is, what pattern of code both loads all of the callbacks correctly and can be run by both python and gunicorn? The code on Multi-page Apps points toward putting all callbacks in index.py, as with this forum post, but this loses most of the advantages of splitting code in different pages.

Write server = app.server in the same file that you have app.run_server() (looks like index.py) and import app from app in that file. Keep app = dash.Dash() in app.py so that it can be imported into other files without circular imports. Then, do gunicorn index:server

Thank you, that was the problem. I kept running in circles trying to debug, possibly because 1) app has two different meanings, as a module name and as the name of an instantiated Dash object within that module, and 2) I was implementing tabs, code in multiple files, and a production setup, and the example code for each of these is separate, so I found it tricky to marry the different examples together.

Full, working, end-to-end code is now up on GitHub, including the server install for Gunicorn/Nginx/HTTPS, and I hope write up in more detail this example of what I hope is best practices.

Great! Your app looks very cool, I’d love to hear more about it if you feel like sharing some screenshots and the story behind it in #show-and-tell :slight_smile: